From 4a8e71e2c6cb8d46aef61a9fbbb45312d7fef35f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:31:50 +0200 Subject: [PATCH 01/23] web: bump the wdio group in /tests/wdio with 3 updates (#7180) Bumps the wdio group in /tests/wdio with 3 updates: [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli), [@wdio/local-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-local-runner) and [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework). Updates `@wdio/cli` from 8.18.0 to 8.18.2 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v8.18.2/packages/wdio-cli) Updates `@wdio/local-runner` from 8.18.0 to 8.18.2 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v8.18.2/packages/wdio-local-runner) Updates `@wdio/mocha-framework` from 8.18.0 to 8.18.2 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v8.18.2/packages/wdio-mocha-framework) --- updated-dependencies: - dependency-name: "@wdio/cli" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: wdio - dependency-name: "@wdio/local-runner" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: wdio - dependency-name: "@wdio/mocha-framework" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: wdio ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/wdio/package-lock.json | 96 ++++++++++++++++++------------------ tests/wdio/package.json | 6 +-- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/tests/wdio/package-lock.json b/tests/wdio/package-lock.json index 2eeafd9eb..2f0ff41a9 100644 --- a/tests/wdio/package-lock.json +++ b/tests/wdio/package-lock.json @@ -9,9 +9,9 @@ "@trivago/prettier-plugin-sort-imports": "^4.2.0", "@typescript-eslint/eslint-plugin": "^6.7.5", "@typescript-eslint/parser": "^6.7.5", - "@wdio/cli": "^8.18.0", - "@wdio/local-runner": "^8.18.0", - "@wdio/mocha-framework": "^8.18.0", + "@wdio/cli": "^8.18.2", + "@wdio/local-runner": "^8.18.2", + "@wdio/mocha-framework": "^8.18.2", "@wdio/spec-reporter": "^8.18.1", "eslint": "^8.51.0", "eslint-config-google": "^0.14.0", @@ -1067,18 +1067,18 @@ } }, "node_modules/@wdio/cli": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.18.0.tgz", - "integrity": "sha512-zLt6pEbSwW/S7sBH5uZrYn9HhexB57ufqMV6IAKgX0SsJQwqOu1hdCIOiH1ZAfAHr2bPjpqIDIW+WOvV7mug9g==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.18.2.tgz", + "integrity": "sha512-vjMedd7PEHZywxbRE/rHzAPbj+hsCJz5b7vPTXu9QuwH2wWU2ab79ZqQpaUMKwZx8yXJfG6neb89tEbF9ximqQ==", "dev": true, "dependencies": { "@types/node": "^20.1.1", - "@wdio/config": "8.18.0", - "@wdio/globals": "8.18.0", + "@wdio/config": "8.18.2", + "@wdio/globals": "8.18.2", "@wdio/logger": "8.16.17", "@wdio/protocols": "8.18.0", "@wdio/types": "8.17.0", - "@wdio/utils": "8.18.0", + "@wdio/utils": "8.18.2", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^3.5.3", @@ -1093,7 +1093,7 @@ "lodash.union": "^4.6.0", "read-pkg-up": "10.1.0", "recursive-readdir": "^2.2.3", - "webdriverio": "8.18.0", + "webdriverio": "8.18.2", "yargs": "^17.7.2", "yarn-install": "^1.0.0" }, @@ -1117,14 +1117,14 @@ } }, "node_modules/@wdio/config": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.18.0.tgz", - "integrity": "sha512-sS5OXyxRtPCXDKloCqtEFuhei9WCxFzM7B5CyTKanbZ+xF4+t21aNF49OXXzWZXhUylK88whGB7amwO8tfJFww==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.18.2.tgz", + "integrity": "sha512-O3K36Wk/G/P5t9NfI/jBjLMdJq1KEDQTmbLvrbRckqzX5SQmPFg2pg18gE9N3JQE4A7qR+imxVo45HmhFDyn4w==", "dev": true, "dependencies": { "@wdio/logger": "8.16.17", "@wdio/types": "8.17.0", - "@wdio/utils": "8.18.0", + "@wdio/utils": "8.18.2", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -1136,28 +1136,28 @@ } }, "node_modules/@wdio/globals": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.18.0.tgz", - "integrity": "sha512-r6BvpMaqD3+pf7U7Lq1EnbahGhf/3BRO6aqQP7z7IlwakoeU9ih/yTA31BGt36wj0Vx8dhFfR0JpFhMXpvDqiA==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.18.2.tgz", + "integrity": "sha512-hHZqqWlvEaVHru+e5bMXsTBbPqKi85JO5q2XKX+ixS4XWoZXoMjN5WzL/3N9GkF2mJqIkyb9DHUT0T2vvf3oNA==", "dev": true, "engines": { "node": "^16.13 || >=18" }, "optionalDependencies": { "expect-webdriverio": "^4.2.5", - "webdriverio": "8.18.0" + "webdriverio": "8.18.2" } }, "node_modules/@wdio/local-runner": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.18.0.tgz", - "integrity": "sha512-fArLIgYbMPP7gqajy6lZSMgECkyKFNRJG75UA0NjMoTBmZLzJavgadnB/uF42dXyNBdZ218abikF90qMLF1RJg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.18.2.tgz", + "integrity": "sha512-W5QRXmH+MngHEVktsX6WXyoP/WI3mSlN66E1xGYLtMVwPhp3wMXDIrk1K/0UCAViX7lQ3tvo0B2QoZhsAXVT+A==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@wdio/logger": "8.16.17", "@wdio/repl": "8.10.1", - "@wdio/runner": "8.18.0", + "@wdio/runner": "8.18.2", "@wdio/types": "8.17.0", "async-exit-hook": "^2.0.1", "split2": "^4.1.0", @@ -1195,16 +1195,16 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.18.0.tgz", - "integrity": "sha512-8c+z3il5s9nWqZ4NqQxOherex2VbMC4xNAllJO4pixeJkKhRI30mB0f1/gMM4YjO7sW801AHSSMD1lWNh/kDOg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.18.2.tgz", + "integrity": "sha512-vsuPyuPbkw8FOsOeru9BJXwbSyk9//MiFnqNWwCdbFqVTc0M+RIYklnVgDUyx7Fnl87XewVWDioOWr71FH4ZhQ==", "dev": true, "dependencies": { "@types/mocha": "^10.0.0", "@types/node": "^20.1.0", "@wdio/logger": "8.16.17", "@wdio/types": "8.17.0", - "@wdio/utils": "8.18.0", + "@wdio/utils": "8.18.2", "mocha": "^10.0.0" }, "engines": { @@ -1246,22 +1246,22 @@ } }, "node_modules/@wdio/runner": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.18.0.tgz", - "integrity": "sha512-5I9DWh1cW9/Om+E7vNWFNx7BqavAzOFvvj1cihTzT766Y3I2wLHAUAE0OJoOZsk53beBJNYnCIOwrOWjk7RdZQ==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.18.2.tgz", + "integrity": "sha512-UPfvKA9yunEadHHDZwveZmKL0ayHDCkUegzUzgHFYmhnijUAa1Xeo837NpBe9y753TWt5PgRA4BIXSDlxJ9ySA==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.18.0", - "@wdio/globals": "8.18.0", + "@wdio/config": "8.18.2", + "@wdio/globals": "8.18.2", "@wdio/logger": "8.16.17", "@wdio/types": "8.17.0", - "@wdio/utils": "8.18.0", + "@wdio/utils": "8.18.2", "deepmerge-ts": "^5.0.0", "expect-webdriverio": "^4.2.5", "gaze": "^1.1.2", - "webdriver": "8.18.0", - "webdriverio": "8.18.0" + "webdriver": "8.18.2", + "webdriverio": "8.18.2" }, "engines": { "node": "^16.13 || >=18" @@ -1308,9 +1308,9 @@ } }, "node_modules/@wdio/utils": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.18.0.tgz", - "integrity": "sha512-ziXToU5BZSW96KNPhTGYl3eVmHQV5YeI+lsBozXJ5tGofaBCYMtbxdAI573IwR6lo8+evEdNTIGJgZXp8lDOxQ==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.18.2.tgz", + "integrity": "sha512-TQrrKv+knFn4Z/T/e/+wdnBoykNBg6rfo0NsAwaWh4PbJ1tf+Dc9GjzWhvJTgHwZf4v78K8Z+77qkqoLCF1wSg==", "dev": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", @@ -8693,18 +8693,18 @@ } }, "node_modules/webdriver": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.18.0.tgz", - "integrity": "sha512-OImB/K2BMGVP77yGpB4qrAwzAVrlusL5egaqoA9sl4inh1Ff+6n+LwQmPfe/dezejm5Fxuaf/HWvWEq91WbghQ==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.18.2.tgz", + "integrity": "sha512-7xr8K2jlrRdhqK6LLHrg96OiccWT5EeBIQXk9xAifgIbs6l/JfzCjC9WqC0AmX9plXjR8wf2LS+Ob9Ajhx6v+A==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.18.0", + "@wdio/config": "8.18.2", "@wdio/logger": "8.16.17", "@wdio/protocols": "8.18.0", "@wdio/types": "8.17.0", - "@wdio/utils": "8.18.0", + "@wdio/utils": "8.18.2", "deepmerge-ts": "^5.1.0", "got": "^ 12.6.1", "ky": "^0.33.0", @@ -8752,18 +8752,18 @@ } }, "node_modules/webdriverio": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.18.0.tgz", - "integrity": "sha512-LVgmZHn36NOL4O1RszBa7TPYf5VAyakmgkkDtWe1tVVQ2AkbIKnhKGLar6BQd/wfLIn61pKfvvmmYwDjnXgkhg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.18.2.tgz", + "integrity": "sha512-vX+U4QH9HdyT3upcOzP6YMpnAA1oZJJAZetvf9aWZ9KnBzgkL60LiZ/q9xCX+VWYKEIvNZ66ekppbuZ8FpobIQ==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.18.0", + "@wdio/config": "8.18.2", "@wdio/logger": "8.16.17", "@wdio/protocols": "8.18.0", "@wdio/repl": "8.10.1", "@wdio/types": "8.17.0", - "@wdio/utils": "8.18.0", + "@wdio/utils": "8.18.2", "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", @@ -8780,7 +8780,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.18.0" + "webdriver": "8.18.2" }, "engines": { "node": "^16.13 || >=18" diff --git a/tests/wdio/package.json b/tests/wdio/package.json index 02313d828..bf0f4758c 100644 --- a/tests/wdio/package.json +++ b/tests/wdio/package.json @@ -6,9 +6,9 @@ "@trivago/prettier-plugin-sort-imports": "^4.2.0", "@typescript-eslint/eslint-plugin": "^6.7.5", "@typescript-eslint/parser": "^6.7.5", - "@wdio/cli": "^8.18.0", - "@wdio/local-runner": "^8.18.0", - "@wdio/mocha-framework": "^8.18.0", + "@wdio/cli": "^8.18.2", + "@wdio/local-runner": "^8.18.2", + "@wdio/mocha-framework": "^8.18.2", "@wdio/spec-reporter": "^8.18.1", "eslint": "^8.51.0", "eslint-config-google": "^0.14.0", From 84e0f7bc2d6039ae9e74df477572d465c620c059 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:32:18 +0200 Subject: [PATCH 02/23] web: bump rollup from 4.0.2 to 4.1.3 in /web (#7179) Bumps [rollup](https://github.com/rollup/rollup) from 4.0.2 to 4.1.3. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.0.2...v4.1.3) --- updated-dependencies: - dependency-name: rollup dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 104 +++++++++++++++++++++--------------------- web/package.json | 2 +- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index ec600c34c..ceb9dbaf3 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -87,7 +87,7 @@ "pyright": "^1.1.331", "react": "^18.2.0", "react-dom": "^18.2.0", - "rollup": "^4.0.2", + "rollup": "^4.1.3", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-postcss-lit": "^2.1.0", @@ -4557,9 +4557,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.0.2.tgz", - "integrity": "sha512-xDvk1pT4vaPU2BOLy0MqHMdYZyntqpaBf8RhBiezlqG9OjY8F50TyctHo8znigYKd+QCFhCmlmXHOL/LoaOl3w==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.1.3.tgz", + "integrity": "sha512-pugxxuAygWTDj3WcyXGspor433gR35riZz66L0EdToX+shXGfh9qKgEFdTPoW9KwIrrJnxDGv9ncfl+a9bFg1w==", "cpu": [ "arm" ], @@ -4570,9 +4570,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.0.2.tgz", - "integrity": "sha512-lqCglytY3E6raze27DD9VQJWohbwCxzqs9aSHcj5X/8hJpzZfNdbsr4Ja9Hqp6iPyF53+5PtPx0pKRlkSvlHZg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.1.3.tgz", + "integrity": "sha512-gO5j0qqT6ETdSf10gxTBeBmsKPC6yK80StTxHr4pvTYpPDfI7/mzSHy/3ez3OQyjxcBXs3i8tSF8aknwCkzv6Q==", "cpu": [ "arm64" ], @@ -4583,9 +4583,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.0.2.tgz", - "integrity": "sha512-nkBKItS6E6CCzvRwgiKad+j+1ibmL7SIInj7oqMWmdkCjiSX6VeVZw2mLlRKIUL+JjsBgpATTfo7BiAXc1v0jA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.1.3.tgz", + "integrity": "sha512-ibOYcmg3VTXxQOb9HnrmGCshO/5hWucl/Yw8KSt6DE12oif4ssX9pxBBEodu65fprS6qKy80rSs1VvpzVFRRXA==", "cpu": [ "arm64" ], @@ -4596,9 +4596,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.0.2.tgz", - "integrity": "sha512-vX2C8xvWPIbpEgQht95+dY6BReKAvtDgPDGi0XN0kWJKkm4WdNmq5dnwscv/zxvi+n6jUTBhs6GtpkkWT4q8Gg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.1.3.tgz", + "integrity": "sha512-wgWtqQZjtOVuWnDr3BoNqO9mRpgb/XoqSyzqHxA4xSUgj+3cksJzTe7XCTzBoRc2IPB0mWr9/W+QDUu7OWleEQ==", "cpu": [ "x64" ], @@ -4609,9 +4609,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.0.2.tgz", - "integrity": "sha512-DVFIfcHOjgmeHOAqji4xNz2wczt1Bmzy9MwBZKBa83SjBVO/i38VHDR+9ixo8QpBOiEagmNw12DucG+v55tCrg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.1.3.tgz", + "integrity": "sha512-73mb6Fk3WnygqCbYHPk7EWAUvmfKZr2teKD+Qb3i6nLQ3wEihEJixg8psUi0pmge77qmuONjOIiJnT8+hTWqEQ==", "cpu": [ "arm" ], @@ -4622,9 +4622,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.0.2.tgz", - "integrity": "sha512-GCK/a9ItUxPI0V5hQEJjH4JtOJO90GF2Hja7TO+EZ8rmkGvEi8/ZDMhXmcuDpQT7/PWrTT9RvnG8snMd5SrhBQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.1.3.tgz", + "integrity": "sha512-XI7zAp5ADEph8iHXjk4silYARe/QO7zDGM71R7A6jnny4vlXCYBPB6V6rZm2kZ4VfckeZQukdGOlnvvW5fWLzA==", "cpu": [ "arm64" ], @@ -4635,9 +4635,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.0.2.tgz", - "integrity": "sha512-cLuBp7rOjIB1R2j/VazjCmHC7liWUur2e9mFflLJBAWCkrZ+X0+QwHLvOQakIwDymungzAKv6W9kHZnTp/Mqrg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.1.3.tgz", + "integrity": "sha512-6yFpmrH6xx00b9qEoabkyeHzamKSv8c43+SgaItTcHzgaaX3HGRSK+ZZt9GokB3Cc7z4HXllK7Ts5fF5W8FddQ==", "cpu": [ "arm64" ], @@ -4648,9 +4648,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.0.2.tgz", - "integrity": "sha512-Zqw4iVnJr2naoyQus0yLy7sLtisCQcpdMKUCeXPBjkJtpiflRime/TMojbnl8O3oxUAj92mxr+t7im/RbgA20w==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.1.3.tgz", + "integrity": "sha512-Zbb2CWJfbu2aZqd7hl9otRffh0MgYVhVsOz/sV6LSJcJ2LldBuvn2K9wVTo1gKh2xtTnJo93vusSppk1WtB4NA==", "cpu": [ "x64" ], @@ -4661,9 +4661,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.0.2.tgz", - "integrity": "sha512-jJRU9TyUD/iMqjf8aLAp7XiN3pIj5v6Qcu+cdzBfVTKDD0Fvua4oUoK8eVJ9ZuKBEQKt3WdlcwJXFkpmMLk6kg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.1.3.tgz", + "integrity": "sha512-GNATGqh1xMSIwFt3A7co3zyw8hMlUs8E+cBe9Hank0L32hpDYavoPqu7Uat74es+eDqrGn0szfm08ZjYf/ApCQ==", "cpu": [ "x64" ], @@ -4674,9 +4674,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.0.2.tgz", - "integrity": "sha512-ZkS2NixCxHKC4zbOnw64ztEGGDVIYP6nKkGBfOAxEPW71Sji9v8z3yaHNuae/JHPwXA+14oDefnOuVfxl59SmQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.1.3.tgz", + "integrity": "sha512-rTxOCH2ZLWkbZJh621qNmS4zDjvIOnvwXdd0Zvkm8twvVoyWATbn6q/bI3bXjQeV7mEASXU1atUWzdeovTcrrw==", "cpu": [ "arm64" ], @@ -4687,9 +4687,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.0.2.tgz", - "integrity": "sha512-3SKjj+tvnZ0oZq2BKB+fI+DqYI83VrRzk7eed8tJkxeZ4zxJZcLSE8YDQLYGq1tZAnAX+H076RHHB4gTZXsQzw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.1.3.tgz", + "integrity": "sha512-JcnmTz5d2kcxKTjcPPt0p86IzhViYOj8f8qFWwyzgDpCHFLXAKv9C4uxnwTornHvWq31km7zZ8IBZui8LjX52w==", "cpu": [ "ia32" ], @@ -4700,9 +4700,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.0.2.tgz", - "integrity": "sha512-MBdJIOxRauKkry7t2q+rTHa3aWjVez2eioWg+etRVS3dE4tChhmt5oqZYr48R6bPmcwEhxQr96gVRfeQrLbqng==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.1.3.tgz", + "integrity": "sha512-B9oi0JSMvSOsjB0Rve1WLFFEv+8uRvok+N3XWtxzSi/jjiVeM6wbfFgnHzZ2LA7FG9tJs0V7aHLD+73Q8VUIvg==", "cpu": [ "x64" ], @@ -20239,9 +20239,9 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.0.2.tgz", - "integrity": "sha512-MCScu4usMPCeVFaiLcgMDaBQeYi1z6vpWxz0r0hq0Hv77Y2YuOTZldkuNJ54BdYBH3e+nkrk6j0Rre/NLDBYzg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.1.3.tgz", + "integrity": "sha512-ytWlDll2r0Q7iQGX+aZ8ptfZIE55vLApNuMnMYlaMO40wk3o/nN6sjuEvwQjhnAcqQ+NSX4vPgXE/eIO609HMA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -20251,18 +20251,18 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.0.2", - "@rollup/rollup-android-arm64": "4.0.2", - "@rollup/rollup-darwin-arm64": "4.0.2", - "@rollup/rollup-darwin-x64": "4.0.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.0.2", - "@rollup/rollup-linux-arm64-gnu": "4.0.2", - "@rollup/rollup-linux-arm64-musl": "4.0.2", - "@rollup/rollup-linux-x64-gnu": "4.0.2", - "@rollup/rollup-linux-x64-musl": "4.0.2", - "@rollup/rollup-win32-arm64-msvc": "4.0.2", - "@rollup/rollup-win32-ia32-msvc": "4.0.2", - "@rollup/rollup-win32-x64-msvc": "4.0.2", + "@rollup/rollup-android-arm-eabi": "4.1.3", + "@rollup/rollup-android-arm64": "4.1.3", + "@rollup/rollup-darwin-arm64": "4.1.3", + "@rollup/rollup-darwin-x64": "4.1.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.1.3", + "@rollup/rollup-linux-arm64-gnu": "4.1.3", + "@rollup/rollup-linux-arm64-musl": "4.1.3", + "@rollup/rollup-linux-x64-gnu": "4.1.3", + "@rollup/rollup-linux-x64-musl": "4.1.3", + "@rollup/rollup-win32-arm64-msvc": "4.1.3", + "@rollup/rollup-win32-ia32-msvc": "4.1.3", + "@rollup/rollup-win32-x64-msvc": "4.1.3", "fsevents": "~2.3.2" } }, diff --git a/web/package.json b/web/package.json index 9555d20d9..7bdb047b5 100644 --- a/web/package.json +++ b/web/package.json @@ -105,7 +105,7 @@ "pyright": "^1.1.331", "react": "^18.2.0", "react-dom": "^18.2.0", - "rollup": "^4.0.2", + "rollup": "^4.1.3", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-postcss-lit": "^2.1.0", From cf977950fd1cd8b1517d623279e075f93bc3a33c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:32:57 +0200 Subject: [PATCH 03/23] web: bump yaml from 2.3.2 to 2.3.3 in /web (#7176) Bumps [yaml](https://github.com/eemeli/yaml) from 2.3.2 to 2.3.3. - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.3.2...v2.3.3) --- updated-dependencies: - dependency-name: yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index ceb9dbaf3..1e364b5c6 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -40,7 +40,7 @@ "rapidoc": "^9.3.4", "style-mod": "^4.1.0", "webcomponent-qr-code": "^1.2.0", - "yaml": "^2.3.2" + "yaml": "^2.3.3" }, "devDependencies": { "@babel/core": "^7.23.2", @@ -23404,9 +23404,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", - "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "engines": { "node": ">= 14" } diff --git a/web/package.json b/web/package.json index 7bdb047b5..54f3f9143 100644 --- a/web/package.json +++ b/web/package.json @@ -58,7 +58,7 @@ "rapidoc": "^9.3.4", "style-mod": "^4.1.0", "webcomponent-qr-code": "^1.2.0", - "yaml": "^2.3.2" + "yaml": "^2.3.3" }, "devDependencies": { "@babel/core": "^7.23.2", From ef7e4a8b57c962bdb36c2db8e4e89ba1a95a630b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:33:45 +0200 Subject: [PATCH 04/23] web: bump @rollup/plugin-commonjs from 25.0.5 to 25.0.7 in /web (#7178) Bumps [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs) from 25.0.5 to 25.0.7. - [Changelog](https://github.com/rollup/plugins/blob/master/packages/commonjs/CHANGELOG.md) - [Commits](https://github.com/rollup/plugins/commits/commonjs-v25.0.7/packages/commonjs) --- updated-dependencies: - dependency-name: "@rollup/plugin-commonjs" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 22 +++++++++++++++++----- web/package.json | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 1e364b5c6..dcd3bb747 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -56,7 +56,7 @@ "@jeysal/storybook-addon-css-user-preferences": "^0.2.0", "@lit/localize-tools": "^0.7.0", "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^25.0.5", + "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.3", "@rollup/plugin-terser": "^0.4.4", @@ -4416,9 +4416,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "25.0.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.5.tgz", - "integrity": "sha512-xY8r/A9oisSeSuLCTfhssyDjo9Vp/eDiRLXkg1MXCcEEgEjPmLU+ZyDB20OOD0NlyDa/8SGbK5uIggF5XTx77w==", + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -4426,7 +4426,7 @@ "estree-walker": "^2.0.2", "glob": "^8.0.3", "is-reference": "1.2.1", - "magic-string": "^0.27.0" + "magic-string": "^0.30.3" }, "engines": { "node": ">=14.0.0" @@ -4440,6 +4440,18 @@ } } }, + "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.2.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", diff --git a/web/package.json b/web/package.json index 54f3f9143..8df5e94d3 100644 --- a/web/package.json +++ b/web/package.json @@ -74,7 +74,7 @@ "@jeysal/storybook-addon-css-user-preferences": "^0.2.0", "@lit/localize-tools": "^0.7.0", "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^25.0.5", + "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.3", "@rollup/plugin-terser": "^0.4.4", From c0f7a75d5cdb3efd57a3dfde30baac94b5bfb404 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:34:09 +0200 Subject: [PATCH 05/23] web: bump the sentry group in /web with 2 updates (#7175) Bumps the sentry group in /web with 2 updates: [@sentry/browser](https://github.com/getsentry/sentry-javascript) and [@sentry/tracing](https://github.com/getsentry/sentry-javascript). Updates `@sentry/browser` from 7.73.0 to 7.74.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.73.0...7.74.0) Updates `@sentry/tracing` from 7.73.0 to 7.74.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.73.0...7.74.0) --- updated-dependencies: - dependency-name: "@sentry/browser" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: sentry - dependency-name: "@sentry/tracing" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: sentry ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 76 +++++++++++++++++++++---------------------- web/package.json | 4 +-- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index dcd3bb747..0e4ceded0 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -24,8 +24,8 @@ "@open-wc/lit-helpers": "^0.6.0", "@patternfly/elements": "^2.4.0", "@patternfly/patternfly": "^4.224.2", - "@sentry/browser": "^7.73.0", - "@sentry/tracing": "^7.73.0", + "@sentry/browser": "^7.74.0", + "@sentry/tracing": "^7.74.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", "chart.js": "^4.4.0", @@ -4725,13 +4725,13 @@ ] }, "node_modules/@sentry-internal/tracing": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.73.0.tgz", - "integrity": "sha512-ig3WL/Nqp8nRQ52P205NaypGKNfIl/G+cIqge9xPW6zfRb5kJdM1YParw9GSJ1SPjEZBkBORGAML0on5H2FILw==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.74.0.tgz", + "integrity": "sha512-JK6IRGgdtZjswGfaGIHNWIThffhOHzVIIaGmglui+VFIzOsOqePjoxaDV0MEvzafxXZD7eWqGE5RGuZ0n6HFVg==", "dependencies": { - "@sentry/core": "7.73.0", - "@sentry/types": "7.73.0", - "@sentry/utils": "7.73.0", + "@sentry/core": "7.74.0", + "@sentry/types": "7.74.0", + "@sentry/utils": "7.74.0", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { @@ -4739,15 +4739,15 @@ } }, "node_modules/@sentry/browser": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.73.0.tgz", - "integrity": "sha512-e301hUixcJ5+HNKCJwajFF5smF4opXEFSclyWsJuFNufv5J/1C1SDhbwG2JjBt5zzdSoKWJKT1ewR6vpICyoDw==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.74.0.tgz", + "integrity": "sha512-Njr8216Z1dFUcl6NqBOk20dssK9SjoVddY74Xq+Q4p3NfXBG3lkMcACXor7SFoJRZXq8CZWGS13Cc5KwViRw4g==", "dependencies": { - "@sentry-internal/tracing": "7.73.0", - "@sentry/core": "7.73.0", - "@sentry/replay": "7.73.0", - "@sentry/types": "7.73.0", - "@sentry/utils": "7.73.0", + "@sentry-internal/tracing": "7.74.0", + "@sentry/core": "7.74.0", + "@sentry/replay": "7.74.0", + "@sentry/types": "7.74.0", + "@sentry/utils": "7.74.0", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { @@ -4755,12 +4755,12 @@ } }, "node_modules/@sentry/core": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.73.0.tgz", - "integrity": "sha512-9FEz4Gq848LOgVN2OxJGYuQqxv7cIVw69VlAzWHEm3njt8mjvlTq+7UiFsGRo84+59V2FQuHxzA7vVjl90WfSg==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.74.0.tgz", + "integrity": "sha512-83NRuqn7nDZkSVBN5yJQqcpXDG4yMYiB7TkYUKrGTzBpRy6KUOrkCdybuKk0oraTIGiGSe5WEwCFySiNgR9FzA==", "dependencies": { - "@sentry/types": "7.73.0", - "@sentry/utils": "7.73.0", + "@sentry/types": "7.74.0", + "@sentry/utils": "7.74.0", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { @@ -4768,43 +4768,43 @@ } }, "node_modules/@sentry/replay": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.73.0.tgz", - "integrity": "sha512-a8IC9SowBisLYD2IdLkXzx7gN4iVwHDJhQvLp2B8ARs1PyPjJ7gCxSMHeGrYp94V0gOXtorNYkrxvuX8ayPROA==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.74.0.tgz", + "integrity": "sha512-GoYa3cHTTFVI/J1cnZ0i4X128mf/JljaswO3PWNTe2k3lSHq/LM5aV0keClRvwM0W8hlix8oOTT06nnenOUmmw==", "dependencies": { - "@sentry/core": "7.73.0", - "@sentry/types": "7.73.0", - "@sentry/utils": "7.73.0" + "@sentry/core": "7.74.0", + "@sentry/types": "7.74.0", + "@sentry/utils": "7.74.0" }, "engines": { "node": ">=12" } }, "node_modules/@sentry/tracing": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.73.0.tgz", - "integrity": "sha512-LOQR6Hkc8ZoflCXWtMlxTbCBEwv0MSOr3vesnRsmlFG8TW1YUIneU+wKnVxToWAZ8fq+6ubclnuIUKHfqTk/Tg==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.74.0.tgz", + "integrity": "sha512-rSFJADhh3J3zmkzJ1EXCOwS3h7F6o/lSKu7CWZSZ6k5kBvbCJ5AXvGQadhPdWPJMMcPFzCJaOyTKEPcwL4tbCw==", "dependencies": { - "@sentry-internal/tracing": "7.73.0" + "@sentry-internal/tracing": "7.74.0" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/types": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.73.0.tgz", - "integrity": "sha512-/v8++bly8jW7r4cP2wswYiiVpn7eLLcqwnfPUMeCQze4zj3F3nTRIKc9BGHzU0V+fhHa3RwRC2ksqTGq1oJMDg==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.74.0.tgz", + "integrity": "sha512-rI5eIRbUycWjn6s6o3yAjjWtIvYSxZDdnKv5je2EZINfLKcMPj1dkl6wQd2F4y7gLfD/N6Y0wZYIXC3DUdJQQg==", "engines": { "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "7.73.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.73.0.tgz", - "integrity": "sha512-h3ZK/qpf4k76FhJV9uiSbvMz3V/0Ovy94C+5/9UgPMVCJXFmVsdw8n/dwANJ7LupVPfYP23xFGgebDMFlK1/2w==", + "version": "7.74.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.74.0.tgz", + "integrity": "sha512-k3np8nuTPtx5KDODPtULfFln4UXdE56MZCcF19Jv6Ljxf+YN/Ady1+0Oi3e0XoSvFpWNyWnglauT7M65qCE6kg==", "dependencies": { - "@sentry/types": "7.73.0", + "@sentry/types": "7.74.0", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { diff --git a/web/package.json b/web/package.json index 8df5e94d3..f2ec37d7a 100644 --- a/web/package.json +++ b/web/package.json @@ -42,8 +42,8 @@ "@open-wc/lit-helpers": "^0.6.0", "@patternfly/elements": "^2.4.0", "@patternfly/patternfly": "^4.224.2", - "@sentry/browser": "^7.73.0", - "@sentry/tracing": "^7.73.0", + "@sentry/browser": "^7.74.0", + "@sentry/tracing": "^7.74.0", "@webcomponents/webcomponentsjs": "^2.8.0", "base64-js": "^1.5.1", "chart.js": "^4.4.0", From 1ffb76501e7cb84a88b55cdabf277989132f3455 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:33:26 +0200 Subject: [PATCH 06/23] web: bump @rollup/plugin-replace from 5.0.3 to 5.0.4 in /web (#7177) Bumps [@rollup/plugin-replace](https://github.com/rollup/plugins/tree/HEAD/packages/replace) from 5.0.3 to 5.0.4. - [Changelog](https://github.com/rollup/plugins/blob/master/packages/replace/CHANGELOG.md) - [Commits](https://github.com/rollup/plugins/commits/babel-v5.0.4/packages/replace) --- updated-dependencies: - dependency-name: "@rollup/plugin-replace" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 54 ++++++++----------------------------------- web/package.json | 2 +- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 0e4ceded0..e72da4ff2 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -58,7 +58,7 @@ "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.3", + "@rollup/plugin-replace": "^5.0.4", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", "@storybook/addon-essentials": "^7.4.6", @@ -4440,18 +4440,6 @@ } } }, - "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.2.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", @@ -4478,13 +4466,13 @@ } }, "node_modules/@rollup/plugin-replace": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.3.tgz", - "integrity": "sha512-je7fu05B800IrMlWjb2wzJcdXzHYW46iTipfChnBDbIbDXhASZs27W1B58T2Yf45jZtJUONegpbce+9Ut2Ti/Q==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.4.tgz", + "integrity": "sha512-E2hmRnlh09K8HGT0rOnnri9OTh+BILGr7NVJGB30S4E3cLRn3J0xjdiyOZ74adPs4NiAMgrjUMGAZNJDBgsdmQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", - "magic-string": "^0.27.0" + "magic-string": "^0.30.3" }, "engines": { "node": ">=14.0.0" @@ -7701,18 +7689,6 @@ "node": ">=14.14" } }, - "node_modules/@storybook/builder-vite/node_modules/magic-string": { - "version": "0.30.4", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz", - "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@storybook/builder-vite/node_modules/rollup": { "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", @@ -9436,18 +9412,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/web-components-vite/node_modules/magic-string": { - "version": "0.30.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", - "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@storybook/web-components/node_modules/@storybook/channels": { "version": "7.4.6", "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.4.6.tgz", @@ -17209,12 +17173,12 @@ } }, "node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" diff --git a/web/package.json b/web/package.json index f2ec37d7a..445a8c6ad 100644 --- a/web/package.json +++ b/web/package.json @@ -76,7 +76,7 @@ "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.3", + "@rollup/plugin-replace": "^5.0.4", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", "@storybook/addon-essentials": "^7.4.6", From 5eb1c4e4bf4f1b7278113d822d7fa005fc18c2c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:33:45 +0200 Subject: [PATCH 07/23] web: bump @formatjs/intl-listformat from 7.4.2 to 7.5.0 in /web (#7182) Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 7.4.2 to 7.5.0. - [Release notes](https://github.com/formatjs/formatjs/releases) - [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@7.4.2...@formatjs/intl-listformat@7.5.0) --- updated-dependencies: - dependency-name: "@formatjs/intl-listformat" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index e72da4ff2..8e96d8522 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -15,7 +15,7 @@ "@codemirror/lang-xml": "^6.0.2", "@codemirror/legacy-modes": "^6.3.3", "@codemirror/theme-one-dark": "^6.1.2", - "@formatjs/intl-listformat": "^7.4.2", + "@formatjs/intl-listformat": "^7.5.0", "@fortawesome/fontawesome-free": "^6.4.2", "@goauthentik/api": "^2023.8.3-1696847703", "@lit-labs/context": "^0.4.1", @@ -2855,9 +2855,9 @@ } }, "node_modules/@formatjs/intl-listformat": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.4.2.tgz", - "integrity": "sha512-+6bSVudEQkf12Hh7kuKt8Xv/MyFlqdwA4V4NLnTZW8uYdF9RxlOELDD0rPaOc2++TMKIzI5o6XXwHPvpL6VrPA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.0.tgz", + "integrity": "sha512-n9FsXGl1T2ZbX6wSyrzCDJHrbJR0YJ9ZNsAqUvHXfbY3nsOmGnSTf5+bkuIp1Xiywu7m1X1Pfm/Ngp/yK1H84A==", "dependencies": { "@formatjs/ecma402-abstract": "1.17.2", "@formatjs/intl-localematcher": "0.4.2", diff --git a/web/package.json b/web/package.json index 445a8c6ad..67280a83d 100644 --- a/web/package.json +++ b/web/package.json @@ -33,7 +33,7 @@ "@codemirror/lang-xml": "^6.0.2", "@codemirror/legacy-modes": "^6.3.3", "@codemirror/theme-one-dark": "^6.1.2", - "@formatjs/intl-listformat": "^7.4.2", + "@formatjs/intl-listformat": "^7.5.0", "@fortawesome/fontawesome-free": "^6.4.2", "@goauthentik/api": "^2023.8.3-1696847703", "@lit-labs/context": "^0.4.1", From 3c3f1678e5d9174fc18ab0641e4c527bcb2a4f5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:34:19 +0200 Subject: [PATCH 08/23] web: bump rollup from 4.1.3 to 4.1.4 in /web (#7181) Bumps [rollup](https://github.com/rollup/rollup) from 4.1.3 to 4.1.4. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.1.3...v4.1.4) --- updated-dependencies: - dependency-name: rollup dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 104 +++++++++++++++++++++--------------------- web/package.json | 2 +- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 8e96d8522..e205d71be 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -87,7 +87,7 @@ "pyright": "^1.1.331", "react": "^18.2.0", "react-dom": "^18.2.0", - "rollup": "^4.1.3", + "rollup": "^4.1.4", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-postcss-lit": "^2.1.0", @@ -4557,9 +4557,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.1.3.tgz", - "integrity": "sha512-pugxxuAygWTDj3WcyXGspor433gR35riZz66L0EdToX+shXGfh9qKgEFdTPoW9KwIrrJnxDGv9ncfl+a9bFg1w==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.1.4.tgz", + "integrity": "sha512-WlzkuFvpKl6CLFdc3V6ESPt7gq5Vrimd2Yv9IzKXdOpgbH4cdDSS1JLiACX8toygihtH5OlxyQzhXOph7Ovlpw==", "cpu": [ "arm" ], @@ -4570,9 +4570,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.1.3.tgz", - "integrity": "sha512-gO5j0qqT6ETdSf10gxTBeBmsKPC6yK80StTxHr4pvTYpPDfI7/mzSHy/3ez3OQyjxcBXs3i8tSF8aknwCkzv6Q==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.1.4.tgz", + "integrity": "sha512-D1e+ABe56T9Pq2fD+R3ybe1ylCDzu3tY4Qm2Mj24R9wXNCq35+JbFbOpc2yrroO2/tGhTobmEl2Bm5xfE/n8RA==", "cpu": [ "arm64" ], @@ -4583,9 +4583,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.1.3.tgz", - "integrity": "sha512-ibOYcmg3VTXxQOb9HnrmGCshO/5hWucl/Yw8KSt6DE12oif4ssX9pxBBEodu65fprS6qKy80rSs1VvpzVFRRXA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.1.4.tgz", + "integrity": "sha512-7vTYrgEiOrjxnjsgdPB+4i7EMxbVp7XXtS+50GJYj695xYTTEMn3HZVEvgtwjOUkAP/Q4HDejm4fIAjLeAfhtg==", "cpu": [ "arm64" ], @@ -4596,9 +4596,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.1.3.tgz", - "integrity": "sha512-wgWtqQZjtOVuWnDr3BoNqO9mRpgb/XoqSyzqHxA4xSUgj+3cksJzTe7XCTzBoRc2IPB0mWr9/W+QDUu7OWleEQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.1.4.tgz", + "integrity": "sha512-eGJVZScKSLZkYjhTAESCtbyTBq9SXeW9+TX36ki5gVhDqJtnQ5k0f9F44jNK5RhAMgIj0Ht9+n6HAgH0gUUyWQ==", "cpu": [ "x64" ], @@ -4609,9 +4609,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.1.3.tgz", - "integrity": "sha512-73mb6Fk3WnygqCbYHPk7EWAUvmfKZr2teKD+Qb3i6nLQ3wEihEJixg8psUi0pmge77qmuONjOIiJnT8+hTWqEQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.1.4.tgz", + "integrity": "sha512-HnigYSEg2hOdX1meROecbk++z1nVJDpEofw9V2oWKqOWzTJlJf1UXVbDE6Hg30CapJxZu5ga4fdAQc/gODDkKg==", "cpu": [ "arm" ], @@ -4622,9 +4622,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.1.3.tgz", - "integrity": "sha512-XI7zAp5ADEph8iHXjk4silYARe/QO7zDGM71R7A6jnny4vlXCYBPB6V6rZm2kZ4VfckeZQukdGOlnvvW5fWLzA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.1.4.tgz", + "integrity": "sha512-TzJ+N2EoTLWkaClV2CUhBlj6ljXofaYzF/R9HXqQ3JCMnCHQZmQnbnZllw7yTDp0OG5whP4gIPozR4QiX+00MQ==", "cpu": [ "arm64" ], @@ -4635,9 +4635,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.1.3.tgz", - "integrity": "sha512-6yFpmrH6xx00b9qEoabkyeHzamKSv8c43+SgaItTcHzgaaX3HGRSK+ZZt9GokB3Cc7z4HXllK7Ts5fF5W8FddQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.1.4.tgz", + "integrity": "sha512-aVPmNMdp6Dlo2tWkAduAD/5TL/NT5uor290YvjvFvCv0Q3L7tVdlD8MOGDL+oRSw5XKXKAsDzHhUOPUNPRHVTQ==", "cpu": [ "arm64" ], @@ -4648,9 +4648,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.1.3.tgz", - "integrity": "sha512-Zbb2CWJfbu2aZqd7hl9otRffh0MgYVhVsOz/sV6LSJcJ2LldBuvn2K9wVTo1gKh2xtTnJo93vusSppk1WtB4NA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.1.4.tgz", + "integrity": "sha512-77Fb79ayiDad0grvVsz4/OB55wJRyw9Ao+GdOBA9XywtHpuq5iRbVyHToGxWquYWlEf6WHFQQnFEttsAzboyKg==", "cpu": [ "x64" ], @@ -4661,9 +4661,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.1.3.tgz", - "integrity": "sha512-GNATGqh1xMSIwFt3A7co3zyw8hMlUs8E+cBe9Hank0L32hpDYavoPqu7Uat74es+eDqrGn0szfm08ZjYf/ApCQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.1.4.tgz", + "integrity": "sha512-/t6C6niEQTqmQTVTD9TDwUzxG91Mlk69/v0qodIPUnjjB3wR4UA3klg+orR2SU3Ux2Cgf2pWPL9utK80/1ek8g==", "cpu": [ "x64" ], @@ -4674,9 +4674,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.1.3.tgz", - "integrity": "sha512-rTxOCH2ZLWkbZJh621qNmS4zDjvIOnvwXdd0Zvkm8twvVoyWATbn6q/bI3bXjQeV7mEASXU1atUWzdeovTcrrw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.1.4.tgz", + "integrity": "sha512-ZY5BHHrOPkMbCuGWFNpJH0t18D2LU6GMYKGaqaWTQ3CQOL57Fem4zE941/Ek5pIsVt70HyDXssVEFQXlITI5Gg==", "cpu": [ "arm64" ], @@ -4687,9 +4687,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.1.3.tgz", - "integrity": "sha512-JcnmTz5d2kcxKTjcPPt0p86IzhViYOj8f8qFWwyzgDpCHFLXAKv9C4uxnwTornHvWq31km7zZ8IBZui8LjX52w==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.1.4.tgz", + "integrity": "sha512-XG2mcRfFrJvYyYaQmvCIvgfkaGinfXrpkBuIbJrTl9SaIQ8HumheWTIwkNz2mktCKwZfXHQNpO7RgXLIGQ7HXA==", "cpu": [ "ia32" ], @@ -4700,9 +4700,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.1.3.tgz", - "integrity": "sha512-B9oi0JSMvSOsjB0Rve1WLFFEv+8uRvok+N3XWtxzSi/jjiVeM6wbfFgnHzZ2LA7FG9tJs0V7aHLD+73Q8VUIvg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.1.4.tgz", + "integrity": "sha512-ANFqWYPwkhIqPmXw8vm0GpBEHiPpqcm99jiiAp71DbCSqLDhrtr019C5vhD0Bw4My+LmMvciZq6IsWHqQpl2ZQ==", "cpu": [ "x64" ], @@ -20215,9 +20215,9 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.1.3.tgz", - "integrity": "sha512-ytWlDll2r0Q7iQGX+aZ8ptfZIE55vLApNuMnMYlaMO40wk3o/nN6sjuEvwQjhnAcqQ+NSX4vPgXE/eIO609HMA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.1.4.tgz", + "integrity": "sha512-U8Yk1lQRKqCkDBip/pMYT+IKaN7b7UesK3fLSTuHBoBJacCE+oBqo/dfG/gkUdQNNB2OBmRP98cn2C2bkYZkyw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -20227,18 +20227,18 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.1.3", - "@rollup/rollup-android-arm64": "4.1.3", - "@rollup/rollup-darwin-arm64": "4.1.3", - "@rollup/rollup-darwin-x64": "4.1.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.1.3", - "@rollup/rollup-linux-arm64-gnu": "4.1.3", - "@rollup/rollup-linux-arm64-musl": "4.1.3", - "@rollup/rollup-linux-x64-gnu": "4.1.3", - "@rollup/rollup-linux-x64-musl": "4.1.3", - "@rollup/rollup-win32-arm64-msvc": "4.1.3", - "@rollup/rollup-win32-ia32-msvc": "4.1.3", - "@rollup/rollup-win32-x64-msvc": "4.1.3", + "@rollup/rollup-android-arm-eabi": "4.1.4", + "@rollup/rollup-android-arm64": "4.1.4", + "@rollup/rollup-darwin-arm64": "4.1.4", + "@rollup/rollup-darwin-x64": "4.1.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.1.4", + "@rollup/rollup-linux-arm64-gnu": "4.1.4", + "@rollup/rollup-linux-arm64-musl": "4.1.4", + "@rollup/rollup-linux-x64-gnu": "4.1.4", + "@rollup/rollup-linux-x64-musl": "4.1.4", + "@rollup/rollup-win32-arm64-msvc": "4.1.4", + "@rollup/rollup-win32-ia32-msvc": "4.1.4", + "@rollup/rollup-win32-x64-msvc": "4.1.4", "fsevents": "~2.3.2" } }, diff --git a/web/package.json b/web/package.json index 67280a83d..76e1ed5bb 100644 --- a/web/package.json +++ b/web/package.json @@ -105,7 +105,7 @@ "pyright": "^1.1.331", "react": "^18.2.0", "react-dom": "^18.2.0", - "rollup": "^4.1.3", + "rollup": "^4.1.4", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-postcss-lit": "^2.1.0", From 07b8c5bc7a90387841ad753425bcf6f5c3109d0f Mon Sep 17 00:00:00 2001 From: Oleh Vivtash Date: Mon, 16 Oct 2023 14:10:21 +0300 Subject: [PATCH 09/23] website/docs: fix API OAuth token usage (#7159) Update api.md JWT Token howto update Signed-off-by: Oleh Vivtash --- website/developer-docs/api/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/developer-docs/api/api.md b/website/developer-docs/api/api.md index e6b7b6dc3..7e3877be7 100644 --- a/website/developer-docs/api/api.md +++ b/website/developer-docs/api/api.md @@ -22,4 +22,4 @@ Users can create tokens to authenticate as any user with a static key, which can ### JWT Token -OAuth2 clients can request the scope `goauthentik.io/api`, which allows their OAuth Refresh token to be used to authenticate to the API. +OAuth2 clients can request the scope `goauthentik.io/api`, which allows their OAuth Access token to be used to authenticate to the API. From 00b2a773b4e087dc9a8467e87997554bacceb349 Mon Sep 17 00:00:00 2001 From: Alissa Gerhard Date: Mon, 16 Oct 2023 13:11:34 +0200 Subject: [PATCH 10/23] sources/ldap: made ldap_sync_single calls from ldap_sync_all asynchronous (#6862) --- authentik/sources/ldap/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentik/sources/ldap/tasks.py b/authentik/sources/ldap/tasks.py index 026f398b6..9c4d6af73 100644 --- a/authentik/sources/ldap/tasks.py +++ b/authentik/sources/ldap/tasks.py @@ -32,7 +32,7 @@ CACHE_KEY_PREFIX = "goauthentik.io/sources/ldap/page/" def ldap_sync_all(): """Sync all sources""" for source in LDAPSource.objects.filter(enabled=True): - ldap_sync_single(source.pk) + ldap_sync_single.apply_async(args=[source.pk]) @CELERY_APP.task( From 25d4905d6c545727188ab0d7e678d81d91804001 Mon Sep 17 00:00:00 2001 From: Jens L Date: Mon, 16 Oct 2023 17:01:44 +0200 Subject: [PATCH 11/23] outposts: use channel groups instead of saving channel names (#7183) * outposts: use channel groups instead of saving channel names Signed-off-by: Jens Langhammer * use pubsub Signed-off-by: Jens Langhammer * support storing other args with state Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- .../outposts/{channels.py => consumer.py} | 46 ++++++++----------- authentik/outposts/models.py | 2 +- authentik/outposts/tasks.py | 9 ++-- authentik/outposts/tests/test_ws.py | 2 +- authentik/outposts/urls.py | 2 +- authentik/providers/proxy/tasks.py | 22 ++++----- authentik/root/settings.py | 4 +- 7 files changed, 39 insertions(+), 48 deletions(-) rename authentik/outposts/{channels.py => consumer.py} (79%) diff --git a/authentik/outposts/channels.py b/authentik/outposts/consumer.py similarity index 79% rename from authentik/outposts/channels.py rename to authentik/outposts/consumer.py index f0b656a47..e8c2ee127 100644 --- a/authentik/outposts/channels.py +++ b/authentik/outposts/consumer.py @@ -4,6 +4,7 @@ from datetime import datetime from enum import IntEnum from typing import Any, Optional +from asgiref.sync import async_to_sync from channels.exceptions import DenyConnection from dacite.core import from_dict from dacite.data import Data @@ -14,6 +15,8 @@ from authentik.core.channels import AuthJsonConsumer from authentik.outposts.apps import GAUGE_OUTPOSTS_CONNECTED, GAUGE_OUTPOSTS_LAST_UPDATE from authentik.outposts.models import OUTPOST_HELLO_INTERVAL, Outpost, OutpostState +OUTPOST_GROUP = "group_outpost_%(outpost_pk)s" + class WebsocketMessageInstruction(IntEnum): """Commands which can be triggered over Websocket""" @@ -47,8 +50,6 @@ class OutpostConsumer(AuthJsonConsumer): last_uid: Optional[str] = None - first_msg = False - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.logger = get_logger() @@ -71,22 +72,26 @@ class OutpostConsumer(AuthJsonConsumer): raise DenyConnection() self.outpost = outpost self.last_uid = self.channel_name + async_to_sync(self.channel_layer.group_add)( + OUTPOST_GROUP % {"outpost_pk": str(self.outpost.pk)}, self.channel_name + ) + GAUGE_OUTPOSTS_CONNECTED.labels( + outpost=self.outpost.name, + uid=self.last_uid, + expected=self.outpost.config.kubernetes_replicas, + ).inc() def disconnect(self, code): + if self.outpost: + async_to_sync(self.channel_layer.group_discard)( + OUTPOST_GROUP % {"outpost_pk": str(self.outpost.pk)}, self.channel_name + ) if self.outpost and self.last_uid: - state = OutpostState.for_instance_uid(self.outpost, self.last_uid) - if self.channel_name in state.channel_ids: - state.channel_ids.remove(self.channel_name) - state.save() GAUGE_OUTPOSTS_CONNECTED.labels( outpost=self.outpost.name, uid=self.last_uid, expected=self.outpost.config.kubernetes_replicas, ).dec() - self.logger.debug( - "removed outpost instance from cache", - instance_uuid=self.last_uid, - ) def receive_json(self, content: Data): msg = from_dict(WebsocketMessage, content) @@ -97,26 +102,13 @@ class OutpostConsumer(AuthJsonConsumer): raise DenyConnection() state = OutpostState.for_instance_uid(self.outpost, uid) - if self.channel_name not in state.channel_ids: - state.channel_ids.append(self.channel_name) state.last_seen = datetime.now() - state.hostname = msg.args.get("hostname", "") - - if not self.first_msg: - GAUGE_OUTPOSTS_CONNECTED.labels( - outpost=self.outpost.name, - uid=self.last_uid, - expected=self.outpost.config.kubernetes_replicas, - ).inc() - self.logger.debug( - "added outpost instance to cache", - instance_uuid=self.last_uid, - ) - self.first_msg = True + state.hostname = msg.args.pop("hostname", "") if msg.instruction == WebsocketMessageInstruction.HELLO: - state.version = msg.args.get("version", None) - state.build_hash = msg.args.get("buildHash", "") + state.version = msg.args.pop("version", None) + state.build_hash = msg.args.pop("buildHash", "") + state.args = msg.args elif msg.instruction == WebsocketMessageInstruction.ACK: return GAUGE_OUTPOSTS_LAST_UPDATE.labels( diff --git a/authentik/outposts/models.py b/authentik/outposts/models.py index 3caae7e73..878a3e9e6 100644 --- a/authentik/outposts/models.py +++ b/authentik/outposts/models.py @@ -411,12 +411,12 @@ class OutpostState: """Outpost instance state, last_seen and version""" uid: str - channel_ids: list[str] = field(default_factory=list) last_seen: Optional[datetime] = field(default=None) version: Optional[str] = field(default=None) version_should: Version = field(default=OUR_VERSION) build_hash: str = field(default="") hostname: str = field(default="") + args: dict = field(default_factory=dict) _outpost: Optional[Outpost] = field(default=None) diff --git a/authentik/outposts/tasks.py b/authentik/outposts/tasks.py index ddb0d5352..b6b3a9bab 100644 --- a/authentik/outposts/tasks.py +++ b/authentik/outposts/tasks.py @@ -25,6 +25,7 @@ from authentik.events.monitored_tasks import ( ) from authentik.lib.config import CONFIG from authentik.lib.utils.reflection import path_to_class +from authentik.outposts.consumer import OUTPOST_GROUP from authentik.outposts.controllers.base import BaseController, ControllerException from authentik.outposts.controllers.docker import DockerClient from authentik.outposts.controllers.kubernetes import KubernetesClient @@ -34,7 +35,6 @@ from authentik.outposts.models import ( Outpost, OutpostModel, OutpostServiceConnection, - OutpostState, OutpostType, ServiceConnectionInvalid, ) @@ -243,10 +243,9 @@ def _outpost_single_update(outpost: Outpost, layer=None): outpost.build_user_permissions(outpost.user) if not layer: # pragma: no cover layer = get_channel_layer() - for state in OutpostState.for_outpost(outpost): - for channel in state.channel_ids: - LOGGER.debug("sending update", channel=channel, instance=state.uid, outpost=outpost) - async_to_sync(layer.send)(channel, {"type": "event.update"}) + group = OUTPOST_GROUP % {"outpost_pk": str(outpost.pk)} + LOGGER.debug("sending update", channel=group, outpost=outpost) + async_to_sync(layer.group_send)(group, {"type": "event.update"}) @CELERY_APP.task( diff --git a/authentik/outposts/tests/test_ws.py b/authentik/outposts/tests/test_ws.py index 9d8546044..b8fcba925 100644 --- a/authentik/outposts/tests/test_ws.py +++ b/authentik/outposts/tests/test_ws.py @@ -7,7 +7,7 @@ from django.test import TransactionTestCase from authentik import __version__ from authentik.core.tests.utils import create_test_flow -from authentik.outposts.channels import WebsocketMessage, WebsocketMessageInstruction +from authentik.outposts.consumer import WebsocketMessage, WebsocketMessageInstruction from authentik.outposts.models import Outpost, OutpostType from authentik.providers.proxy.models import ProxyProvider from authentik.root import websocket diff --git a/authentik/outposts/urls.py b/authentik/outposts/urls.py index 353dfd13c..cd7ba3bf8 100644 --- a/authentik/outposts/urls.py +++ b/authentik/outposts/urls.py @@ -7,7 +7,7 @@ from authentik.outposts.api.service_connections import ( KubernetesServiceConnectionViewSet, ServiceConnectionViewSet, ) -from authentik.outposts.channels import OutpostConsumer +from authentik.outposts.consumer import OutpostConsumer from authentik.root.middleware import ChannelsLoggingMiddleware websocket_urlpatterns = [ diff --git a/authentik/providers/proxy/tasks.py b/authentik/providers/proxy/tasks.py index 630b0d186..aec8e669a 100644 --- a/authentik/providers/proxy/tasks.py +++ b/authentik/providers/proxy/tasks.py @@ -3,7 +3,8 @@ from asgiref.sync import async_to_sync from channels.layers import get_channel_layer from django.db import DatabaseError, InternalError, ProgrammingError -from authentik.outposts.models import Outpost, OutpostState, OutpostType +from authentik.outposts.consumer import OUTPOST_GROUP +from authentik.outposts.models import Outpost, OutpostType from authentik.providers.proxy.models import ProxyProvider from authentik.root.celery import CELERY_APP @@ -23,13 +24,12 @@ def proxy_on_logout(session_id: str): """Update outpost instances connected to a single outpost""" layer = get_channel_layer() for outpost in Outpost.objects.filter(type=OutpostType.PROXY): - for state in OutpostState.for_outpost(outpost): - for channel in state.channel_ids: - async_to_sync(layer.send)( - channel, - { - "type": "event.provider.specific", - "sub_type": "logout", - "session_id": session_id, - }, - ) + group = OUTPOST_GROUP % {"outpost_pk": str(outpost.pk)} + async_to_sync(layer.group_send)( + group, + { + "type": "event.provider.specific", + "sub_type": "logout", + "session_id": session_id, + }, + ) diff --git a/authentik/root/settings.py b/authentik/root/settings.py index a7ed583ae..3cb081862 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -253,10 +253,10 @@ ASGI_APPLICATION = "authentik.root.asgi.application" CHANNEL_LAYERS = { "default": { - "BACKEND": "channels_redis.core.RedisChannelLayer", + "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer", "CONFIG": { "hosts": [f"{_redis_url}/{CONFIG.get('redis.db')}"], - "prefix": "authentik_channels", + "prefix": "authentik_channels_", }, }, } From dce913496e861c26062a469aa63cfea8d4ebee16 Mon Sep 17 00:00:00 2001 From: Jens L Date: Mon, 16 Oct 2023 17:28:53 +0200 Subject: [PATCH 12/23] lifecycle: re-fix system migrations (#7185) fix system migrations? Signed-off-by: Jens Langhammer --- lifecycle/system_migrations/to_0_10.py | 2 ++ lifecycle/system_migrations/to_0_13_authentik.py | 3 ++- lifecycle/system_migrations/to_0_14_events..py | 7 +++++-- lifecycle/system_migrations/to_2021_3_authenticator.py | 2 ++ lifecycle/system_migrations/to_2023_1_hibp_remove.py | 7 +++++-- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lifecycle/system_migrations/to_0_10.py b/lifecycle/system_migrations/to_0_10.py index 84ab45b39..ebad5c70a 100644 --- a/lifecycle/system_migrations/to_0_10.py +++ b/lifecycle/system_migrations/to_0_10.py @@ -2,6 +2,7 @@ from lifecycle.migrate import BaseMigration SQL_STATEMENT = """ +BEGIN TRANSACTION; DELETE FROM django_migrations WHERE app = 'passbook_stages_prompt'; DROP TABLE passbook_stages_prompt_prompt cascade; DROP TABLE passbook_stages_prompt_promptstage cascade; @@ -22,6 +23,7 @@ DELETE FROM django_migrations WHERE app = 'passbook_flows' AND name = '0008_defa DELETE FROM django_migrations WHERE app = 'passbook_flows' AND name = '0009_source_flows'; DELETE FROM django_migrations WHERE app = 'passbook_flows' AND name = '0010_provider_flows'; DELETE FROM django_migrations WHERE app = 'passbook_stages_password' AND name = '0002_passwordstage_change_flow'; +COMMIT; """ diff --git a/lifecycle/system_migrations/to_0_13_authentik.py b/lifecycle/system_migrations/to_0_13_authentik.py index 8ba702132..b621859d7 100644 --- a/lifecycle/system_migrations/to_0_13_authentik.py +++ b/lifecycle/system_migrations/to_0_13_authentik.py @@ -4,7 +4,7 @@ from redis import Redis from authentik.lib.config import CONFIG from lifecycle.migrate import BaseMigration -SQL_STATEMENT = """ +SQL_STATEMENT = """BEGIN TRANSACTION; ALTER TABLE passbook_audit_event RENAME TO authentik_audit_event; ALTER TABLE passbook_core_application RENAME TO authentik_core_application; ALTER TABLE passbook_core_group RENAME TO authentik_core_group; @@ -92,6 +92,7 @@ ALTER SEQUENCE passbook_stages_prompt_promptstage_validation_policies_id_seq REN UPDATE django_migrations SET app = replace(app, 'passbook', 'authentik'); UPDATE django_content_type SET app_label = replace(app_label, 'passbook', 'authentik'); +COMMIT; """ diff --git a/lifecycle/system_migrations/to_0_14_events..py b/lifecycle/system_migrations/to_0_14_events..py index b1a0cc727..9a7b14979 100644 --- a/lifecycle/system_migrations/to_0_14_events..py +++ b/lifecycle/system_migrations/to_0_14_events..py @@ -1,9 +1,12 @@ # flake8: noqa from lifecycle.migrate import BaseMigration -SQL_STATEMENT = """ALTER TABLE authentik_audit_event RENAME TO authentik_events_event; +SQL_STATEMENT = """BEGIN TRANSACTION; +ALTER TABLE authentik_audit_event RENAME TO authentik_events_event; UPDATE django_migrations SET app = replace(app, 'authentik_audit', 'authentik_events'); -UPDATE django_content_type SET app_label = replace(app_label, 'authentik_audit', 'authentik_events');""" +UPDATE django_content_type SET app_label = replace(app_label, 'authentik_audit', 'authentik_events'); + +COMMIT;""" class Migration(BaseMigration): diff --git a/lifecycle/system_migrations/to_2021_3_authenticator.py b/lifecycle/system_migrations/to_2021_3_authenticator.py index 3b633fef1..52a870ba2 100644 --- a/lifecycle/system_migrations/to_2021_3_authenticator.py +++ b/lifecycle/system_migrations/to_2021_3_authenticator.py @@ -2,6 +2,7 @@ from lifecycle.migrate import BaseMigration SQL_STATEMENT = """ +BEGIN TRANSACTION; ALTER TABLE authentik_stages_otp_static_otpstaticstage RENAME TO authentik_stages_authenticator_static_otpstaticstage; UPDATE django_migrations SET app = replace(app, 'authentik_stages_otp_static', 'authentik_stages_authenticator_static'); UPDATE django_content_type SET app_label = replace(app_label, 'authentik_stages_otp_static', 'authentik_stages_authenticator_static'); @@ -13,6 +14,7 @@ UPDATE django_content_type SET app_label = replace(app_label, 'authentik_stages_ ALTER TABLE authentik_stages_otp_validate_otpvalidatestage RENAME TO authentik_stages_authenticator_validate_otpvalidatestage; UPDATE django_migrations SET app = replace(app, 'authentik_stages_otp_validate', 'authentik_stages_authenticator_validate'); UPDATE django_content_type SET app_label = replace(app_label, 'authentik_stages_otp_validate', 'authentik_stages_authenticator_validate'); +COMMIT; """ diff --git a/lifecycle/system_migrations/to_2023_1_hibp_remove.py b/lifecycle/system_migrations/to_2023_1_hibp_remove.py index c43f6bb85..92ec2e1f6 100644 --- a/lifecycle/system_migrations/to_2023_1_hibp_remove.py +++ b/lifecycle/system_migrations/to_2023_1_hibp_remove.py @@ -1,8 +1,11 @@ # flake8: noqa from lifecycle.migrate import BaseMigration -SQL_STATEMENT = """DROP TABLE "authentik_policies_hibp_haveibeenpwendpolicy"; -DELETE FROM django_migrations WHERE app = 'authentik_policies_hibp';""" +SQL_STATEMENT = """ +BEGIN TRANSACTION; +DROP TABLE "authentik_policies_hibp_haveibeenpwendpolicy"; +DELETE FROM django_migrations WHERE app = 'authentik_policies_hibp'; +COMMIT;""" class Migration(BaseMigration): From e28babb0b8d90b40e71f84ca921dbedf26646c5d Mon Sep 17 00:00:00 2001 From: Jens L Date: Mon, 16 Oct 2023 17:31:50 +0200 Subject: [PATCH 13/23] core: Initial RBAC (#6806) * rename consent permission Signed-off-by: Jens Langhammer * the user version Signed-off-by: Jens Langhammer t Signed-off-by: Jens Langhammer * initial role Signed-off-by: Jens Langhammer * start form Signed-off-by: Jens Langhammer * some minor table refactoring Signed-off-by: Jens Langhammer * fix user, add assign Signed-off-by: Jens Langhammer * add roles ui Signed-off-by: Jens Langhammer * fix backend Signed-off-by: Jens Langhammer * add assign API for roles Signed-off-by: Jens Langhammer * start adding toggle buttons Signed-off-by: Jens Langhammer * start view page Signed-off-by: Jens Langhammer * exclude add_ permission for per-object perms Signed-off-by: Jens Langhammer * small cleanup Signed-off-by: Jens Langhammer * add permission list for roles Signed-off-by: Jens Langhammer * make sidebar update Signed-off-by: Jens Langhammer * fix page header not re-rendering? Signed-off-by: Jens Langhammer * fixup Signed-off-by: Jens Langhammer * add search Signed-off-by: Jens Langhammer * show first category in table groupBy except when its empty Signed-off-by: Jens Langhammer * make model and object PK optional but required together Signed-off-by: Jens Langhammer * allow for setting global perms Signed-off-by: Jens Langhammer * exclude non-authentik permissions Signed-off-by: Jens Langhammer * exclude models which aren't allowed (base models etc) Signed-off-by: Jens Langhammer * ensure all models have verbose_name set, exclude some more internal objects Signed-off-by: Jens Langhammer * lint fix Signed-off-by: Jens Langhammer * fix role perm assign Signed-off-by: Jens Langhammer * add unasign for global perms Signed-off-by: Jens Langhammer * add meta changes Signed-off-by: Jens Langhammer * clear modal state after submit Signed-off-by: Jens Langhammer * add roles to our group Signed-off-by: Jens Langhammer * fix duplicate url names Signed-off-by: Jens Langhammer * make recursive group query more usable Signed-off-by: Jens Langhammer * add name field to role itself and move group creation to signal Signed-off-by: Jens Langhammer * start sync Signed-off-by: Jens Langhammer * move rbac stuff to separate django app Signed-off-by: Jens Langhammer * fix lint and such Signed-off-by: Jens Langhammer * fix go Signed-off-by: Jens Langhammer * update Signed-off-by: Jens Langhammer * start API changes Signed-off-by: Jens Langhammer * add more API tests Signed-off-by: Jens Langhammer * make admin interface not require superuser for now, improve error handling Signed-off-by: Jens Langhammer * replace some IsAdminUser where applicable Signed-off-by: Jens Langhammer * migrate flow inspector perms to actual permission Signed-off-by: Jens Langhammer * fix license not being a serializermodel Signed-off-by: Jens Langhammer * add permission modal to models without view page Signed-off-by: Jens Langhammer * add additional permissions to assign/unassign permissions Signed-off-by: Jens Langhammer * add action to unassign user permissions Signed-off-by: Jens Langhammer * add permissions tab to remaining view pages Signed-off-by: Jens Langhammer * fix Signed-off-by: Jens Langhammer * fix flow inspector permission check Signed-off-by: Jens Langhammer * fix codecov config? Signed-off-by: Jens Langhammer * add more API tests Signed-off-by: Jens Langhammer * ensure viewsets have an order set Signed-off-by: Jens Langhammer * hopefully the last api name change Signed-off-by: Jens Langhammer * make perm modal less confusing Signed-off-by: Jens Langhammer * start user view permission page Signed-off-by: Jens Langhammer * only make delete bulk form expandable if usedBy is set Signed-off-by: Jens Langhammer * expand permission tables Signed-off-by: Jens Langhammer * add more things Signed-off-by: Jens Langhammer * add user global permission table Signed-off-by: Jens Langhammer * fix lint Signed-off-by: Jens Langhammer * fix tests' url names Signed-off-by: Jens Langhammer * add tests for assign perms Signed-off-by: Jens Langhammer * add unassign tests Signed-off-by: Jens Langhammer * rebuild permissions Signed-off-by: Jens Langhammer * prevent assigning/unassigning permissions to internal service accounts Signed-off-by: Jens Langhammer * only enable default api browser in debug Signed-off-by: Jens Langhammer * fix role object permissions showing duplicate Signed-off-by: Jens Langhammer * fix role link on role object permissions table Signed-off-by: Jens Langhammer * fix object permission modal having duplicate close buttons Signed-off-by: Jens Langhammer * return error if user has no global perm and no object perms also improve error display on table Signed-off-by: Jens Langhammer * small optimisation Signed-off-by: Jens Langhammer * optimise even more Signed-off-by: Jens Langhammer * update locale Signed-off-by: Jens Langhammer * add system permission for non-object permissions Signed-off-by: Jens Langhammer * allow access to admin interface based on perm Signed-off-by: Jens Langhammer * clean Signed-off-by: Jens Langhammer * don't exclude base models Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- .github/codecov.yml | 4 +- Makefile | 3 +- authentik/admin/api/meta.py | 6 +- authentik/admin/api/metrics.py | 4 +- authentik/admin/api/system.py | 4 +- authentik/admin/api/tasks.py | 6 +- authentik/admin/api/workers.py | 4 +- authentik/api/authorization.py | 6 +- authentik/api/decorators.py | 16 +- authentik/api/pagination.py | 7 + authentik/api/tests/test_viewsets.py | 1 + authentik/blueprints/api.py | 3 +- authentik/blueprints/v1/importer.py | 18 +- authentik/core/api/applications.py | 4 +- authentik/core/api/groups.py | 26 +- .../core/api/transactional_applications.py | 1 + authentik/core/api/used_by.py | 5 + authentik/core/api/users.py | 25 +- authentik/core/migrations/0032_group_roles.py | 45 + authentik/core/models.py | 78 +- authentik/core/signals.py | 3 + authentik/core/tests/utils.py | 12 +- authentik/enterprise/api.py | 4 +- ...e_users_license_internal_users_and_more.py | 4 + authentik/enterprise/models.py | 12 +- authentik/flows/api/bindings.py | 1 + authentik/flows/challenge.py | 7 - .../migrations/0026_alter_flow_options.py | 25 + authentik/flows/models.py | 7 +- authentik/flows/views/inspector.py | 18 +- authentik/lib/validators.py | 32 + .../migrations/0020_alter_outpost_type.py | 4 + authentik/outposts/models.py | 4 + authentik/policies/models.py | 4 +- authentik/rbac/__init__.py | 0 authentik/rbac/api/__init__.py | 0 authentik/rbac/api/rbac.py | 130 ++ authentik/rbac/api/rbac_assigned_by_roles.py | 123 ++ authentik/rbac/api/rbac_assigned_by_users.py | 129 ++ authentik/rbac/api/rbac_roles.py | 71 + authentik/rbac/api/rbac_users.py | 71 + authentik/rbac/api/roles.py | 24 + authentik/rbac/apps.py | 15 + authentik/rbac/filters.py | 26 + authentik/rbac/migrations/0001_initial.py | 47 + .../rbac/migrations/0002_systempermission.py | 33 + authentik/rbac/migrations/__init__.py | 0 authentik/rbac/models.py | 73 + authentik/rbac/permissions.py | 30 + authentik/rbac/signals.py | 67 + authentik/rbac/tests/__init__.py | 0 .../rbac/tests/test_api_assigned_by_roles.py | 151 ++ .../rbac/tests/test_api_assigned_by_users.py | 196 ++ authentik/rbac/tests/test_api_filters.py | 122 ++ authentik/rbac/tests/test_roles.py | 35 + authentik/rbac/urls.py | 24 + authentik/root/settings.py | 8 +- .../migrations/0009_throttling.py | 8 + .../stages/authenticator_static/models.py | 8 +- .../migrations/0010_alter_totpdevice_key.py | 4 + authentik/stages/authenticator_totp/models.py | 4 +- authentik/stages/consent/stage.py | 13 +- authentik/stages/prompt/api.py | 1 + blueprints/schema.json | 109 +- internal/outpost/ldap/search/memory/memory.go | 2 +- schema.yml | 1624 ++++++++++++++++- web/src/admin/AdminInterface.ts | 7 +- web/src/admin/Routes.ts | 8 + .../admin-overview/cards/AdminStatusCard.ts | 9 + .../admin/applications/ApplicationViewPage.ts | 14 +- web/src/admin/blueprints/BlueprintListPage.ts | 13 +- .../crypto/CertificateKeyPairListPage.ts | 32 +- .../enterprise/EnterpriseLicenseListPage.ts | 34 +- web/src/admin/events/RuleListPage.ts | 32 +- web/src/admin/events/TransportListPage.ts | 14 +- web/src/admin/flows/FlowViewPage.ts | 14 +- web/src/admin/groups/GroupForm.ts | 39 +- web/src/admin/groups/GroupViewPage.ts | 34 +- web/src/admin/outposts/OutpostListPage.ts | 14 +- .../outposts/ServiceConnectionListPage.ts | 39 +- web/src/admin/policies/PolicyListPage.ts | 4 + .../policies/reputation/ReputationListPage.ts | 15 +- .../PropertyMappingListPage.ts | 3 + .../providers/ldap/LDAPProviderViewPage.ts | 14 +- .../oauth2/OAuth2ProviderViewPage.ts | 7 + .../providers/proxy/ProxyProviderViewPage.ts | 14 +- .../radius/RadiusProviderViewPage.ts | 15 +- .../providers/saml/SAMLProviderViewPage.ts | 8 + .../providers/scim/SCIMProviderViewPage.ts | 14 +- web/src/admin/roles/RoleForm.ts | 56 + web/src/admin/roles/RoleListPage.ts | 98 + web/src/admin/roles/RolePermissionForm.ts | 88 + .../admin/roles/RolePermissionGlobalTable.ts | 89 + .../admin/roles/RolePermissionObjectTable.ts | 94 + web/src/admin/roles/RoleViewPage.ts | 144 ++ .../admin/sources/ldap/LDAPSourceViewPage.ts | 15 +- .../sources/oauth/OAuthSourceViewPage.ts | 14 +- .../admin/sources/plex/PlexSourceViewPage.ts | 13 +- .../admin/sources/saml/SAMLSourceViewPage.ts | 14 +- web/src/admin/stages/StageListPage.ts | 3 + .../stages/invitation/InvitationListLink.ts | 3 +- .../stages/invitation/InvitationListPage.ts | 31 +- web/src/admin/stages/prompt/PromptListPage.ts | 26 +- web/src/admin/tenants/TenantListPage.ts | 27 +- web/src/admin/tokens/TokenListPage.ts | 22 +- web/src/admin/users/RelatedUserList.ts | 67 +- .../UserAssignedGlobalPermissionsTable.ts | 88 + .../UserAssignedObjectPermissionsTable.ts | 90 + ...UserDevicesList.ts => UserDevicesTable.ts} | 4 +- web/src/admin/users/UserPermissionForm.ts | 88 + web/src/admin/users/UserViewPage.ts | 45 +- web/src/common/errors.ts | 27 + web/src/common/users.ts | 1 + web/src/elements/PageHeader.ts | 3 +- .../ak-locale-context/ak-locale-context.ts | 2 +- web/src/elements/charts/Chart.ts | 45 +- web/src/elements/forms/DeleteBulkForm.ts | 6 +- .../elements/rbac/ObjectPermissionModal.ts | 74 + .../elements/rbac/ObjectPermissionsPage.ts | 68 + .../elements/rbac/PermissionSelectModal.ts | 95 + .../elements/rbac/RoleObjectPermissionForm.ts | 107 ++ .../rbac/RoleObjectPermissionTable.ts | 97 + .../elements/rbac/UserObjectPermissionForm.ts | 111 ++ .../rbac/UserObjectPermissionTable.ts | 90 + web/src/elements/table/Table.ts | 75 +- web/src/elements/table/TablePage.ts | 8 - web/src/flow/stages/consent/ConsentStage.ts | 8 +- web/src/user/UserInterface.ts | 6 +- .../user/user-settings/UserSettingsPage.ts | 2 - web/xliff/de.xlf | 105 ++ web/xliff/en.xlf | 105 ++ web/xliff/es.xlf | 105 ++ web/xliff/fr.xlf | 105 ++ web/xliff/pl.xlf | 105 ++ web/xliff/pseudo-LOCALE.xlf | 105 ++ web/xliff/tr.xlf | 105 ++ web/xliff/zh-Hans.xlf | 151 +- web/xliff/zh-Hant.xlf | 105 ++ web/xliff/zh_TW.xlf | 105 ++ 139 files changed, 6563 insertions(+), 425 deletions(-) create mode 100644 authentik/core/migrations/0032_group_roles.py create mode 100644 authentik/flows/migrations/0026_alter_flow_options.py create mode 100644 authentik/lib/validators.py create mode 100644 authentik/rbac/__init__.py create mode 100644 authentik/rbac/api/__init__.py create mode 100644 authentik/rbac/api/rbac.py create mode 100644 authentik/rbac/api/rbac_assigned_by_roles.py create mode 100644 authentik/rbac/api/rbac_assigned_by_users.py create mode 100644 authentik/rbac/api/rbac_roles.py create mode 100644 authentik/rbac/api/rbac_users.py create mode 100644 authentik/rbac/api/roles.py create mode 100644 authentik/rbac/apps.py create mode 100644 authentik/rbac/filters.py create mode 100644 authentik/rbac/migrations/0001_initial.py create mode 100644 authentik/rbac/migrations/0002_systempermission.py create mode 100644 authentik/rbac/migrations/__init__.py create mode 100644 authentik/rbac/models.py create mode 100644 authentik/rbac/permissions.py create mode 100644 authentik/rbac/signals.py create mode 100644 authentik/rbac/tests/__init__.py create mode 100644 authentik/rbac/tests/test_api_assigned_by_roles.py create mode 100644 authentik/rbac/tests/test_api_assigned_by_users.py create mode 100644 authentik/rbac/tests/test_api_filters.py create mode 100644 authentik/rbac/tests/test_roles.py create mode 100644 authentik/rbac/urls.py create mode 100644 web/src/admin/roles/RoleForm.ts create mode 100644 web/src/admin/roles/RoleListPage.ts create mode 100644 web/src/admin/roles/RolePermissionForm.ts create mode 100644 web/src/admin/roles/RolePermissionGlobalTable.ts create mode 100644 web/src/admin/roles/RolePermissionObjectTable.ts create mode 100644 web/src/admin/roles/RoleViewPage.ts create mode 100644 web/src/admin/users/UserAssignedGlobalPermissionsTable.ts create mode 100644 web/src/admin/users/UserAssignedObjectPermissionsTable.ts rename web/src/admin/users/{UserDevicesList.ts => UserDevicesTable.ts} (96%) create mode 100644 web/src/admin/users/UserPermissionForm.ts create mode 100644 web/src/elements/rbac/ObjectPermissionModal.ts create mode 100644 web/src/elements/rbac/ObjectPermissionsPage.ts create mode 100644 web/src/elements/rbac/PermissionSelectModal.ts create mode 100644 web/src/elements/rbac/RoleObjectPermissionForm.ts create mode 100644 web/src/elements/rbac/RoleObjectPermissionTable.ts create mode 100644 web/src/elements/rbac/UserObjectPermissionForm.ts create mode 100644 web/src/elements/rbac/UserObjectPermissionTable.ts diff --git a/.github/codecov.yml b/.github/codecov.yml index 1042659ed..8db67faf6 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -6,5 +6,5 @@ coverage: # adjust accordingly based on how flaky your tests are # this allows a 1% drop from the previous base commit coverage threshold: 1% - notify: - after_n_builds: 3 +comment: + after_n_builds: 3 diff --git a/Makefile b/Makefile index bb7f70a43..9eb357164 100644 --- a/Makefile +++ b/Makefile @@ -62,8 +62,9 @@ lint-fix: ## Lint and automatically fix errors in the python source code. Repor codespell -w $(CODESPELL_ARGS) lint: ## Lint the python and golang sources - pylint $(PY_SOURCES) bandit -r $(PY_SOURCES) -x node_modules + ./web/node_modules/.bin/pyright $(PY_SOURCES) + pylint $(PY_SOURCES) golangci-lint run -v migrate: ## Run the Authentik Django server's migrations diff --git a/authentik/admin/api/meta.py b/authentik/admin/api/meta.py index 25d944411..52640b8c5 100644 --- a/authentik/admin/api/meta.py +++ b/authentik/admin/api/meta.py @@ -1,7 +1,7 @@ """Meta API""" from drf_spectacular.utils import extend_schema from rest_framework.fields import CharField -from rest_framework.permissions import IsAdminUser +from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ViewSet @@ -21,7 +21,7 @@ class AppSerializer(PassiveSerializer): class AppsViewSet(ViewSet): """Read-only view list all installed apps""" - permission_classes = [IsAdminUser] + permission_classes = [IsAuthenticated] @extend_schema(responses={200: AppSerializer(many=True)}) def list(self, request: Request) -> Response: @@ -35,7 +35,7 @@ class AppsViewSet(ViewSet): class ModelViewSet(ViewSet): """Read-only view list all installed models""" - permission_classes = [IsAdminUser] + permission_classes = [IsAuthenticated] @extend_schema(responses={200: AppSerializer(many=True)}) def list(self, request: Request) -> Response: diff --git a/authentik/admin/api/metrics.py b/authentik/admin/api/metrics.py index 08aea59d2..af32662b1 100644 --- a/authentik/admin/api/metrics.py +++ b/authentik/admin/api/metrics.py @@ -5,7 +5,7 @@ from django.db.models.functions import ExtractHour from drf_spectacular.utils import extend_schema, extend_schema_field from guardian.shortcuts import get_objects_for_user from rest_framework.fields import IntegerField, SerializerMethodField -from rest_framework.permissions import IsAdminUser +from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView @@ -68,7 +68,7 @@ class LoginMetricsSerializer(PassiveSerializer): class AdministrationMetricsViewSet(APIView): """Login Metrics per 1h""" - permission_classes = [IsAdminUser] + permission_classes = [IsAuthenticated] @extend_schema(responses={200: LoginMetricsSerializer(many=False)}) def get(self, request: Request) -> Response: diff --git a/authentik/admin/api/system.py b/authentik/admin/api/system.py index 11dc5dfec..7e7d2d920 100644 --- a/authentik/admin/api/system.py +++ b/authentik/admin/api/system.py @@ -8,7 +8,6 @@ from django.utils.timezone import now from drf_spectacular.utils import extend_schema from gunicorn import version_info as gunicorn_version from rest_framework.fields import SerializerMethodField -from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView @@ -17,6 +16,7 @@ from authentik.core.api.utils import PassiveSerializer from authentik.lib.utils.reflection import get_env from authentik.outposts.apps import MANAGED_OUTPOST from authentik.outposts.models import Outpost +from authentik.rbac.permissions import HasPermission class RuntimeDict(TypedDict): @@ -88,7 +88,7 @@ class SystemSerializer(PassiveSerializer): class SystemView(APIView): """Get system information.""" - permission_classes = [IsAdminUser] + permission_classes = [HasPermission("authentik_rbac.view_system_info")] pagination_class = None filter_backends = [] serializer_class = SystemSerializer diff --git a/authentik/admin/api/tasks.py b/authentik/admin/api/tasks.py index 00fbe4e08..72714dad5 100644 --- a/authentik/admin/api/tasks.py +++ b/authentik/admin/api/tasks.py @@ -14,14 +14,15 @@ from rest_framework.fields import ( ListField, SerializerMethodField, ) -from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.viewsets import ViewSet from structlog.stdlib import get_logger +from authentik.api.decorators import permission_required from authentik.core.api.utils import PassiveSerializer from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus +from authentik.rbac.permissions import HasPermission LOGGER = get_logger() @@ -63,7 +64,7 @@ class TaskSerializer(PassiveSerializer): class TaskViewSet(ViewSet): """Read-only view set that returns all background tasks""" - permission_classes = [IsAdminUser] + permission_classes = [HasPermission("authentik_rbac.view_system_tasks")] serializer_class = TaskSerializer @extend_schema( @@ -93,6 +94,7 @@ class TaskViewSet(ViewSet): tasks = sorted(TaskInfo.all().values(), key=lambda task: task.task_name) return Response(TaskSerializer(tasks, many=True).data) + @permission_required(None, ["authentik_rbac.run_system_tasks"]) @extend_schema( request=OpenApiTypes.NONE, responses={ diff --git a/authentik/admin/api/workers.py b/authentik/admin/api/workers.py index ab6d03873..3b5da0594 100644 --- a/authentik/admin/api/workers.py +++ b/authentik/admin/api/workers.py @@ -2,18 +2,18 @@ from django.conf import settings from drf_spectacular.utils import extend_schema, inline_serializer from rest_framework.fields import IntegerField -from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView +from authentik.rbac.permissions import HasPermission from authentik.root.celery import CELERY_APP class WorkerView(APIView): """Get currently connected worker count.""" - permission_classes = [IsAdminUser] + permission_classes = [HasPermission("authentik_rbac.view_system_info")] @extend_schema(responses=inline_serializer("Workers", fields={"count": IntegerField()})) def get(self, request: Request) -> Response: diff --git a/authentik/api/authorization.py b/authentik/api/authorization.py index 05cd45819..e3ae48e5c 100644 --- a/authentik/api/authorization.py +++ b/authentik/api/authorization.py @@ -7,9 +7,9 @@ from rest_framework.authentication import get_authorization_header from rest_framework.filters import BaseFilterBackend from rest_framework.permissions import BasePermission from rest_framework.request import Request -from rest_framework_guardian.filters import ObjectPermissionsFilter from authentik.api.authentication import validate_auth +from authentik.rbac.filters import ObjectFilter class OwnerFilter(BaseFilterBackend): @@ -26,14 +26,14 @@ class OwnerFilter(BaseFilterBackend): class SecretKeyFilter(DjangoFilterBackend): """Allow access to all objects when authenticated with secret key as token. - Replaces both DjangoFilterBackend and ObjectPermissionsFilter""" + Replaces both DjangoFilterBackend and ObjectFilter""" def filter_queryset(self, request: Request, queryset: QuerySet, view) -> QuerySet: auth_header = get_authorization_header(request) token = validate_auth(auth_header) if token and token == settings.SECRET_KEY: return queryset - queryset = ObjectPermissionsFilter().filter_queryset(request, queryset, view) + queryset = ObjectFilter().filter_queryset(request, queryset, view) return super().filter_queryset(request, queryset, view) diff --git a/authentik/api/decorators.py b/authentik/api/decorators.py index a79cebc92..0cd737c76 100644 --- a/authentik/api/decorators.py +++ b/authentik/api/decorators.py @@ -10,7 +10,7 @@ from structlog.stdlib import get_logger LOGGER = get_logger() -def permission_required(perm: Optional[str] = None, other_perms: Optional[list[str]] = None): +def permission_required(obj_perm: Optional[str] = None, global_perms: Optional[list[str]] = None): """Check permissions for a single custom action""" def wrapper_outter(func: Callable): @@ -18,15 +18,17 @@ def permission_required(perm: Optional[str] = None, other_perms: Optional[list[s @wraps(func) def wrapper(self: ModelViewSet, request: Request, *args, **kwargs) -> Response: - if perm: + if obj_perm: obj = self.get_object() - if not request.user.has_perm(perm, obj): - LOGGER.debug("denying access for object", user=request.user, perm=perm, obj=obj) + if not request.user.has_perm(obj_perm, obj): + LOGGER.debug( + "denying access for object", user=request.user, perm=obj_perm, obj=obj + ) return self.permission_denied(request) - if other_perms: - for other_perm in other_perms: + if global_perms: + for other_perm in global_perms: if not request.user.has_perm(other_perm): - LOGGER.debug("denying access for other", user=request.user, perm=perm) + LOGGER.debug("denying access for other", user=request.user, perm=other_perm) return self.permission_denied(request) return func(self, request, *args, **kwargs) diff --git a/authentik/api/pagination.py b/authentik/api/pagination.py index 7125c8968..402dbac9b 100644 --- a/authentik/api/pagination.py +++ b/authentik/api/pagination.py @@ -77,3 +77,10 @@ class Pagination(pagination.PageNumberPagination): }, "required": ["pagination", "results"], } + + +class SmallerPagination(Pagination): + """Smaller pagination for objects which might require a lot of queries + to retrieve all data for.""" + + max_page_size = 10 diff --git a/authentik/api/tests/test_viewsets.py b/authentik/api/tests/test_viewsets.py index dee956461..ac3d7da62 100644 --- a/authentik/api/tests/test_viewsets.py +++ b/authentik/api/tests/test_viewsets.py @@ -16,6 +16,7 @@ def viewset_tester_factory(test_viewset: type[ModelViewSet]) -> Callable: def tester(self: TestModelViewSets): self.assertIsNotNone(getattr(test_viewset, "search_fields", None)) + self.assertIsNotNone(getattr(test_viewset, "ordering", None)) filterset_class = getattr(test_viewset, "filterset_class", None) if not filterset_class: self.assertIsNotNone(getattr(test_viewset, "filterset_fields", None)) diff --git a/authentik/blueprints/api.py b/authentik/blueprints/api.py index 9fac62c72..721eb5dcb 100644 --- a/authentik/blueprints/api.py +++ b/authentik/blueprints/api.py @@ -4,7 +4,6 @@ from drf_spectacular.utils import extend_schema, inline_serializer from rest_framework.decorators import action from rest_framework.exceptions import ValidationError from rest_framework.fields import CharField, DateTimeField, JSONField -from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.serializers import ListSerializer, ModelSerializer @@ -87,11 +86,11 @@ class BlueprintInstanceSerializer(ModelSerializer): class BlueprintInstanceViewSet(UsedByMixin, ModelViewSet): """Blueprint instances""" - permission_classes = [IsAdminUser] serializer_class = BlueprintInstanceSerializer queryset = BlueprintInstance.objects.all() search_fields = ["name", "path"] filterset_fields = ["name", "path"] + ordering = ["name"] @extend_schema( responses={ diff --git a/authentik/blueprints/v1/importer.py b/authentik/blueprints/v1/importer.py index 76c667c25..f2191548e 100644 --- a/authentik/blueprints/v1/importer.py +++ b/authentik/blueprints/v1/importer.py @@ -35,25 +35,28 @@ from authentik.core.models import ( Source, UserSourceConnection, ) +from authentik.enterprise.models import LicenseUsage from authentik.events.utils import cleanse_dict from authentik.flows.models import FlowToken, Stage from authentik.lib.models import SerializerModel from authentik.lib.sentry import SentryIgnoredException from authentik.outposts.models import OutpostServiceConnection from authentik.policies.models import Policy, PolicyBindingModel +from authentik.providers.scim.models import SCIMGroup, SCIMUser # Context set when the serializer is created in a blueprint context # Update website/developer-docs/blueprints/v1/models.md when used SERIALIZER_CONTEXT_BLUEPRINT = "blueprint_entry" -def is_model_allowed(model: type[Model]) -> bool: - """Check if model is allowed""" +def excluded_models() -> list[type[Model]]: + """Return a list of all excluded models that shouldn't be exposed via API + or other means (internal only, base classes, non-used objects, etc)""" # pylint: disable=imported-auth-user from django.contrib.auth.models import Group as DjangoGroup from django.contrib.auth.models import User as DjangoUser - excluded_models = ( + return ( DjangoUser, DjangoGroup, # Base classes @@ -69,8 +72,15 @@ def is_model_allowed(model: type[Model]) -> bool: AuthenticatedSession, # Classes which are only internally managed FlowToken, + LicenseUsage, + SCIMGroup, + SCIMUser, ) - return model not in excluded_models and issubclass(model, (SerializerModel, BaseMetaModel)) + + +def is_model_allowed(model: type[Model]) -> bool: + """Check if model is allowed""" + return model not in excluded_models() and issubclass(model, (SerializerModel, BaseMetaModel)) class DoRollback(SentryIgnoredException): diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index f40aa3165..478181c28 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -17,7 +17,6 @@ from rest_framework.request import Request from rest_framework.response import Response 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 @@ -38,6 +37,7 @@ from authentik.lib.utils.file import ( from authentik.policies.api.exec import PolicyTestResultSerializer from authentik.policies.engine import PolicyEngine from authentik.policies.types import PolicyResult +from authentik.rbac.filters import ObjectFilter LOGGER = get_logger() @@ -122,7 +122,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet): def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet: """Custom filter_queryset method which ignores guardian, but still supports sorting""" for backend in list(self.filter_backends): - if backend == ObjectPermissionsFilter: + if backend == ObjectFilter: continue queryset = backend().filter_queryset(self.request, queryset, self) return queryset diff --git a/authentik/core/api/groups.py b/authentik/core/api/groups.py index 961633037..4c6a8b509 100644 --- a/authentik/core/api/groups.py +++ b/authentik/core/api/groups.py @@ -2,7 +2,6 @@ from json import loads from typing import Optional -from django.db.models.query import QuerySet from django.http import Http404 from django_filters.filters import CharFilter, ModelMultipleChoiceFilter from django_filters.filterset import FilterSet @@ -14,12 +13,12 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.serializers import ListSerializer, ModelSerializer, ValidationError from rest_framework.viewsets import ModelViewSet -from rest_framework_guardian.filters import ObjectPermissionsFilter from authentik.api.decorators import permission_required from authentik.core.api.used_by import UsedByMixin from authentik.core.api.utils import PassiveSerializer, is_dict from authentik.core.models import Group, User +from authentik.rbac.api.roles import RoleSerializer class GroupMemberSerializer(ModelSerializer): @@ -49,6 +48,12 @@ class GroupSerializer(ModelSerializer): users_obj = ListSerializer( child=GroupMemberSerializer(), read_only=True, source="users", required=False ) + roles_obj = ListSerializer( + child=RoleSerializer(), + read_only=True, + source="roles", + required=False, + ) parent_name = CharField(source="parent.name", read_only=True, allow_null=True) num_pk = IntegerField(read_only=True) @@ -71,8 +76,10 @@ class GroupSerializer(ModelSerializer): "parent", "parent_name", "users", - "attributes", "users_obj", + "attributes", + "roles", + "roles_obj", ] extra_kwargs = { "users": { @@ -138,19 +145,6 @@ class GroupViewSet(UsedByMixin, ModelViewSet): filterset_class = GroupFilter ordering = ["name"] - def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet: - """Custom filter_queryset method which ignores guardian, but still supports sorting""" - for backend in list(self.filter_backends): - if backend == ObjectPermissionsFilter: - continue - queryset = backend().filter_queryset(self.request, queryset, self) - return queryset - - def filter_queryset(self, queryset): - if self.request.user.has_perm("authentik_core.view_group"): - return self._filter_queryset_for_list(queryset) - return super().filter_queryset(queryset) - @permission_required(None, ["authentik_core.add_user"]) @extend_schema( request=UserAccountSerializer, diff --git a/authentik/core/api/transactional_applications.py b/authentik/core/api/transactional_applications.py index 9cc0ab0e5..19b6ea465 100644 --- a/authentik/core/api/transactional_applications.py +++ b/authentik/core/api/transactional_applications.py @@ -119,6 +119,7 @@ class TransactionApplicationResponseSerializer(PassiveSerializer): class TransactionalApplicationView(APIView): """Create provider and application and attach them in a single transaction""" + # TODO: Migrate to a more specific permission permission_classes = [IsAdminUser] @extend_schema( diff --git a/authentik/core/api/used_by.py b/authentik/core/api/used_by.py index 66f0a70dd..25e7d8295 100644 --- a/authentik/core/api/used_by.py +++ b/authentik/core/api/used_by.py @@ -73,6 +73,11 @@ class UsedByMixin: # but so we only apply them once, have a simple flag for the first object first_object = True + # TODO: This will only return the used-by references that the user can see + # Either we have to leak model information here to not make the list + # useless if the user doesn't have all permissions, or we need to double + # query and check if there is a difference between modes the user can see + # and can't see and add a warning for obj in get_objects_for_user( request.user, f"{app}.view_{model_name}", manager ).all(): diff --git a/authentik/core/api/users.py b/authentik/core/api/users.py index be59dc1c1..d4adacc97 100644 --- a/authentik/core/api/users.py +++ b/authentik/core/api/users.py @@ -7,7 +7,6 @@ from django.contrib.auth import update_session_auth_hash from django.contrib.sessions.backends.cache import KEY_PREFIX from django.core.cache import cache from django.db.models.functions import ExtractHour -from django.db.models.query import QuerySet from django.db.transaction import atomic from django.db.utils import IntegrityError from django.urls import reverse_lazy @@ -52,7 +51,6 @@ from rest_framework.serializers import ( ) from rest_framework.validators import UniqueValidator from rest_framework.viewsets import ModelViewSet -from rest_framework_guardian.filters import ObjectPermissionsFilter from structlog.stdlib import get_logger from authentik.admin.api.metrics import CoordinateSerializer @@ -205,6 +203,7 @@ class UserSelfSerializer(ModelSerializer): groups = SerializerMethodField() uid = CharField(read_only=True) settings = SerializerMethodField() + system_permissions = SerializerMethodField() @extend_schema_field( ListSerializer( @@ -226,6 +225,14 @@ class UserSelfSerializer(ModelSerializer): """Get user settings with tenant and group settings applied""" return user.group_attributes(self._context["request"]).get("settings", {}) + def get_system_permissions(self, user: User) -> list[str]: + """Get all system permissions assigned to the user""" + return list( + user.user_permissions.filter( + content_type__app_label="authentik_rbac", content_type__model="systempermission" + ).values_list("codename", flat=True) + ) + class Meta: model = User fields = [ @@ -240,6 +247,7 @@ class UserSelfSerializer(ModelSerializer): "uid", "settings", "type", + "system_permissions", ] extra_kwargs = { "is_active": {"read_only": True}, @@ -654,19 +662,6 @@ class UserViewSet(UsedByMixin, ModelViewSet): return Response(status=204) - def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet: - """Custom filter_queryset method which ignores guardian, but still supports sorting""" - for backend in list(self.filter_backends): - if backend == ObjectPermissionsFilter: - continue - queryset = backend().filter_queryset(self.request, queryset, self) - return queryset - - def filter_queryset(self, queryset): - if self.request.user.has_perm("authentik_core.view_user"): - return self._filter_queryset_for_list(queryset) - return super().filter_queryset(queryset) - @extend_schema( responses={ 200: inline_serializer( diff --git a/authentik/core/migrations/0032_group_roles.py b/authentik/core/migrations/0032_group_roles.py new file mode 100644 index 000000000..754b1bfba --- /dev/null +++ b/authentik/core/migrations/0032_group_roles.py @@ -0,0 +1,45 @@ +# Generated by Django 4.2.6 on 2023-10-11 13:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_core", "0031_alter_user_type"), + ("authentik_rbac", "0001_initial"), + ] + + operations = [ + migrations.AlterModelOptions( + name="group", + options={"verbose_name": "Group", "verbose_name_plural": "Groups"}, + ), + migrations.AlterModelOptions( + name="token", + options={ + "permissions": [("view_token_key", "View token's key")], + "verbose_name": "Token", + "verbose_name_plural": "Tokens", + }, + ), + migrations.AlterModelOptions( + name="user", + options={ + "permissions": [ + ("reset_user_password", "Reset Password"), + ("impersonate", "Can impersonate other users"), + ("assign_user_permissions", "Can assign permissions to users"), + ("unassign_user_permissions", "Can unassign permissions from users"), + ], + "verbose_name": "User", + "verbose_name_plural": "Users", + }, + ), + migrations.AddField( + model_name="group", + name="roles", + field=models.ManyToManyField( + blank=True, related_name="ak_groups", to="authentik_rbac.role" + ), + ), + ] diff --git a/authentik/core/models.py b/authentik/core/models.py index 7f4c25ca7..5365ef693 100644 --- a/authentik/core/models.py +++ b/authentik/core/models.py @@ -1,7 +1,7 @@ """authentik core models""" from datetime import timedelta from hashlib import sha256 -from typing import Any, Optional +from typing import Any, Optional, Self from uuid import uuid4 from deepmerge import always_merger @@ -88,6 +88,8 @@ class Group(SerializerModel): default=False, help_text=_("Users added to this group will be superusers.") ) + roles = models.ManyToManyField("authentik_rbac.Role", related_name="ak_groups", blank=True) + parent = models.ForeignKey( "Group", blank=True, @@ -115,6 +117,38 @@ class Group(SerializerModel): """Recursively check if `user` is member of us, or any parent.""" return user.all_groups().filter(group_uuid=self.group_uuid).exists() + def children_recursive(self: Self | QuerySet["Group"]) -> QuerySet["Group"]: + """Recursively get all groups that have this as parent or are indirectly related""" + direct_groups = [] + if isinstance(self, QuerySet): + direct_groups = list(x for x in self.all().values_list("pk", flat=True).iterator()) + else: + direct_groups = [self.pk] + if len(direct_groups) < 1: + return Group.objects.none() + query = """ + WITH RECURSIVE parents AS ( + SELECT authentik_core_group.*, 0 AS relative_depth + FROM authentik_core_group + WHERE authentik_core_group.group_uuid = ANY(%s) + + UNION ALL + + SELECT authentik_core_group.*, parents.relative_depth + 1 + FROM authentik_core_group, parents + WHERE ( + authentik_core_group.group_uuid = parents.parent_id and + parents.relative_depth < 20 + ) + ) + SELECT group_uuid + FROM parents + GROUP BY group_uuid, name + ORDER BY name; + """ + group_pks = [group.pk for group in Group.objects.raw(query, [direct_groups]).iterator()] + return Group.objects.filter(pk__in=group_pks) + def __str__(self): return f"Group {self.name}" @@ -125,6 +159,8 @@ class Group(SerializerModel): "parent", ), ) + verbose_name = _("Group") + verbose_name_plural = _("Groups") class UserManager(DjangoUserManager): @@ -160,33 +196,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser): """Recursively get all groups this user is a member of. At least one query is done to get the direct groups of the user, with groups there are at most 3 queries done""" - direct_groups = list( - x for x in self.ak_groups.all().values_list("pk", flat=True).iterator() - ) - if len(direct_groups) < 1: - return Group.objects.none() - query = """ - WITH RECURSIVE parents AS ( - SELECT authentik_core_group.*, 0 AS relative_depth - FROM authentik_core_group - WHERE authentik_core_group.group_uuid = ANY(%s) - - UNION ALL - - SELECT authentik_core_group.*, parents.relative_depth + 1 - FROM authentik_core_group, parents - WHERE ( - authentik_core_group.group_uuid = parents.parent_id and - parents.relative_depth < 20 - ) - ) - SELECT group_uuid - FROM parents - GROUP BY group_uuid, name - ORDER BY name; - """ - group_pks = [group.pk for group in Group.objects.raw(query, [direct_groups]).iterator()] - return Group.objects.filter(pk__in=group_pks) + return Group.children_recursive(self.ak_groups.all()) def group_attributes(self, request: Optional[HttpRequest] = None) -> dict[str, Any]: """Get a dictionary containing the attributes from all groups the user belongs to, @@ -261,12 +271,14 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser): return get_avatar(self) class Meta: - permissions = ( - ("reset_user_password", "Reset Password"), - ("impersonate", "Can impersonate other users"), - ) verbose_name = _("User") verbose_name_plural = _("Users") + permissions = [ + ("reset_user_password", _("Reset Password")), + ("impersonate", _("Can impersonate other users")), + ("assign_user_permissions", _("Can assign permissions to users")), + ("unassign_user_permissions", _("Can unassign permissions from users")), + ] class Provider(SerializerModel): @@ -675,7 +687,7 @@ class Token(SerializerModel, ManagedModel, ExpiringModel): models.Index(fields=["identifier"]), models.Index(fields=["key"]), ] - permissions = (("view_token_key", "View token's key"),) + permissions = [("view_token_key", _("View token's key"))] class PropertyMapping(SerializerModel, ManagedModel): diff --git a/authentik/core/signals.py b/authentik/core/signals.py index 76cbd38ff..31340fce9 100644 --- a/authentik/core/signals.py +++ b/authentik/core/signals.py @@ -7,6 +7,7 @@ from django.db.models import Model from django.db.models.signals import post_save, pre_delete, pre_save from django.dispatch import receiver from django.http.request import HttpRequest +from structlog.stdlib import get_logger from authentik.core.models import Application, AuthenticatedSession, BackchannelProvider, User @@ -15,6 +16,8 @@ password_changed = Signal() # Arguments: credentials: dict[str, any], request: HttpRequest, stage: Stage login_failed = Signal() +LOGGER = get_logger() + @receiver(post_save, sender=Application) def post_save_application(sender: type[Model], instance, created: bool, **_): diff --git a/authentik/core/tests/utils.py b/authentik/core/tests/utils.py index 59294e6fd..da4294f42 100644 --- a/authentik/core/tests/utils.py +++ b/authentik/core/tests/utils.py @@ -21,10 +21,9 @@ def create_test_flow( ) -def create_test_admin_user(name: Optional[str] = None, **kwargs) -> User: - """Generate a test-admin user""" +def create_test_user(name: Optional[str] = None, **kwargs) -> User: + """Generate a test user""" uid = generate_id(20) if not name else name - group = Group.objects.create(name=uid, is_superuser=True) kwargs.setdefault("email", f"{uid}@goauthentik.io") kwargs.setdefault("username", uid) user: User = User.objects.create( @@ -33,6 +32,13 @@ def create_test_admin_user(name: Optional[str] = None, **kwargs) -> User: ) user.set_password(uid) user.save() + return user + + +def create_test_admin_user(name: Optional[str] = None, **kwargs) -> User: + """Generate a test-admin user""" + user = create_test_user(name, **kwargs) + group = Group.objects.create(name=user.name or name, is_superuser=True) group.users.add(user) return user diff --git a/authentik/enterprise/api.py b/authentik/enterprise/api.py index 6a215b1fe..fdf0a11fc 100644 --- a/authentik/enterprise/api.py +++ b/authentik/enterprise/api.py @@ -6,7 +6,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema, inline_serializer from rest_framework.decorators import action from rest_framework.fields import BooleanField, CharField, DateTimeField, IntegerField -from rest_framework.permissions import IsAdminUser, IsAuthenticated +from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request from rest_framework.response import Response from rest_framework.serializers import ModelSerializer @@ -84,7 +84,7 @@ class LicenseViewSet(UsedByMixin, ModelViewSet): 200: inline_serializer("InstallIDSerializer", {"install_id": CharField(required=True)}), }, ) - @action(detail=False, methods=["GET"], permission_classes=[IsAdminUser]) + @action(detail=False, methods=["GET"]) def get_install_id(self, request: Request) -> Response: """Get install_id""" return Response( diff --git a/authentik/enterprise/migrations/0002_rename_users_license_internal_users_and_more.py b/authentik/enterprise/migrations/0002_rename_users_license_internal_users_and_more.py index b3c199743..da574762e 100644 --- a/authentik/enterprise/migrations/0002_rename_users_license_internal_users_and_more.py +++ b/authentik/enterprise/migrations/0002_rename_users_license_internal_users_and_more.py @@ -33,4 +33,8 @@ class Migration(migrations.Migration): "verbose_name_plural": "License Usage Records", }, ), + migrations.AlterModelOptions( + name="license", + options={"verbose_name": "License", "verbose_name_plural": "Licenses"}, + ), ] diff --git a/authentik/enterprise/models.py b/authentik/enterprise/models.py index d10acd3ef..aca8f0b02 100644 --- a/authentik/enterprise/models.py +++ b/authentik/enterprise/models.py @@ -19,8 +19,10 @@ from django.utils.translation import gettext as _ from guardian.shortcuts import get_anonymous_user from jwt import PyJWTError, decode, get_unverified_header from rest_framework.exceptions import ValidationError +from rest_framework.serializers import BaseSerializer from authentik.core.models import ExpiringModel, User, UserTypes +from authentik.lib.models import SerializerModel from authentik.root.install_id import get_install_id @@ -151,7 +153,7 @@ class LicenseKey: return usage.record_date -class License(models.Model): +class License(SerializerModel): """An authentik enterprise license""" license_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) @@ -162,6 +164,12 @@ class License(models.Model): internal_users = models.BigIntegerField() external_users = models.BigIntegerField() + @property + def serializer(self) -> type[BaseSerializer]: + from authentik.enterprise.api import LicenseSerializer + + return LicenseSerializer + @property def status(self) -> LicenseKey: """Get parsed license status""" @@ -169,6 +177,8 @@ class License(models.Model): class Meta: indexes = (HashIndex(fields=("key",)),) + verbose_name = _("License") + verbose_name_plural = _("Licenses") def usage_expiry(): diff --git a/authentik/flows/api/bindings.py b/authentik/flows/api/bindings.py index bda3ab323..96e2e4662 100644 --- a/authentik/flows/api/bindings.py +++ b/authentik/flows/api/bindings.py @@ -45,3 +45,4 @@ class FlowStageBindingViewSet(UsedByMixin, ModelViewSet): serializer_class = FlowStageBindingSerializer filterset_fields = "__all__" search_fields = ["stage__name"] + ordering = ["order"] diff --git a/authentik/flows/challenge.py b/authentik/flows/challenge.py index e1eeb56fd..eb968ce79 100644 --- a/authentik/flows/challenge.py +++ b/authentik/flows/challenge.py @@ -132,13 +132,6 @@ class PermissionDict(TypedDict): name: str -class PermissionSerializer(PassiveSerializer): - """Permission used for consent""" - - name = CharField(allow_blank=True) - id = CharField() - - class ChallengeResponse(PassiveSerializer): """Base class for all challenge responses""" diff --git a/authentik/flows/migrations/0026_alter_flow_options.py b/authentik/flows/migrations/0026_alter_flow_options.py new file mode 100644 index 000000000..c53d3774e --- /dev/null +++ b/authentik/flows/migrations/0026_alter_flow_options.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.6 on 2023-10-10 17:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_flows", "0025_alter_flowstagebinding_evaluate_on_plan_and_more"), + ] + + operations = [ + migrations.AlterModelOptions( + name="flow", + options={ + "permissions": [ + ("export_flow", "Can export a Flow"), + ("inspect_flow", "Can inspect a Flow's execution"), + ("view_flow_cache", "View Flow's cache metrics"), + ("clear_flow_cache", "Clear Flow's cache metrics"), + ], + "verbose_name": "Flow", + "verbose_name_plural": "Flows", + }, + ), + ] diff --git a/authentik/flows/models.py b/authentik/flows/models.py index f638ef04d..67f7f0a9c 100644 --- a/authentik/flows/models.py +++ b/authentik/flows/models.py @@ -194,9 +194,10 @@ class Flow(SerializerModel, PolicyBindingModel): verbose_name_plural = _("Flows") permissions = [ - ("export_flow", "Can export a Flow"), - ("view_flow_cache", "View Flow's cache metrics"), - ("clear_flow_cache", "Clear Flow's cache metrics"), + ("export_flow", _("Can export a Flow")), + ("inspect_flow", _("Can inspect a Flow's execution")), + ("view_flow_cache", _("View Flow's cache metrics")), + ("clear_flow_cache", _("Clear Flow's cache metrics")), ] diff --git a/authentik/flows/views/inspector.py b/authentik/flows/views/inspector.py index 3593c65da..4800ead6d 100644 --- a/authentik/flows/views/inspector.py +++ b/authentik/flows/views/inspector.py @@ -3,6 +3,7 @@ from hashlib import sha256 from typing import Any from django.conf import settings +from django.http import Http404 from django.http.request import HttpRequest from django.http.response import HttpResponse from django.shortcuts import get_object_or_404 @@ -11,7 +12,6 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import OpenApiResponse, extend_schema from rest_framework.fields import BooleanField, ListField, SerializerMethodField -from rest_framework.permissions import IsAdminUser from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView @@ -68,21 +68,19 @@ class FlowInspectionSerializer(PassiveSerializer): class FlowInspectorView(APIView): """Flow inspector API""" - permission_classes = [IsAdminUser] - flow: Flow _logger: BoundLogger - - def check_permissions(self, request): - """Always allow access when in debug mode""" - if settings.DEBUG: - return None - return super().check_permissions(request) + permission_classes = [] def setup(self, request: HttpRequest, flow_slug: str): super().setup(request, flow_slug=flow_slug) - self.flow = get_object_or_404(Flow.objects.select_related(), slug=flow_slug) self._logger = get_logger().bind(flow_slug=flow_slug) + self.flow = get_object_or_404(Flow.objects.select_related(), slug=flow_slug) + if settings.DEBUG: + return + if request.user.has_perm("authentik_flow.inspect_flow", self.flow): + return + raise Http404 @extend_schema( responses={ diff --git a/authentik/lib/validators.py b/authentik/lib/validators.py new file mode 100644 index 000000000..7c67da8c1 --- /dev/null +++ b/authentik/lib/validators.py @@ -0,0 +1,32 @@ +"""Serializer validators""" +from typing import Optional + +from django.utils.translation import gettext_lazy as _ +from rest_framework.exceptions import ValidationError +from rest_framework.serializers import Serializer +from rest_framework.utils.representation import smart_repr + + +class RequiredTogetherValidator: + """Serializer-level validator that ensures all fields in `fields` are only + used together""" + + fields: list[str] + requires_context = True + message = _("The fields {field_names} must be used together.") + + def __init__(self, fields: list[str], message: Optional[str] = None) -> None: + self.fields = fields + self.message = message or self.message + + def __call__(self, attrs: dict, serializer: Serializer): + """Check that if any of the fields in `self.fields` are set, all of them must be set""" + if any(field in attrs for field in self.fields) and not all( + field in attrs for field in self.fields + ): + field_names = ", ".join(self.fields) + message = self.message.format(field_names=field_names) + raise ValidationError(message, code="required") + + def __repr__(self): + return "<%s(fields=%s)>" % (self.__class__.__name__, smart_repr(self.fields)) diff --git a/authentik/outposts/migrations/0020_alter_outpost_type.py b/authentik/outposts/migrations/0020_alter_outpost_type.py index 5036137c0..b643f8d47 100644 --- a/authentik/outposts/migrations/0020_alter_outpost_type.py +++ b/authentik/outposts/migrations/0020_alter_outpost_type.py @@ -28,4 +28,8 @@ class Migration(migrations.Migration): verbose_name="Managed by authentik", ), ), + migrations.AlterModelOptions( + name="outpost", + options={"verbose_name": "Outpost", "verbose_name_plural": "Outposts"}, + ), ] diff --git a/authentik/outposts/models.py b/authentik/outposts/models.py index 878a3e9e6..f876a0cf3 100644 --- a/authentik/outposts/models.py +++ b/authentik/outposts/models.py @@ -405,6 +405,10 @@ class Outpost(SerializerModel, ManagedModel): def __str__(self) -> str: return f"Outpost {self.name}" + class Meta: + verbose_name = _("Outpost") + verbose_name_plural = _("Outposts") + @dataclass class OutpostState: diff --git a/authentik/policies/models.py b/authentik/policies/models.py index 2e7d69c61..b99aeb549 100644 --- a/authentik/policies/models.py +++ b/authentik/policies/models.py @@ -190,8 +190,8 @@ class Policy(SerializerModel, CreatedUpdatedModel): verbose_name_plural = _("Policies") permissions = [ - ("view_policy_cache", "View Policy's cache metrics"), - ("clear_policy_cache", "Clear Policy's cache metrics"), + ("view_policy_cache", _("View Policy's cache metrics")), + ("clear_policy_cache", _("Clear Policy's cache metrics")), ] class PolicyMeta: diff --git a/authentik/rbac/__init__.py b/authentik/rbac/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/rbac/api/__init__.py b/authentik/rbac/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/rbac/api/rbac.py b/authentik/rbac/api/rbac.py new file mode 100644 index 000000000..3f468f483 --- /dev/null +++ b/authentik/rbac/api/rbac.py @@ -0,0 +1,130 @@ +"""common RBAC serializers""" +from django.apps import apps +from django.contrib.auth.models import Permission +from django.db.models import QuerySet +from django_filters.filters import ModelChoiceFilter +from django_filters.filterset import FilterSet +from rest_framework.exceptions import ValidationError +from rest_framework.fields import ( + CharField, + ChoiceField, + ListField, + ReadOnlyField, + SerializerMethodField, +) +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ReadOnlyModelViewSet + +from authentik.core.api.utils import PassiveSerializer +from authentik.core.models import User +from authentik.lib.validators import RequiredTogetherValidator +from authentik.policies.event_matcher.models import model_choices +from authentik.rbac.models import Role + + +class PermissionSerializer(ModelSerializer): + """Global permission""" + + app_label = ReadOnlyField(source="content_type.app_label") + app_label_verbose = SerializerMethodField() + model = ReadOnlyField(source="content_type.model") + model_verbose = SerializerMethodField() + + def get_app_label_verbose(self, instance: Permission) -> str: + """Human-readable app label""" + return apps.get_app_config(instance.content_type.app_label).verbose_name + + def get_model_verbose(self, instance: Permission) -> str: + """Human-readable model name""" + return apps.get_model( + instance.content_type.app_label, instance.content_type.model + )._meta.verbose_name + + class Meta: + model = Permission + fields = [ + "id", + "name", + "codename", + "model", + "app_label", + "app_label_verbose", + "model_verbose", + ] + + +class PermissionFilter(FilterSet): + """Filter permissions""" + + role = ModelChoiceFilter(queryset=Role.objects.all(), method="filter_role") + user = ModelChoiceFilter(queryset=User.objects.all()) + + def filter_role(self, queryset: QuerySet, name, value: Role) -> QuerySet: + """Filter permissions based on role""" + return queryset.filter(group__role=value) + + class Meta: + model = Permission + fields = [ + "codename", + "content_type__model", + "content_type__app_label", + "role", + "user", + ] + + +class RBACPermissionViewSet(ReadOnlyModelViewSet): + """Read-only list of all permissions, filterable by model and app""" + + queryset = Permission.objects.none() + serializer_class = PermissionSerializer + ordering = ["name"] + filterset_class = PermissionFilter + search_fields = [ + "codename", + "content_type__model", + "content_type__app_label", + ] + + def get_queryset(self) -> QuerySet: + return ( + Permission.objects.all() + .select_related("content_type") + .filter( + content_type__app_label__startswith="authentik", + ) + ) + + +class PermissionAssignSerializer(PassiveSerializer): + """Request to assign a new permission""" + + permissions = ListField(child=CharField()) + model = ChoiceField(choices=model_choices(), required=False) + object_pk = CharField(required=False) + + validators = [RequiredTogetherValidator(fields=["model", "object_pk"])] + + def validate(self, attrs: dict) -> dict: + model_instance = None + # Check if we're setting an object-level perm or global + model = attrs.get("model") + object_pk = attrs.get("object_pk") + if model and object_pk: + model = apps.get_model(attrs["model"]) + model_instance = model.objects.filter(pk=attrs["object_pk"]).first() + attrs["model_instance"] = model_instance + if attrs.get("model"): + return attrs + permissions = attrs.get("permissions", []) + if not all("." in perm for perm in permissions): + raise ValidationError( + { + "permissions": ( + "When assigning global permissions, codename must be given as " + "app_label.codename" + ) + } + ) + return attrs diff --git a/authentik/rbac/api/rbac_assigned_by_roles.py b/authentik/rbac/api/rbac_assigned_by_roles.py new file mode 100644 index 000000000..5dcdcab12 --- /dev/null +++ b/authentik/rbac/api/rbac_assigned_by_roles.py @@ -0,0 +1,123 @@ +"""common RBAC serializers""" +from django.db.models import Q, QuerySet +from django.db.transaction import atomic +from django_filters.filters import CharFilter, ChoiceFilter +from django_filters.filterset import FilterSet +from drf_spectacular.utils import OpenApiResponse, extend_schema +from guardian.models import GroupObjectPermission +from guardian.shortcuts import assign_perm, remove_perm +from rest_framework.decorators import action +from rest_framework.fields import CharField, ReadOnlyField +from rest_framework.mixins import ListModelMixin +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import GenericViewSet + +from authentik.api.decorators import permission_required +from authentik.core.api.utils import PassiveSerializer +from authentik.policies.event_matcher.models import model_choices +from authentik.rbac.api.rbac import PermissionAssignSerializer +from authentik.rbac.models import Role + + +class RoleObjectPermissionSerializer(ModelSerializer): + """Role-bound object level permission""" + + app_label = ReadOnlyField(source="content_type.app_label") + model = ReadOnlyField(source="content_type.model") + codename = ReadOnlyField(source="permission.codename") + name = ReadOnlyField(source="permission.name") + object_pk = ReadOnlyField() + + class Meta: + model = GroupObjectPermission + fields = ["id", "codename", "model", "app_label", "object_pk", "name"] + + +class RoleAssignedObjectPermissionSerializer(PassiveSerializer): + """Roles assigned object permission serializer""" + + role_pk = CharField(source="group.role.pk", read_only=True) + name = CharField(source="group.name", read_only=True) + permissions = RoleObjectPermissionSerializer( + many=True, source="group.groupobjectpermission_set" + ) + + class Meta: + model = Role + fields = ["role_pk", "name", "permissions"] + + +class RoleAssignedPermissionFilter(FilterSet): + """Role Assigned permission filter""" + + model = ChoiceFilter(choices=model_choices(), method="filter_model", required=True) + object_pk = CharFilter(method="filter_object_pk") + + def filter_model(self, queryset: QuerySet, name, value: str) -> QuerySet: + """Filter by object type""" + app, _, model = value.partition(".") + return queryset.filter( + Q( + group__permissions__content_type__app_label=app, + group__permissions__content_type__model=model, + ) + | Q( + group__groupobjectpermission__permission__content_type__app_label=app, + group__groupobjectpermission__permission__content_type__model=model, + ) + ).distinct() + + def filter_object_pk(self, queryset: QuerySet, name, value: str) -> QuerySet: + """Filter by object primary key""" + return queryset.filter(Q(group__groupobjectpermission__object_pk=value)).distinct() + + +class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet): + """Get assigned object permissions for a single object""" + + serializer_class = RoleAssignedObjectPermissionSerializer + ordering = ["name"] + # The filtering is done in the filterset, + # which has a required filter that does the heavy lifting + queryset = Role.objects.all() + filterset_class = RoleAssignedPermissionFilter + + @permission_required("authentik_rbac.assign_role_permissions") + @extend_schema( + request=PermissionAssignSerializer(), + responses={ + 204: OpenApiResponse(description="Successfully assigned"), + }, + ) + @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) + def assign(self, request: Request, *args, **kwargs) -> Response: + """Assign permission(s) to role. When `object_pk` is set, the permissions + are only assigned to the specific object, otherwise they are assigned globally.""" + role: Role = self.get_object() + data = PermissionAssignSerializer(data=request.data) + data.is_valid(raise_exception=True) + with atomic(): + for perm in data.validated_data["permissions"]: + assign_perm(perm, role.group, data.validated_data["model_instance"]) + return Response(status=204) + + @permission_required("authentik_rbac.unassign_role_permissions") + @extend_schema( + request=PermissionAssignSerializer(), + responses={ + 204: OpenApiResponse(description="Successfully unassigned"), + }, + ) + @action(methods=["PATCH"], detail=True, pagination_class=None, filter_backends=[]) + def unassign(self, request: Request, *args, **kwargs) -> Response: + """Unassign permission(s) to role. When `object_pk` is set, the permissions + are only assigned to the specific object, otherwise they are assigned globally.""" + role: Role = self.get_object() + data = PermissionAssignSerializer(data=request.data) + data.is_valid(raise_exception=True) + with atomic(): + for perm in data.validated_data["permissions"]: + remove_perm(perm, role.group, data.validated_data["model_instance"]) + return Response(status=204) diff --git a/authentik/rbac/api/rbac_assigned_by_users.py b/authentik/rbac/api/rbac_assigned_by_users.py new file mode 100644 index 000000000..d69b30a52 --- /dev/null +++ b/authentik/rbac/api/rbac_assigned_by_users.py @@ -0,0 +1,129 @@ +"""common RBAC serializers""" +from django.db.models import Q, QuerySet +from django.db.transaction import atomic +from django_filters.filters import CharFilter, ChoiceFilter +from django_filters.filterset import FilterSet +from drf_spectacular.utils import OpenApiResponse, extend_schema +from guardian.models import UserObjectPermission +from guardian.shortcuts import assign_perm, remove_perm +from rest_framework.decorators import action +from rest_framework.exceptions import ValidationError +from rest_framework.fields import BooleanField, ReadOnlyField +from rest_framework.mixins import ListModelMixin +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import GenericViewSet + +from authentik.api.decorators import permission_required +from authentik.core.api.groups import GroupMemberSerializer +from authentik.core.models import User, UserTypes +from authentik.policies.event_matcher.models import model_choices +from authentik.rbac.api.rbac import PermissionAssignSerializer + + +class UserObjectPermissionSerializer(ModelSerializer): + """User-bound object level permission""" + + app_label = ReadOnlyField(source="content_type.app_label") + model = ReadOnlyField(source="content_type.model") + codename = ReadOnlyField(source="permission.codename") + name = ReadOnlyField(source="permission.name") + object_pk = ReadOnlyField() + + class Meta: + model = UserObjectPermission + fields = ["id", "codename", "model", "app_label", "object_pk", "name"] + + +class UserAssignedObjectPermissionSerializer(GroupMemberSerializer): + """Users assigned object permission serializer""" + + permissions = UserObjectPermissionSerializer(many=True, source="userobjectpermission_set") + is_superuser = BooleanField() + + class Meta: + model = GroupMemberSerializer.Meta.model + fields = GroupMemberSerializer.Meta.fields + ["permissions", "is_superuser"] + + +class UserAssignedPermissionFilter(FilterSet): + """Assigned permission filter""" + + model = ChoiceFilter(choices=model_choices(), method="filter_model", required=True) + object_pk = CharFilter(method="filter_object_pk") + + def filter_model(self, queryset: QuerySet, name, value: str) -> QuerySet: + """Filter by object type""" + app, _, model = value.partition(".") + return queryset.filter( + Q( + user_permissions__content_type__app_label=app, + user_permissions__content_type__model=model, + ) + | Q( + userobjectpermission__permission__content_type__app_label=app, + userobjectpermission__permission__content_type__model=model, + ) + | Q(ak_groups__is_superuser=True) + ).distinct() + + def filter_object_pk(self, queryset: QuerySet, name, value: str) -> QuerySet: + """Filter by object primary key""" + return queryset.filter( + Q(userobjectpermission__object_pk=value) | Q(ak_groups__is_superuser=True), + ).distinct() + + +class UserAssignedPermissionViewSet(ListModelMixin, GenericViewSet): + """Get assigned object permissions for a single object""" + + serializer_class = UserAssignedObjectPermissionSerializer + ordering = ["username"] + # The filtering is done in the filterset, + # which has a required filter that does the heavy lifting + queryset = User.objects.all() + filterset_class = UserAssignedPermissionFilter + + @permission_required("authentik_core.assign_user_permissions") + @extend_schema( + request=PermissionAssignSerializer(), + responses={ + 204: OpenApiResponse(description="Successfully assigned"), + }, + ) + @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) + def assign(self, request: Request, *args, **kwargs) -> Response: + """Assign permission(s) to user""" + user: User = self.get_object() + if user.type == UserTypes.INTERNAL_SERVICE_ACCOUNT: + raise ValidationError("Permissions cannot be assigned to an internal service account.") + data = PermissionAssignSerializer(data=request.data) + data.is_valid(raise_exception=True) + with atomic(): + for perm in data.validated_data["permissions"]: + assign_perm(perm, user, data.validated_data["model_instance"]) + return Response(status=204) + + @permission_required("authentik_core.unassign_user_permissions") + @extend_schema( + request=PermissionAssignSerializer(), + responses={ + 204: OpenApiResponse(description="Successfully unassigned"), + }, + ) + @action(methods=["PATCH"], detail=True, pagination_class=None, filter_backends=[]) + def unassign(self, request: Request, *args, **kwargs) -> Response: + """Unassign permission(s) to user. When `object_pk` is set, the permissions + are only assigned to the specific object, otherwise they are assigned globally.""" + user: User = self.get_object() + if user.type == UserTypes.INTERNAL_SERVICE_ACCOUNT: + raise ValidationError( + "Permissions cannot be unassigned from an internal service account." + ) + data = PermissionAssignSerializer(data=request.data) + data.is_valid(raise_exception=True) + with atomic(): + for perm in data.validated_data["permissions"]: + remove_perm(perm, user, data.validated_data["model_instance"]) + return Response(status=204) diff --git a/authentik/rbac/api/rbac_roles.py b/authentik/rbac/api/rbac_roles.py new file mode 100644 index 000000000..162a3225b --- /dev/null +++ b/authentik/rbac/api/rbac_roles.py @@ -0,0 +1,71 @@ +"""common RBAC serializers""" +from typing import Optional + +from django.apps import apps +from django_filters.filters import UUIDFilter +from django_filters.filterset import FilterSet +from guardian.models import GroupObjectPermission +from guardian.shortcuts import get_objects_for_group +from rest_framework.fields import SerializerMethodField +from rest_framework.mixins import ListModelMixin +from rest_framework.viewsets import GenericViewSet + +from authentik.api.pagination import SmallerPagination +from authentik.rbac.api.rbac_assigned_by_roles import RoleObjectPermissionSerializer + + +class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer): + """User permission with additional object-related data""" + + app_label_verbose = SerializerMethodField() + model_verbose = SerializerMethodField() + + object_description = SerializerMethodField() + + def get_app_label_verbose(self, instance: GroupObjectPermission) -> str: + """Get app label from permission's model""" + return apps.get_app_config(instance.content_type.app_label).verbose_name + + def get_model_verbose(self, instance: GroupObjectPermission) -> str: + """Get model label from permission's model""" + return apps.get_model( + instance.content_type.app_label, instance.content_type.model + )._meta.verbose_name + + def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]: + """Get model description from attached model. This operation takes at least + one additional query, and the description is only shown if the user/role has the + view_ permission on the object""" + app_label = instance.content_type.app_label + model = instance.content_type.model + model_class = apps.get_model(app_label, model) + objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class) + obj = objects.first() + if not obj: + return None + return str(obj) + + class Meta(RoleObjectPermissionSerializer.Meta): + fields = RoleObjectPermissionSerializer.Meta.fields + [ + "app_label_verbose", + "model_verbose", + "object_description", + ] + + +class RolePermissionFilter(FilterSet): + """Role permission filter""" + + uuid = UUIDFilter("group__role__uuid", required=True) + + +class RolePermissionViewSet(ListModelMixin, GenericViewSet): + """Get a role's assigned object permissions""" + + serializer_class = ExtraRoleObjectPermissionSerializer + ordering = ["group__role__name"] + pagination_class = SmallerPagination + # The filtering is done in the filterset, + # which has a required filter that does the heavy lifting + queryset = GroupObjectPermission.objects.select_related("content_type", "group__role").all() + filterset_class = RolePermissionFilter diff --git a/authentik/rbac/api/rbac_users.py b/authentik/rbac/api/rbac_users.py new file mode 100644 index 000000000..04f3fcabd --- /dev/null +++ b/authentik/rbac/api/rbac_users.py @@ -0,0 +1,71 @@ +"""common RBAC serializers""" +from typing import Optional + +from django.apps import apps +from django_filters.filters import NumberFilter +from django_filters.filterset import FilterSet +from guardian.models import UserObjectPermission +from guardian.shortcuts import get_objects_for_user +from rest_framework.fields import SerializerMethodField +from rest_framework.mixins import ListModelMixin +from rest_framework.viewsets import GenericViewSet + +from authentik.api.pagination import SmallerPagination +from authentik.rbac.api.rbac_assigned_by_users import UserObjectPermissionSerializer + + +class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer): + """User permission with additional object-related data""" + + app_label_verbose = SerializerMethodField() + model_verbose = SerializerMethodField() + + object_description = SerializerMethodField() + + def get_app_label_verbose(self, instance: UserObjectPermission) -> str: + """Get app label from permission's model""" + return apps.get_app_config(instance.content_type.app_label).verbose_name + + def get_model_verbose(self, instance: UserObjectPermission) -> str: + """Get model label from permission's model""" + return apps.get_model( + instance.content_type.app_label, instance.content_type.model + )._meta.verbose_name + + def get_object_description(self, instance: UserObjectPermission) -> Optional[str]: + """Get model description from attached model. This operation takes at least + one additional query, and the description is only shown if the user/role has the + view_ permission on the object""" + app_label = instance.content_type.app_label + model = instance.content_type.model + model_class = apps.get_model(app_label, model) + objects = get_objects_for_user(instance.user, f"{app_label}.view_{model}", model_class) + obj = objects.first() + if not obj: + return None + return str(obj) + + class Meta(UserObjectPermissionSerializer.Meta): + fields = UserObjectPermissionSerializer.Meta.fields + [ + "app_label_verbose", + "model_verbose", + "object_description", + ] + + +class UserPermissionFilter(FilterSet): + """User-assigned permission filter""" + + user_id = NumberFilter("user__id", required=True) + + +class UserPermissionViewSet(ListModelMixin, GenericViewSet): + """Get a users's assigned object permissions""" + + serializer_class = ExtraUserObjectPermissionSerializer + ordering = ["user__username"] + pagination_class = SmallerPagination + # The filtering is done in the filterset, + # which has a required filter that does the heavy lifting + queryset = UserObjectPermission.objects.select_related("content_type", "user").all() + filterset_class = UserPermissionFilter diff --git a/authentik/rbac/api/roles.py b/authentik/rbac/api/roles.py new file mode 100644 index 000000000..36eef8a19 --- /dev/null +++ b/authentik/rbac/api/roles.py @@ -0,0 +1,24 @@ +"""RBAC Roles""" +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet + +from authentik.core.api.used_by import UsedByMixin +from authentik.rbac.models import Role + + +class RoleSerializer(ModelSerializer): + """Role serializer""" + + class Meta: + model = Role + fields = ["pk", "name"] + + +class RoleViewSet(UsedByMixin, ModelViewSet): + """Role viewset""" + + serializer_class = RoleSerializer + queryset = Role.objects.all() + search_fields = ["group__name"] + ordering = ["group__name"] + filterset_fields = ["group__name"] diff --git a/authentik/rbac/apps.py b/authentik/rbac/apps.py new file mode 100644 index 000000000..f6b878c01 --- /dev/null +++ b/authentik/rbac/apps.py @@ -0,0 +1,15 @@ +"""authentik rbac app config""" +from authentik.blueprints.apps import ManagedAppConfig + + +class AuthentikRBACConfig(ManagedAppConfig): + """authentik rbac app config""" + + name = "authentik.rbac" + label = "authentik_rbac" + verbose_name = "authentik RBAC" + default = True + + def reconcile_load_rbac_signals(self): + """Load rbac signals""" + self.import_module("authentik.rbac.signals") diff --git a/authentik/rbac/filters.py b/authentik/rbac/filters.py new file mode 100644 index 000000000..395efd673 --- /dev/null +++ b/authentik/rbac/filters.py @@ -0,0 +1,26 @@ +"""RBAC API Filter""" +from django.db.models import QuerySet +from rest_framework.exceptions import PermissionDenied +from rest_framework.request import Request +from rest_framework_guardian.filters import ObjectPermissionsFilter + + +class ObjectFilter(ObjectPermissionsFilter): + """Object permission filter that grants global permission higher priority than + per-object permissions""" + + def filter_queryset(self, request: Request, queryset: QuerySet, view) -> QuerySet: + permission = self.perm_format % { + "app_label": queryset.model._meta.app_label, + "model_name": queryset.model._meta.model_name, + } + # having the global permission set on a user has higher priority than + # per-object permissions + if request.user.has_perm(permission): + return queryset + queryset = super().filter_queryset(request, queryset, view) + if not queryset.exists(): + # User doesn't have direct permission to all objects + # and also no object permissions assigned (directly or via role) + raise PermissionDenied() + return queryset diff --git a/authentik/rbac/migrations/0001_initial.py b/authentik/rbac/migrations/0001_initial.py new file mode 100644 index 000000000..85cabbf11 --- /dev/null +++ b/authentik/rbac/migrations/0001_initial.py @@ -0,0 +1,47 @@ +# Generated by Django 4.2.6 on 2023-10-11 13:37 + +import uuid + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + ] + + operations = [ + migrations.CreateModel( + name="Role", + fields=[ + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ("name", models.TextField(max_length=150, unique=True)), + ( + "group", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="auth.group" + ), + ), + ], + options={ + "verbose_name": "Role", + "verbose_name_plural": "Roles", + "permissions": [ + ("assign_role_permissions", "Can assign permissions to users"), + ("unassign_role_permissions", "Can unassign permissions from users"), + ], + }, + ), + ] diff --git a/authentik/rbac/migrations/0002_systempermission.py b/authentik/rbac/migrations/0002_systempermission.py new file mode 100644 index 000000000..eb4a03bb4 --- /dev/null +++ b/authentik/rbac/migrations/0002_systempermission.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.6 on 2023-10-12 15:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_rbac", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="SystemPermission", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ], + options={ + "permissions": [ + ("view_system_info", "Can view system info"), + ("view_system_tasks", "Can view system tasks"), + ("run_system_tasks", "Can run system tasks"), + ("access_admin_interface", "Can access admin interface"), + ], + "managed": False, + "default_permissions": (), + }, + ), + ] diff --git a/authentik/rbac/migrations/__init__.py b/authentik/rbac/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/rbac/models.py b/authentik/rbac/models.py new file mode 100644 index 000000000..fe6096f7d --- /dev/null +++ b/authentik/rbac/models.py @@ -0,0 +1,73 @@ +"""RBAC models""" +from typing import Optional +from uuid import uuid4 + +from django.db import models +from django.db.transaction import atomic +from django.utils.translation import gettext_lazy as _ +from guardian.shortcuts import assign_perm +from rest_framework.serializers import BaseSerializer + +from authentik.lib.models import SerializerModel + + +class Role(SerializerModel): + """RBAC role, which can have different permissions (both global and per-object) attached + to it.""" + + uuid = models.UUIDField(default=uuid4, editable=False, unique=True, primary_key=True) + # Due to the way django and django-guardian work, this is somewhat of a hack. + # Django and django-guardian allow for setting permissions on users and groups, but they + # only allow for a custom user object, not a custom group object, which is why + # we have both authentik and django groups. With this model, we use the inbuilt group system + # for RBAC. This means that every Role needs a single django group that its assigned to + # which will hold all of the actual permissions + # The main advantage of that is that all the permission checking just works out of the box, + # as these permissions are checked by default by django and most other libraries that build + # on top of django + group = models.OneToOneField("auth.Group", on_delete=models.CASCADE) + + # name field has the same constraints as the group model + name = models.TextField(max_length=150, unique=True) + + def assign_permission(self, *perms: str, obj: Optional[models.Model] = None): + """Assign permission to role, can handle multiple permissions, + but when assigning multiple permissions to an object the permissions + must all belong to the object given""" + with atomic(): + for perm in perms: + assign_perm(perm, self.group, obj) + + @property + def serializer(self) -> type[BaseSerializer]: + from authentik.rbac.api.roles import RoleSerializer + + return RoleSerializer + + def __str__(self) -> str: + return f"Role {self.name}" + + class Meta: + verbose_name = _("Role") + verbose_name_plural = _("Roles") + permissions = [ + ("assign_role_permissions", _("Can assign permissions to users")), + ("unassign_role_permissions", _("Can unassign permissions from users")), + ] + + +class SystemPermission(models.Model): + """System-wide permissions that are not related to any direct + database model""" + + class Meta: + managed = False + default_permissions = () + verbose_name = _("System permission") + verbose_name_plural = _("System permissions") + permissions = [ + ("view_system_info", _("Can view system info")), + ("view_system_tasks", _("Can view system tasks")), + ("run_system_tasks", _("Can run system tasks")), + ("access_admin_interface", _("Can access admin interface")), + ] diff --git a/authentik/rbac/permissions.py b/authentik/rbac/permissions.py new file mode 100644 index 000000000..011a02715 --- /dev/null +++ b/authentik/rbac/permissions.py @@ -0,0 +1,30 @@ +"""RBAC Permissions""" +from django.db.models import Model +from rest_framework.permissions import BasePermission, DjangoObjectPermissions +from rest_framework.request import Request + + +class ObjectPermissions(DjangoObjectPermissions): + """RBAC Permissions""" + + def has_object_permission(self, request: Request, view, obj: Model): + queryset = self._queryset(view) + model_cls = queryset.model + perms = self.get_required_object_permissions(request.method, model_cls) + # Rank global permissions higher than per-object permissions + if request.user.has_perms(perms): + return True + return super().has_object_permission(request, view, obj) + + +# pylint: disable=invalid-name +def HasPermission(*perm: str) -> type[BasePermission]: + """Permission checker for any non-object permissions, returns + a BasePermission class that can be used with rest_framework""" + + # pylint: disable=missing-class-docstring, invalid-name + class checker(BasePermission): + def has_permission(self, request: Request, view): + return bool(request.user and request.user.has_perms(perm)) + + return checker diff --git a/authentik/rbac/signals.py b/authentik/rbac/signals.py new file mode 100644 index 000000000..f3bbbc036 --- /dev/null +++ b/authentik/rbac/signals.py @@ -0,0 +1,67 @@ +"""rbac signals""" +from django.contrib.auth.models import Group as DjangoGroup +from django.db.models.signals import m2m_changed, pre_save +from django.db.transaction import atomic +from django.dispatch import receiver +from structlog.stdlib import get_logger + +from authentik.core.models import Group +from authentik.rbac.models import Role + +LOGGER = get_logger() + + +@receiver(pre_save, sender=Role) +def rbac_role_pre_save(sender: type[Role], instance: Role, **_): + """Ensure role has a group object created for it""" + if hasattr(instance, "group"): + return + group, _ = DjangoGroup.objects.get_or_create(name=instance.name) + instance.group = group + + +@receiver(m2m_changed, sender=Group.roles.through) +def rbac_group_role_m2m(sender: type[Group], action: str, instance: Group, reverse: bool, **_): + """RBAC: Sync group members into roles when roles are assigned""" + if action not in ["post_add", "post_remove", "post_clear"]: + return + with atomic(): + group_users = list( + instance.children_recursive() + .exclude(users__isnull=True) + .values_list("users", flat=True) + ) + if not group_users: + return + for role in instance.roles.all(): + role: Role + role.group.user_set.set(group_users) + LOGGER.debug("Updated users in group", group=instance) + + +# pylint: disable=no-member +@receiver(m2m_changed, sender=Group.users.through) +def rbac_group_users_m2m( + sender: type[Group], action: str, instance: Group, pk_set: set, reverse: bool, **_ +): + """Handle Group/User m2m and mirror it to roles""" + if action not in ["post_add", "post_remove"]: + return + # reverse: instance is a Group, pk_set is a list of user pks + # non-reverse: instance is a User, pk_set is a list of groups + with atomic(): + if reverse: + for role in instance.roles.all(): + role: Role + if action == "post_add": + role.group.user_set.add(*pk_set) + elif action == "post_remove": + role.group.user_set.remove(*pk_set) + else: + for group in Group.objects.filter(pk__in=pk_set): + for role in group.roles.all(): + role: Role + if action == "post_add": + role.group.user_set.add(instance) + elif action == "post_remove": + role.group.user_set.remove(instance) diff --git a/authentik/rbac/tests/__init__.py b/authentik/rbac/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/rbac/tests/test_api_assigned_by_roles.py b/authentik/rbac/tests/test_api_assigned_by_roles.py new file mode 100644 index 000000000..07032e805 --- /dev/null +++ b/authentik/rbac/tests/test_api_assigned_by_roles.py @@ -0,0 +1,151 @@ +"""Test RoleAssignedPermissionViewSet api""" +from django.urls import reverse +from rest_framework.test import APITestCase + +from authentik.core.models import Group +from authentik.core.tests.utils import create_test_admin_user, create_test_user +from authentik.lib.generators import generate_id +from authentik.rbac.api.rbac_assigned_by_roles import RoleAssignedObjectPermissionSerializer +from authentik.rbac.models import Role +from authentik.stages.invitation.models import Invitation + + +class TestRBACRoleAPI(APITestCase): + """Test RoleAssignedPermissionViewSet api""" + + def setUp(self) -> None: + self.superuser = create_test_admin_user() + + self.user = create_test_user() + self.role = Role.objects.create(name=generate_id()) + self.group = Group.objects.create(name=generate_id()) + self.group.roles.add(self.role) + self.group.users.add(self.user) + + def test_filter_assigned(self): + """Test RoleAssignedPermissionViewSet's filters""" + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv) + # self.user doesn't have permissions to see their (object) permissions + self.client.force_login(self.superuser) + res = self.client.get( + reverse("authentik_api:permissions-assigned-by-roles-list"), + { + "model": "authentik_stages_invitation.invitation", + "object_pk": str(inv.pk), + "ordering": "pk", + }, + ) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 1, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 1, + }, + "results": [ + RoleAssignedObjectPermissionSerializer(instance=self.role).data, + ], + }, + ) + + def test_assign_global(self): + """Test permission assign""" + self.client.force_login(self.superuser) + res = self.client.post( + reverse( + "authentik_api:permissions-assigned-by-roles-assign", + kwargs={ + "pk": self.role.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + }, + ) + self.assertEqual(res.status_code, 204) + self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation")) + + def test_assign_object(self): + """Test permission assign (object)""" + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + self.client.force_login(self.superuser) + res = self.client.post( + reverse( + "authentik_api:permissions-assigned-by-roles-assign", + kwargs={ + "pk": self.role.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + "model": "authentik_stages_invitation.invitation", + "object_pk": str(inv.pk), + }, + ) + self.assertEqual(res.status_code, 204) + self.assertTrue( + self.user.has_perm( + "authentik_stages_invitation.view_invitation", + inv, + ) + ) + + def test_unassign_global(self): + """Test permission unassign""" + self.role.assign_permission("authentik_stages_invitation.view_invitation") + self.client.force_login(self.superuser) + res = self.client.patch( + reverse( + "authentik_api:permissions-assigned-by-roles-unassign", + kwargs={ + "pk": str(self.role.pk), + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + }, + ) + self.assertEqual(res.status_code, 204) + self.assertFalse(self.user.has_perm("authentik_stages_invitation.view_invitation")) + + def test_unassign_object(self): + """Test permission unassign (object)""" + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv) + self.client.force_login(self.superuser) + res = self.client.patch( + reverse( + "authentik_api:permissions-assigned-by-roles-unassign", + kwargs={ + "pk": str(self.role.pk), + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + "model": "authentik_stages_invitation.invitation", + "object_pk": str(inv.pk), + }, + ) + self.assertEqual(res.status_code, 204) + self.assertFalse( + self.user.has_perm( + "authentik_stages_invitation.view_invitation", + inv, + ) + ) diff --git a/authentik/rbac/tests/test_api_assigned_by_users.py b/authentik/rbac/tests/test_api_assigned_by_users.py new file mode 100644 index 000000000..fa1238495 --- /dev/null +++ b/authentik/rbac/tests/test_api_assigned_by_users.py @@ -0,0 +1,196 @@ +"""Test UserAssignedPermissionViewSet api""" +from django.urls import reverse +from guardian.shortcuts import assign_perm +from rest_framework.test import APITestCase + +from authentik.core.models import Group, UserTypes +from authentik.core.tests.utils import create_test_admin_user, create_test_user +from authentik.lib.generators import generate_id +from authentik.rbac.api.rbac_assigned_by_users import UserAssignedObjectPermissionSerializer +from authentik.rbac.models import Role +from authentik.stages.invitation.models import Invitation + + +class TestRBACUserAPI(APITestCase): + """Test UserAssignedPermissionViewSet api""" + + def setUp(self) -> None: + self.superuser = create_test_admin_user() + + self.user = create_test_user() + self.role = Role.objects.create(name=generate_id()) + self.group = Group.objects.create(name=generate_id()) + self.group.roles.add(self.role) + self.group.users.add(self.user) + + def test_filter_assigned(self): + """Test UserAssignedPermissionViewSet's filters""" + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + assign_perm("authentik_stages_invitation.view_invitation", self.user, inv) + # self.user doesn't have permissions to see their (object) permissions + self.client.force_login(self.superuser) + res = self.client.get( + reverse("authentik_api:permissions-assigned-by-users-list"), + { + "model": "authentik_stages_invitation.invitation", + "object_pk": str(inv.pk), + "ordering": "pk", + }, + ) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 2, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 2, + }, + "results": sorted( + [ + UserAssignedObjectPermissionSerializer(instance=self.user).data, + UserAssignedObjectPermissionSerializer(instance=self.superuser).data, + ], + key=lambda u: u["pk"], + ), + }, + ) + + def test_assign_global(self): + """Test permission assign""" + self.client.force_login(self.superuser) + res = self.client.post( + reverse( + "authentik_api:permissions-assigned-by-users-assign", + kwargs={ + "pk": self.user.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + }, + ) + self.assertEqual(res.status_code, 204) + self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation")) + + def test_assign_global_internal_sa(self): + """Test permission assign (to internal service account)""" + self.client.force_login(self.superuser) + self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT + self.user.save() + res = self.client.post( + reverse( + "authentik_api:permissions-assigned-by-users-assign", + kwargs={ + "pk": self.user.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + }, + ) + self.assertEqual(res.status_code, 400) + self.assertFalse(self.user.has_perm("authentik_stages_invitation.view_invitation")) + + def test_assign_object(self): + """Test permission assign (object)""" + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + self.client.force_login(self.superuser) + res = self.client.post( + reverse( + "authentik_api:permissions-assigned-by-users-assign", + kwargs={ + "pk": self.user.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + "model": "authentik_stages_invitation.invitation", + "object_pk": str(inv.pk), + }, + ) + self.assertEqual(res.status_code, 204) + self.assertTrue( + self.user.has_perm( + "authentik_stages_invitation.view_invitation", + inv, + ) + ) + + def test_unassign_global(self): + """Test permission unassign""" + assign_perm("authentik_stages_invitation.view_invitation", self.user) + self.client.force_login(self.superuser) + res = self.client.patch( + reverse( + "authentik_api:permissions-assigned-by-users-unassign", + kwargs={ + "pk": self.user.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + }, + ) + self.assertEqual(res.status_code, 204) + self.assertFalse(self.user.has_perm("authentik_stages_invitation.view_invitation")) + + def test_unassign_global_internal_sa(self): + """Test permission unassign (from internal service account)""" + self.client.force_login(self.superuser) + self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT + self.user.save() + assign_perm("authentik_stages_invitation.view_invitation", self.user) + self.client.force_login(self.superuser) + res = self.client.patch( + reverse( + "authentik_api:permissions-assigned-by-users-unassign", + kwargs={ + "pk": self.user.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + }, + ) + self.assertEqual(res.status_code, 400) + self.assertTrue(self.user.has_perm("authentik_stages_invitation.view_invitation")) + + def test_unassign_object(self): + """Test permission unassign (object)""" + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + assign_perm("authentik_stages_invitation.view_invitation", self.user, inv) + self.client.force_login(self.superuser) + res = self.client.patch( + reverse( + "authentik_api:permissions-assigned-by-users-unassign", + kwargs={ + "pk": self.user.pk, + }, + ), + { + "permissions": ["authentik_stages_invitation.view_invitation"], + "model": "authentik_stages_invitation.invitation", + "object_pk": str(inv.pk), + }, + ) + self.assertEqual(res.status_code, 204) + self.assertFalse( + self.user.has_perm( + "authentik_stages_invitation.view_invitation", + inv, + ) + ) diff --git a/authentik/rbac/tests/test_api_filters.py b/authentik/rbac/tests/test_api_filters.py new file mode 100644 index 000000000..91bd707d7 --- /dev/null +++ b/authentik/rbac/tests/test_api_filters.py @@ -0,0 +1,122 @@ +"""RBAC role tests""" +from django.urls import reverse +from rest_framework.test import APITestCase + +from authentik.core.models import Group +from authentik.core.tests.utils import create_test_admin_user, create_test_user +from authentik.lib.generators import generate_id +from authentik.rbac.models import Role +from authentik.stages.invitation.api import InvitationSerializer +from authentik.stages.invitation.models import Invitation + + +class TestAPIPerms(APITestCase): + """Test API Permission and filtering""" + + def setUp(self) -> None: + self.superuser = create_test_admin_user() + + self.user = create_test_user() + self.role = Role.objects.create(name=generate_id()) + self.group = Group.objects.create(name=generate_id()) + self.group.roles.add(self.role) + self.group.users.add(self.user) + + def test_list_simple(self): + """Test list (single object, role has global permission)""" + self.client.force_login(self.user) + self.role.assign_permission("authentik_stages_invitation.view_invitation") + + Invitation.objects.all().delete() + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + res = self.client.get(reverse("authentik_api:invitation-list")) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 1, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 1, + }, + "results": [ + InvitationSerializer(instance=inv).data, + ], + }, + ) + + def test_list_object_perm(self): + """Test list""" + self.client.force_login(self.user) + + Invitation.objects.all().delete() + Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + inv2 = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv2) + + res = self.client.get(reverse("authentik_api:invitation-list")) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 1, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 1, + }, + "results": [ + InvitationSerializer(instance=inv2).data, + ], + }, + ) + + def test_list_denied(self): + """Test list without adding permission""" + self.client.force_login(self.user) + + res = self.client.get(reverse("authentik_api:invitation-list")) + self.assertEqual(res.status_code, 403) + self.assertJSONEqual( + res.content.decode(), + {"detail": "You do not have permission to perform this action."}, + ) + + def test_create_simple(self): + """Test create with permission""" + self.client.force_login(self.user) + self.role.assign_permission("authentik_stages_invitation.add_invitation") + res = self.client.post( + reverse("authentik_api:invitation-list"), + data={ + "name": generate_id(), + }, + ) + self.assertEqual(res.status_code, 201) + + def test_create_simple_denied(self): + """Test create without assigning permission""" + self.client.force_login(self.user) + res = self.client.post( + reverse("authentik_api:invitation-list"), + data={ + "name": generate_id(), + }, + ) + self.assertEqual(res.status_code, 403) diff --git a/authentik/rbac/tests/test_roles.py b/authentik/rbac/tests/test_roles.py new file mode 100644 index 000000000..f9cbfdabb --- /dev/null +++ b/authentik/rbac/tests/test_roles.py @@ -0,0 +1,35 @@ +"""RBAC role tests""" +from rest_framework.test import APITestCase + +from authentik.core.models import Group +from authentik.core.tests.utils import create_test_admin_user +from authentik.lib.generators import generate_id +from authentik.rbac.models import Role + + +class TestRoles(APITestCase): + """Test roles""" + + def test_role_create(self): + """Test creation""" + user = create_test_admin_user() + group = Group.objects.create(name=generate_id()) + role = Role.objects.create(name=generate_id()) + role.assign_permission("authentik_core.view_application") + group.roles.add(role) + group.users.add(user) + self.assertEqual(list(role.group.user_set.all()), [user]) + self.assertTrue(user.has_perm("authentik_core.view_application")) + + def test_role_create_remove(self): + """Test creation and remove""" + user = create_test_admin_user() + group = Group.objects.create(name=generate_id()) + role = Role.objects.create(name=generate_id()) + role.assign_permission("authentik_core.view_application") + group.roles.add(role) + group.users.add(user) + self.assertEqual(list(role.group.user_set.all()), [user]) + self.assertTrue(user.has_perm("authentik_core.view_application")) + user.delete() + self.assertEqual(list(role.group.user_set.all()), []) diff --git a/authentik/rbac/urls.py b/authentik/rbac/urls.py new file mode 100644 index 000000000..586264a50 --- /dev/null +++ b/authentik/rbac/urls.py @@ -0,0 +1,24 @@ +"""RBAC API urls""" +from authentik.rbac.api.rbac import RBACPermissionViewSet +from authentik.rbac.api.rbac_assigned_by_roles import RoleAssignedPermissionViewSet +from authentik.rbac.api.rbac_assigned_by_users import UserAssignedPermissionViewSet +from authentik.rbac.api.rbac_roles import RolePermissionViewSet +from authentik.rbac.api.rbac_users import UserPermissionViewSet +from authentik.rbac.api.roles import RoleViewSet + +api_urlpatterns = [ + ( + "rbac/permissions/assigned_by_users", + UserAssignedPermissionViewSet, + "permissions-assigned-by-users", + ), + ( + "rbac/permissions/assigned_by_roles", + RoleAssignedPermissionViewSet, + "permissions-assigned-by-roles", + ), + ("rbac/permissions/users", UserPermissionViewSet, "permissions-users"), + ("rbac/permissions/roles", RolePermissionViewSet, "permissions-roles"), + ("rbac/permissions", RBACPermissionViewSet), + ("rbac/roles", RoleViewSet), +] diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 3cb081862..ee31f2cc6 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -77,6 +77,7 @@ INSTALLED_APPS = [ "authentik.providers.radius", "authentik.providers.saml", "authentik.providers.scim", + "authentik.rbac", "authentik.recovery", "authentik.sources.ldap", "authentik.sources.oauth", @@ -156,7 +157,7 @@ REST_FRAMEWORK = { "DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination", "PAGE_SIZE": 100, "DEFAULT_FILTER_BACKENDS": [ - "rest_framework_guardian.filters.ObjectPermissionsFilter", + "authentik.rbac.filters.ObjectFilter", "django_filters.rest_framework.DjangoFilterBackend", "rest_framework.filters.OrderingFilter", "rest_framework.filters.SearchFilter", @@ -164,7 +165,7 @@ REST_FRAMEWORK = { "DEFAULT_PARSER_CLASSES": [ "rest_framework.parsers.JSONParser", ], - "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.DjangoObjectPermissions",), + "DEFAULT_PERMISSION_CLASSES": ("authentik.rbac.permissions.ObjectPermissions",), "DEFAULT_AUTHENTICATION_CLASSES": ( "authentik.api.authentication.TokenAuthentication", "rest_framework.authentication.SessionAuthentication", @@ -410,6 +411,9 @@ if DEBUG: INSTALLED_APPS.append("silk") SILKY_PYTHON_PROFILER = True MIDDLEWARE = ["silk.middleware.SilkyMiddleware"] + MIDDLEWARE + REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append( + "rest_framework.renderers.BrowsableAPIRenderer" + ) INSTALLED_APPS.append("authentik.core") diff --git a/authentik/stages/authenticator_static/migrations/0009_throttling.py b/authentik/stages/authenticator_static/migrations/0009_throttling.py index 17690de2e..1883f8836 100644 --- a/authentik/stages/authenticator_static/migrations/0009_throttling.py +++ b/authentik/stages/authenticator_static/migrations/0009_throttling.py @@ -30,4 +30,12 @@ class Migration(migrations.Migration): name="staticdevice", options={"verbose_name": "Static device", "verbose_name_plural": "Static devices"}, ), + migrations.AlterModelOptions( + name="staticdevice", + options={"verbose_name": "Static Device", "verbose_name_plural": "Static Devices"}, + ), + migrations.AlterModelOptions( + name="statictoken", + options={"verbose_name": "Static Token", "verbose_name_plural": "Static Tokens"}, + ), ] diff --git a/authentik/stages/authenticator_static/models.py b/authentik/stages/authenticator_static/models.py index ac8b55b08..7ce345159 100644 --- a/authentik/stages/authenticator_static/models.py +++ b/authentik/stages/authenticator_static/models.py @@ -95,8 +95,8 @@ class StaticDevice(SerializerModel, ThrottlingMixin, Device): return match is not None class Meta(Device.Meta): - verbose_name = _("Static device") - verbose_name_plural = _("Static devices") + verbose_name = _("Static Device") + verbose_name_plural = _("Static Devices") class StaticToken(models.Model): @@ -124,3 +124,7 @@ class StaticToken(models.Model): """ return b32encode(urandom(5)).decode("utf-8").lower() + + class Meta: + verbose_name = _("Static Token") + verbose_name_plural = _("Static Tokens") diff --git a/authentik/stages/authenticator_totp/migrations/0010_alter_totpdevice_key.py b/authentik/stages/authenticator_totp/migrations/0010_alter_totpdevice_key.py index 436eaa38a..af007e4df 100644 --- a/authentik/stages/authenticator_totp/migrations/0010_alter_totpdevice_key.py +++ b/authentik/stages/authenticator_totp/migrations/0010_alter_totpdevice_key.py @@ -25,4 +25,8 @@ class Migration(migrations.Migration): name="totpdevice", options={"verbose_name": "TOTP device", "verbose_name_plural": "TOTP devices"}, ), + migrations.AlterModelOptions( + name="totpdevice", + options={"verbose_name": "TOTP Device", "verbose_name_plural": "TOTP Devices"}, + ), ] diff --git a/authentik/stages/authenticator_totp/models.py b/authentik/stages/authenticator_totp/models.py index 6828a8e2e..41bf2d2c8 100644 --- a/authentik/stages/authenticator_totp/models.py +++ b/authentik/stages/authenticator_totp/models.py @@ -241,5 +241,5 @@ class TOTPDevice(SerializerModel, ThrottlingMixin, Device): return None class Meta(Device.Meta): - verbose_name = _("TOTP device") - verbose_name_plural = _("TOTP devices") + verbose_name = _("TOTP Device") + verbose_name_plural = _("TOTP Devices") diff --git a/authentik/stages/consent/stage.py b/authentik/stages/consent/stage.py index d8c42724f..61677559c 100644 --- a/authentik/stages/consent/stage.py +++ b/authentik/stages/consent/stage.py @@ -6,11 +6,11 @@ from django.http import HttpRequest, HttpResponse from django.utils.timezone import now from rest_framework.fields import CharField +from authentik.core.api.utils import PassiveSerializer from authentik.flows.challenge import ( Challenge, ChallengeResponse, ChallengeTypes, - PermissionSerializer, WithUserInfoChallenge, ) from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_PENDING_USER @@ -25,12 +25,19 @@ PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS = "consent_additional_permissions" SESSION_KEY_CONSENT_TOKEN = "authentik/stages/consent/token" # nosec +class ConsentPermissionSerializer(PassiveSerializer): + """Permission used for consent""" + + name = CharField(allow_blank=True) + id = CharField() + + class ConsentChallenge(WithUserInfoChallenge): """Challenge info for consent screens""" header_text = CharField(required=False) - permissions = PermissionSerializer(many=True) - additional_permissions = PermissionSerializer(many=True) + permissions = ConsentPermissionSerializer(many=True) + additional_permissions = ConsentPermissionSerializer(many=True) component = CharField(default="ak-stage-consent") token = CharField(required=True) diff --git a/authentik/stages/prompt/api.py b/authentik/stages/prompt/api.py index 255c97b4b..7d484f598 100644 --- a/authentik/stages/prompt/api.py +++ b/authentik/stages/prompt/api.py @@ -71,6 +71,7 @@ class PromptViewSet(UsedByMixin, ModelViewSet): queryset = Prompt.objects.all().prefetch_related("promptstage_set") serializer_class = PromptSerializer + ordering = ["field_key"] filterset_fields = ["field_key", "name", "label", "type", "placeholder"] search_fields = ["field_key", "name", "label", "type", "placeholder"] diff --git a/blueprints/schema.json b/blueprints/schema.json index 9dc77b796..57f624087 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -1188,6 +1188,43 @@ } } }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_rbac.role" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "attrs": { + "$ref": "#/$defs/model_authentik_rbac.role" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_rbac.role" + } + } + }, { "type": "object", "required": [ @@ -2705,6 +2742,43 @@ } } }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_enterprise.license" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "attrs": { + "$ref": "#/$defs/model_authentik_enterprise.license" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_enterprise.license" + } + } + }, { "type": "object", "required": [ @@ -3372,6 +3446,7 @@ "authentik.providers.radius", "authentik.providers.saml", "authentik.providers.scim", + "authentik.rbac", "authentik.recovery", "authentik.sources.ldap", "authentik.sources.oauth", @@ -3443,6 +3518,7 @@ "authentik_providers_saml.samlpropertymapping", "authentik_providers_scim.scimprovider", "authentik_providers_scim.scimmapping", + "authentik_rbac.role", "authentik_sources_ldap.ldapsource", "authentik_sources_ldap.ldappropertymapping", "authentik_sources_oauth.oauthsource", @@ -3483,7 +3559,8 @@ "authentik_core.group", "authentik_core.user", "authentik_core.application", - "authentik_core.token" + "authentik_core.token", + "authentik_enterprise.license" ], "title": "Model", "description": "Match events created by selected model. When left empty, all models are matched. When an app is selected, all the application's models are matched." @@ -4944,6 +5021,18 @@ }, "required": [] }, + "model_authentik_rbac.role": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 150, + "minLength": 1, + "title": "Name" + } + }, + "required": [] + }, "model_authentik_sources_ldap.ldapsource": { "type": "object", "properties": { @@ -8405,6 +8494,13 @@ "type": "object", "additionalProperties": true, "title": "Attributes" + }, + "roles": { + "type": "array", + "items": { + "type": "integer" + }, + "title": "Roles" } }, "required": [] @@ -8599,6 +8695,17 @@ }, "required": [] }, + "model_authentik_enterprise.license": { + "type": "object", + "properties": { + "key": { + "type": "string", + "minLength": 1, + "title": "Key" + } + }, + "required": [] + }, "model_authentik_blueprints.metaapplyblueprint": { "type": "object", "properties": { diff --git a/internal/outpost/ldap/search/memory/memory.go b/internal/outpost/ldap/search/memory/memory.go index d877b76e5..177099f7e 100644 --- a/internal/outpost/ldap/search/memory/memory.go +++ b/internal/outpost/ldap/search/memory/memory.go @@ -162,7 +162,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult, for _, u := range g.UsersObj { if flag.UserPk == u.Pk { //TODO: Is there a better way to clone this object? - fg := api.NewGroup(g.Pk, g.NumPk, g.Name, g.ParentName, []api.GroupMember{u}) + fg := api.NewGroup(g.Pk, g.NumPk, g.Name, g.ParentName, []api.GroupMember{u}, []api.Role{}) fg.SetUsers([]int32{flag.UserPk}) if g.Parent.IsSet() { fg.SetParent(*g.Parent.Get()) diff --git a/schema.yml b/schema.yml index 03eed6d01..219633aa2 100644 --- a/schema.yml +++ b/schema.yml @@ -885,7 +885,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -918,7 +918,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -957,7 +957,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -995,7 +995,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -1113,7 +1113,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -1146,7 +1146,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -1185,7 +1185,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -1223,7 +1223,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -2030,7 +2030,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -2063,7 +2063,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -2102,7 +2102,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -2140,7 +2140,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -2170,7 +2170,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this Static device. + description: A unique integer value identifying this Static Device. required: true tags: - authenticators @@ -2262,7 +2262,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -2295,7 +2295,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -2334,7 +2334,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -2372,7 +2372,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -2402,7 +2402,7 @@ paths: name: id schema: type: integer - description: A unique integer value identifying this TOTP device. + description: A unique integer value identifying this TOTP Device. required: true tags: - authenticators @@ -3379,7 +3379,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -3413,7 +3413,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -3453,7 +3453,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -3492,7 +3492,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -3523,7 +3523,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -3562,7 +3562,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -3601,7 +3601,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this group. + description: A UUID string identifying this Group. required: true tags: - core @@ -5653,7 +5653,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this license. + description: A UUID string identifying this License. required: true tags: - enterprise @@ -5687,7 +5687,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this license. + description: A UUID string identifying this License. required: true tags: - enterprise @@ -5727,7 +5727,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this license. + description: A UUID string identifying this License. required: true tags: - enterprise @@ -5766,7 +5766,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this license. + description: A UUID string identifying this License. required: true tags: - enterprise @@ -5797,7 +5797,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this license. + description: A UUID string identifying this License. required: true tags: - enterprise @@ -9120,7 +9120,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this outpost. + description: A UUID string identifying this Outpost. required: true tags: - outposts @@ -9154,7 +9154,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this outpost. + description: A UUID string identifying this Outpost. required: true tags: - outposts @@ -9194,7 +9194,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this outpost. + description: A UUID string identifying this Outpost. required: true tags: - outposts @@ -9233,7 +9233,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this outpost. + description: A UUID string identifying this Outpost. required: true tags: - outposts @@ -9312,7 +9312,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this outpost. + description: A UUID string identifying this Outpost. required: true tags: - outposts @@ -9349,7 +9349,7 @@ paths: schema: type: string format: uuid - description: A UUID string identifying this outpost. + description: A UUID string identifying this Outpost. required: true tags: - outposts @@ -17051,6 +17051,1068 @@ paths: schema: $ref: '#/components/schemas/GenericError' description: '' + /rbac/permissions/: + get: + operationId: rbac_permissions_list + description: Read-only list of all permissions, filterable by model and app + parameters: + - in: query + name: codename + schema: + type: string + - in: query + name: content_type__app_label + schema: + type: string + - in: query + name: content_type__model + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - in: query + name: role + schema: + type: string + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - in: query + name: user + schema: + type: integer + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedPermissionList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/{id}/: + get: + operationId: rbac_permissions_retrieve + description: Read-only list of all permissions, filterable by model and app + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this permission. + required: true + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Permission' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/assigned_by_roles/: + get: + operationId: rbac_permissions_assigned_by_roles_list + description: Get assigned object permissions for a single object + parameters: + - in: query + name: model + schema: + type: string + enum: + - authentik_blueprints.blueprintinstance + - authentik_core.application + - authentik_core.group + - authentik_core.token + - authentik_core.user + - authentik_crypto.certificatekeypair + - authentik_enterprise.license + - authentik_events.event + - authentik_events.notification + - authentik_events.notificationrule + - authentik_events.notificationtransport + - authentik_events.notificationwebhookmapping + - authentik_flows.flow + - authentik_flows.flowstagebinding + - authentik_outposts.dockerserviceconnection + - authentik_outposts.kubernetesserviceconnection + - authentik_outposts.outpost + - authentik_policies.policybinding + - authentik_policies_dummy.dummypolicy + - authentik_policies_event_matcher.eventmatcherpolicy + - authentik_policies_expiry.passwordexpirypolicy + - authentik_policies_expression.expressionpolicy + - authentik_policies_password.passwordpolicy + - authentik_policies_reputation.reputation + - authentik_policies_reputation.reputationpolicy + - authentik_providers_ldap.ldapprovider + - authentik_providers_oauth2.accesstoken + - authentik_providers_oauth2.authorizationcode + - authentik_providers_oauth2.oauth2provider + - authentik_providers_oauth2.refreshtoken + - authentik_providers_oauth2.scopemapping + - authentik_providers_proxy.proxyprovider + - authentik_providers_radius.radiusprovider + - authentik_providers_saml.samlpropertymapping + - authentik_providers_saml.samlprovider + - authentik_providers_scim.scimmapping + - authentik_providers_scim.scimprovider + - authentik_rbac.role + - authentik_sources_ldap.ldappropertymapping + - authentik_sources_ldap.ldapsource + - authentik_sources_oauth.oauthsource + - authentik_sources_oauth.useroauthsourceconnection + - authentik_sources_plex.plexsource + - authentik_sources_plex.plexsourceconnection + - authentik_sources_saml.samlsource + - authentik_sources_saml.usersamlsourceconnection + - authentik_stages_authenticator_duo.authenticatorduostage + - authentik_stages_authenticator_duo.duodevice + - authentik_stages_authenticator_sms.authenticatorsmsstage + - authentik_stages_authenticator_sms.smsdevice + - authentik_stages_authenticator_static.authenticatorstaticstage + - authentik_stages_authenticator_static.staticdevice + - authentik_stages_authenticator_totp.authenticatortotpstage + - authentik_stages_authenticator_totp.totpdevice + - authentik_stages_authenticator_validate.authenticatorvalidatestage + - authentik_stages_authenticator_webauthn.authenticatewebauthnstage + - authentik_stages_authenticator_webauthn.webauthndevice + - authentik_stages_captcha.captchastage + - authentik_stages_consent.consentstage + - authentik_stages_consent.userconsent + - authentik_stages_deny.denystage + - authentik_stages_dummy.dummystage + - authentik_stages_email.emailstage + - authentik_stages_identification.identificationstage + - authentik_stages_invitation.invitation + - authentik_stages_invitation.invitationstage + - authentik_stages_password.passwordstage + - authentik_stages_prompt.prompt + - authentik_stages_prompt.promptstage + - authentik_stages_user_delete.userdeletestage + - authentik_stages_user_login.userloginstage + - authentik_stages_user_logout.userlogoutstage + - authentik_stages_user_write.userwritestage + - authentik_tenants.tenant + description: |- + * `authentik_crypto.certificatekeypair` - Certificate-Key Pair + * `authentik_events.event` - Event + * `authentik_events.notificationtransport` - Notification Transport + * `authentik_events.notification` - Notification + * `authentik_events.notificationrule` - Notification Rule + * `authentik_events.notificationwebhookmapping` - Webhook Mapping + * `authentik_flows.flow` - Flow + * `authentik_flows.flowstagebinding` - Flow Stage Binding + * `authentik_outposts.dockerserviceconnection` - Docker Service-Connection + * `authentik_outposts.kubernetesserviceconnection` - Kubernetes Service-Connection + * `authentik_outposts.outpost` - Outpost + * `authentik_policies_dummy.dummypolicy` - Dummy Policy + * `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy + * `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy + * `authentik_policies_expression.expressionpolicy` - Expression Policy + * `authentik_policies_password.passwordpolicy` - Password Policy + * `authentik_policies_reputation.reputationpolicy` - Reputation Policy + * `authentik_policies_reputation.reputation` - Reputation Score + * `authentik_policies.policybinding` - Policy Binding + * `authentik_providers_ldap.ldapprovider` - LDAP Provider + * `authentik_providers_oauth2.scopemapping` - Scope Mapping + * `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider + * `authentik_providers_oauth2.authorizationcode` - Authorization Code + * `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token + * `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token + * `authentik_providers_proxy.proxyprovider` - Proxy Provider + * `authentik_providers_radius.radiusprovider` - Radius Provider + * `authentik_providers_saml.samlprovider` - SAML Provider + * `authentik_providers_saml.samlpropertymapping` - SAML Property Mapping + * `authentik_providers_scim.scimprovider` - SCIM Provider + * `authentik_providers_scim.scimmapping` - SCIM Mapping + * `authentik_rbac.role` - Role + * `authentik_sources_ldap.ldapsource` - LDAP Source + * `authentik_sources_ldap.ldappropertymapping` - LDAP Property Mapping + * `authentik_sources_oauth.oauthsource` - OAuth Source + * `authentik_sources_oauth.useroauthsourceconnection` - User OAuth Source Connection + * `authentik_sources_plex.plexsource` - Plex Source + * `authentik_sources_plex.plexsourceconnection` - User Plex Source Connection + * `authentik_sources_saml.samlsource` - SAML Source + * `authentik_sources_saml.usersamlsourceconnection` - User SAML Source Connection + * `authentik_stages_authenticator_duo.authenticatorduostage` - Duo Authenticator Setup Stage + * `authentik_stages_authenticator_duo.duodevice` - Duo Device + * `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage + * `authentik_stages_authenticator_sms.smsdevice` - SMS Device + * `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage + * `authentik_stages_authenticator_static.staticdevice` - Static Device + * `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage + * `authentik_stages_authenticator_totp.totpdevice` - TOTP Device + * `authentik_stages_authenticator_validate.authenticatorvalidatestage` - Authenticator Validation Stage + * `authentik_stages_authenticator_webauthn.authenticatewebauthnstage` - WebAuthn Authenticator Setup Stage + * `authentik_stages_authenticator_webauthn.webauthndevice` - WebAuthn Device + * `authentik_stages_captcha.captchastage` - Captcha Stage + * `authentik_stages_consent.consentstage` - Consent Stage + * `authentik_stages_consent.userconsent` - User Consent + * `authentik_stages_deny.denystage` - Deny Stage + * `authentik_stages_dummy.dummystage` - Dummy Stage + * `authentik_stages_email.emailstage` - Email Stage + * `authentik_stages_identification.identificationstage` - Identification Stage + * `authentik_stages_invitation.invitationstage` - Invitation Stage + * `authentik_stages_invitation.invitation` - Invitation + * `authentik_stages_password.passwordstage` - Password Stage + * `authentik_stages_prompt.prompt` - Prompt + * `authentik_stages_prompt.promptstage` - Prompt Stage + * `authentik_stages_user_delete.userdeletestage` - User Delete Stage + * `authentik_stages_user_login.userloginstage` - User Login Stage + * `authentik_stages_user_logout.userlogoutstage` - User Logout Stage + * `authentik_stages_user_write.userwritestage` - User Write Stage + * `authentik_tenants.tenant` - Tenant + * `authentik_blueprints.blueprintinstance` - Blueprint Instance + * `authentik_core.group` - Group + * `authentik_core.user` - User + * `authentik_core.application` - Application + * `authentik_core.token` - Token + * `authentik_enterprise.license` - License + required: true + - in: query + name: object_pk + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedRoleAssignedObjectPermissionList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/assigned_by_roles/{uuid}/assign/: + post: + operationId: rbac_permissions_assigned_by_roles_assign_create + description: |- + Assign permission(s) to role. When `object_pk` is set, the permissions + are only assigned to the specific object, otherwise they are assigned globally. + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PermissionAssignRequest' + required: true + security: + - authentik: [] + responses: + '204': + description: Successfully assigned + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/assigned_by_roles/{uuid}/unassign/: + patch: + operationId: rbac_permissions_assigned_by_roles_unassign_partial_update + description: |- + Unassign permission(s) to role. When `object_pk` is set, the permissions + are only assigned to the specific object, otherwise they are assigned globally. + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedPermissionAssignRequest' + security: + - authentik: [] + responses: + '204': + description: Successfully unassigned + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/assigned_by_users/: + get: + operationId: rbac_permissions_assigned_by_users_list + description: Get assigned object permissions for a single object + parameters: + - in: query + name: model + schema: + type: string + enum: + - authentik_blueprints.blueprintinstance + - authentik_core.application + - authentik_core.group + - authentik_core.token + - authentik_core.user + - authentik_crypto.certificatekeypair + - authentik_enterprise.license + - authentik_events.event + - authentik_events.notification + - authentik_events.notificationrule + - authentik_events.notificationtransport + - authentik_events.notificationwebhookmapping + - authentik_flows.flow + - authentik_flows.flowstagebinding + - authentik_outposts.dockerserviceconnection + - authentik_outposts.kubernetesserviceconnection + - authentik_outposts.outpost + - authentik_policies.policybinding + - authentik_policies_dummy.dummypolicy + - authentik_policies_event_matcher.eventmatcherpolicy + - authentik_policies_expiry.passwordexpirypolicy + - authentik_policies_expression.expressionpolicy + - authentik_policies_password.passwordpolicy + - authentik_policies_reputation.reputation + - authentik_policies_reputation.reputationpolicy + - authentik_providers_ldap.ldapprovider + - authentik_providers_oauth2.accesstoken + - authentik_providers_oauth2.authorizationcode + - authentik_providers_oauth2.oauth2provider + - authentik_providers_oauth2.refreshtoken + - authentik_providers_oauth2.scopemapping + - authentik_providers_proxy.proxyprovider + - authentik_providers_radius.radiusprovider + - authentik_providers_saml.samlpropertymapping + - authentik_providers_saml.samlprovider + - authentik_providers_scim.scimmapping + - authentik_providers_scim.scimprovider + - authentik_rbac.role + - authentik_sources_ldap.ldappropertymapping + - authentik_sources_ldap.ldapsource + - authentik_sources_oauth.oauthsource + - authentik_sources_oauth.useroauthsourceconnection + - authentik_sources_plex.plexsource + - authentik_sources_plex.plexsourceconnection + - authentik_sources_saml.samlsource + - authentik_sources_saml.usersamlsourceconnection + - authentik_stages_authenticator_duo.authenticatorduostage + - authentik_stages_authenticator_duo.duodevice + - authentik_stages_authenticator_sms.authenticatorsmsstage + - authentik_stages_authenticator_sms.smsdevice + - authentik_stages_authenticator_static.authenticatorstaticstage + - authentik_stages_authenticator_static.staticdevice + - authentik_stages_authenticator_totp.authenticatortotpstage + - authentik_stages_authenticator_totp.totpdevice + - authentik_stages_authenticator_validate.authenticatorvalidatestage + - authentik_stages_authenticator_webauthn.authenticatewebauthnstage + - authentik_stages_authenticator_webauthn.webauthndevice + - authentik_stages_captcha.captchastage + - authentik_stages_consent.consentstage + - authentik_stages_consent.userconsent + - authentik_stages_deny.denystage + - authentik_stages_dummy.dummystage + - authentik_stages_email.emailstage + - authentik_stages_identification.identificationstage + - authentik_stages_invitation.invitation + - authentik_stages_invitation.invitationstage + - authentik_stages_password.passwordstage + - authentik_stages_prompt.prompt + - authentik_stages_prompt.promptstage + - authentik_stages_user_delete.userdeletestage + - authentik_stages_user_login.userloginstage + - authentik_stages_user_logout.userlogoutstage + - authentik_stages_user_write.userwritestage + - authentik_tenants.tenant + description: |- + * `authentik_crypto.certificatekeypair` - Certificate-Key Pair + * `authentik_events.event` - Event + * `authentik_events.notificationtransport` - Notification Transport + * `authentik_events.notification` - Notification + * `authentik_events.notificationrule` - Notification Rule + * `authentik_events.notificationwebhookmapping` - Webhook Mapping + * `authentik_flows.flow` - Flow + * `authentik_flows.flowstagebinding` - Flow Stage Binding + * `authentik_outposts.dockerserviceconnection` - Docker Service-Connection + * `authentik_outposts.kubernetesserviceconnection` - Kubernetes Service-Connection + * `authentik_outposts.outpost` - Outpost + * `authentik_policies_dummy.dummypolicy` - Dummy Policy + * `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy + * `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy + * `authentik_policies_expression.expressionpolicy` - Expression Policy + * `authentik_policies_password.passwordpolicy` - Password Policy + * `authentik_policies_reputation.reputationpolicy` - Reputation Policy + * `authentik_policies_reputation.reputation` - Reputation Score + * `authentik_policies.policybinding` - Policy Binding + * `authentik_providers_ldap.ldapprovider` - LDAP Provider + * `authentik_providers_oauth2.scopemapping` - Scope Mapping + * `authentik_providers_oauth2.oauth2provider` - OAuth2/OpenID Provider + * `authentik_providers_oauth2.authorizationcode` - Authorization Code + * `authentik_providers_oauth2.accesstoken` - OAuth2 Access Token + * `authentik_providers_oauth2.refreshtoken` - OAuth2 Refresh Token + * `authentik_providers_proxy.proxyprovider` - Proxy Provider + * `authentik_providers_radius.radiusprovider` - Radius Provider + * `authentik_providers_saml.samlprovider` - SAML Provider + * `authentik_providers_saml.samlpropertymapping` - SAML Property Mapping + * `authentik_providers_scim.scimprovider` - SCIM Provider + * `authentik_providers_scim.scimmapping` - SCIM Mapping + * `authentik_rbac.role` - Role + * `authentik_sources_ldap.ldapsource` - LDAP Source + * `authentik_sources_ldap.ldappropertymapping` - LDAP Property Mapping + * `authentik_sources_oauth.oauthsource` - OAuth Source + * `authentik_sources_oauth.useroauthsourceconnection` - User OAuth Source Connection + * `authentik_sources_plex.plexsource` - Plex Source + * `authentik_sources_plex.plexsourceconnection` - User Plex Source Connection + * `authentik_sources_saml.samlsource` - SAML Source + * `authentik_sources_saml.usersamlsourceconnection` - User SAML Source Connection + * `authentik_stages_authenticator_duo.authenticatorduostage` - Duo Authenticator Setup Stage + * `authentik_stages_authenticator_duo.duodevice` - Duo Device + * `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage + * `authentik_stages_authenticator_sms.smsdevice` - SMS Device + * `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage + * `authentik_stages_authenticator_static.staticdevice` - Static Device + * `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage + * `authentik_stages_authenticator_totp.totpdevice` - TOTP Device + * `authentik_stages_authenticator_validate.authenticatorvalidatestage` - Authenticator Validation Stage + * `authentik_stages_authenticator_webauthn.authenticatewebauthnstage` - WebAuthn Authenticator Setup Stage + * `authentik_stages_authenticator_webauthn.webauthndevice` - WebAuthn Device + * `authentik_stages_captcha.captchastage` - Captcha Stage + * `authentik_stages_consent.consentstage` - Consent Stage + * `authentik_stages_consent.userconsent` - User Consent + * `authentik_stages_deny.denystage` - Deny Stage + * `authentik_stages_dummy.dummystage` - Dummy Stage + * `authentik_stages_email.emailstage` - Email Stage + * `authentik_stages_identification.identificationstage` - Identification Stage + * `authentik_stages_invitation.invitationstage` - Invitation Stage + * `authentik_stages_invitation.invitation` - Invitation + * `authentik_stages_password.passwordstage` - Password Stage + * `authentik_stages_prompt.prompt` - Prompt + * `authentik_stages_prompt.promptstage` - Prompt Stage + * `authentik_stages_user_delete.userdeletestage` - User Delete Stage + * `authentik_stages_user_login.userloginstage` - User Login Stage + * `authentik_stages_user_logout.userlogoutstage` - User Logout Stage + * `authentik_stages_user_write.userwritestage` - User Write Stage + * `authentik_tenants.tenant` - Tenant + * `authentik_blueprints.blueprintinstance` - Blueprint Instance + * `authentik_core.group` - Group + * `authentik_core.user` - User + * `authentik_core.application` - Application + * `authentik_core.token` - Token + * `authentik_enterprise.license` - License + required: true + - in: query + name: object_pk + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedUserAssignedObjectPermissionList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/assigned_by_users/{id}/assign/: + post: + operationId: rbac_permissions_assigned_by_users_assign_create + description: Assign permission(s) to user + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this User. + required: true + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PermissionAssignRequest' + required: true + security: + - authentik: [] + responses: + '204': + description: Successfully assigned + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/assigned_by_users/{id}/unassign/: + patch: + operationId: rbac_permissions_assigned_by_users_unassign_partial_update + description: |- + Unassign permission(s) to user. When `object_pk` is set, the permissions + are only assigned to the specific object, otherwise they are assigned globally. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this User. + required: true + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedPermissionAssignRequest' + security: + - authentik: [] + responses: + '204': + description: Successfully unassigned + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/roles/: + get: + operationId: rbac_permissions_roles_list + description: Get a role's assigned object permissions + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - in: query + name: uuid + schema: + type: string + format: uuid + required: true + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedExtraRoleObjectPermissionList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/permissions/users/: + get: + operationId: rbac_permissions_users_list + description: Get a users's assigned object permissions + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - in: query + name: user_id + schema: + type: integer + required: true + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedExtraUserObjectPermissionList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/roles/: + get: + operationId: rbac_roles_list + description: Role viewset + parameters: + - in: query + name: group__name + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedRoleList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + post: + operationId: rbac_roles_create + description: Role viewset + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RoleRequest' + required: true + security: + - authentik: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/roles/{uuid}/: + get: + operationId: rbac_roles_retrieve + description: Role viewset + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + put: + operationId: rbac_roles_update + description: Role viewset + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RoleRequest' + required: true + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + patch: + operationId: rbac_roles_partial_update + description: Role viewset + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedRoleRequest' + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Role' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + delete: + operationId: rbac_roles_destroy + description: Role viewset + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + security: + - authentik: [] + responses: + '204': + description: No response body + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /rbac/roles/{uuid}/used_by/: + get: + operationId: rbac_roles_used_by_list + description: Get a list of all objects that use this object + parameters: + - in: path + name: uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Role. + required: true + tags: + - rbac + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UsedBy' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' /root/config/: get: operationId: root_config_retrieve @@ -26725,6 +27787,7 @@ components: - authentik.providers.radius - authentik.providers.saml - authentik.providers.scim + - authentik.rbac - authentik.recovery - authentik.sources.ldap - authentik.sources.oauth @@ -26775,6 +27838,7 @@ components: * `authentik.providers.radius` - authentik Providers.Radius * `authentik.providers.saml` - authentik Providers.SAML * `authentik.providers.scim` - authentik Providers.SCIM + * `authentik.rbac` - authentik RBAC * `authentik.recovery` - authentik Recovery * `authentik.sources.ldap` - authentik Sources.LDAP * `authentik.sources.oauth` - authentik Sources.OAuth @@ -28495,11 +29559,11 @@ components: permissions: type: array items: - $ref: '#/components/schemas/Permission' + $ref: '#/components/schemas/ConsentPermission' additional_permissions: type: array items: - $ref: '#/components/schemas/Permission' + $ref: '#/components/schemas/ConsentPermission' token: type: string required: @@ -28522,6 +29586,17 @@ components: minLength: 1 required: - token + ConsentPermission: + type: object + description: Permission used for consent + properties: + name: + type: string + id: + type: string + required: + - id + - name ConsentStage: type: object description: ConsentStage Serializer @@ -29510,6 +30585,7 @@ components: * `authentik.providers.radius` - authentik Providers.Radius * `authentik.providers.saml` - authentik Providers.SAML * `authentik.providers.scim` - authentik Providers.SCIM + * `authentik.rbac` - authentik RBAC * `authentik.recovery` - authentik Recovery * `authentik.sources.ldap` - authentik Sources.LDAP * `authentik.sources.oauth` - authentik Sources.OAuth @@ -29556,7 +30632,7 @@ components: * `authentik_flows.flowstagebinding` - Flow Stage Binding * `authentik_outposts.dockerserviceconnection` - Docker Service-Connection * `authentik_outposts.kubernetesserviceconnection` - Kubernetes Service-Connection - * `authentik_outposts.outpost` - outpost + * `authentik_outposts.outpost` - Outpost * `authentik_policies_dummy.dummypolicy` - Dummy Policy * `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy * `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy @@ -29577,6 +30653,7 @@ components: * `authentik_providers_saml.samlpropertymapping` - SAML Property Mapping * `authentik_providers_scim.scimprovider` - SCIM Provider * `authentik_providers_scim.scimmapping` - SCIM Mapping + * `authentik_rbac.role` - Role * `authentik_sources_ldap.ldapsource` - LDAP Source * `authentik_sources_ldap.ldappropertymapping` - LDAP Property Mapping * `authentik_sources_oauth.oauthsource` - OAuth Source @@ -29590,9 +30667,9 @@ components: * `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage * `authentik_stages_authenticator_sms.smsdevice` - SMS Device * `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage - * `authentik_stages_authenticator_static.staticdevice` - Static device + * `authentik_stages_authenticator_static.staticdevice` - Static Device * `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage - * `authentik_stages_authenticator_totp.totpdevice` - TOTP device + * `authentik_stages_authenticator_totp.totpdevice` - TOTP Device * `authentik_stages_authenticator_validate.authenticatorvalidatestage` - Authenticator Validation Stage * `authentik_stages_authenticator_webauthn.authenticatewebauthnstage` - WebAuthn Authenticator Setup Stage * `authentik_stages_authenticator_webauthn.webauthndevice` - WebAuthn Device @@ -29614,10 +30691,11 @@ components: * `authentik_stages_user_write.userwritestage` - User Write Stage * `authentik_tenants.tenant` - Tenant * `authentik_blueprints.blueprintinstance` - Blueprint Instance - * `authentik_core.group` - group + * `authentik_core.group` - Group * `authentik_core.user` - User * `authentik_core.application` - Application * `authentik_core.token` - Token + * `authentik_enterprise.license` - License required: - bound_to - component @@ -29703,6 +30781,7 @@ components: * `authentik.providers.radius` - authentik Providers.Radius * `authentik.providers.saml` - authentik Providers.SAML * `authentik.providers.scim` - authentik Providers.SCIM + * `authentik.rbac` - authentik RBAC * `authentik.recovery` - authentik Recovery * `authentik.sources.ldap` - authentik Sources.LDAP * `authentik.sources.oauth` - authentik Sources.OAuth @@ -29749,7 +30828,7 @@ components: * `authentik_flows.flowstagebinding` - Flow Stage Binding * `authentik_outposts.dockerserviceconnection` - Docker Service-Connection * `authentik_outposts.kubernetesserviceconnection` - Kubernetes Service-Connection - * `authentik_outposts.outpost` - outpost + * `authentik_outposts.outpost` - Outpost * `authentik_policies_dummy.dummypolicy` - Dummy Policy * `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy * `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy @@ -29770,6 +30849,7 @@ components: * `authentik_providers_saml.samlpropertymapping` - SAML Property Mapping * `authentik_providers_scim.scimprovider` - SCIM Provider * `authentik_providers_scim.scimmapping` - SCIM Mapping + * `authentik_rbac.role` - Role * `authentik_sources_ldap.ldapsource` - LDAP Source * `authentik_sources_ldap.ldappropertymapping` - LDAP Property Mapping * `authentik_sources_oauth.oauthsource` - OAuth Source @@ -29783,9 +30863,9 @@ components: * `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage * `authentik_stages_authenticator_sms.smsdevice` - SMS Device * `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage - * `authentik_stages_authenticator_static.staticdevice` - Static device + * `authentik_stages_authenticator_static.staticdevice` - Static Device * `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage - * `authentik_stages_authenticator_totp.totpdevice` - TOTP device + * `authentik_stages_authenticator_totp.totpdevice` - TOTP Device * `authentik_stages_authenticator_validate.authenticatorvalidatestage` - Authenticator Validation Stage * `authentik_stages_authenticator_webauthn.authenticatewebauthnstage` - WebAuthn Authenticator Setup Stage * `authentik_stages_authenticator_webauthn.webauthndevice` - WebAuthn Device @@ -29807,10 +30887,11 @@ components: * `authentik_stages_user_write.userwritestage` - User Write Stage * `authentik_tenants.tenant` - Tenant * `authentik_blueprints.blueprintinstance` - Blueprint Instance - * `authentik_core.group` - group + * `authentik_core.group` - Group * `authentik_core.user` - User * `authentik_core.application` - Application * `authentik_core.token` - Token + * `authentik_enterprise.license` - License required: - name EventRequest: @@ -29948,6 +31029,106 @@ components: required: - expression - name + ExtraRoleObjectPermission: + type: object + description: User permission with additional object-related data + properties: + id: + type: integer + readOnly: true + codename: + type: string + readOnly: true + model: + type: string + title: Python model class name + readOnly: true + app_label: + type: string + readOnly: true + object_pk: + type: string + title: Object ID + readOnly: true + name: + type: string + readOnly: true + app_label_verbose: + type: string + description: Get app label from permission's model + readOnly: true + model_verbose: + type: string + description: Get model label from permission's model + readOnly: true + object_description: + type: string + nullable: true + description: |- + Get model description from attached model. This operation takes at least + one additional query, and the description is only shown if the user/role has the + view_ permission on the object + readOnly: true + required: + - app_label + - app_label_verbose + - codename + - id + - model + - model_verbose + - name + - object_description + - object_pk + ExtraUserObjectPermission: + type: object + description: User permission with additional object-related data + properties: + id: + type: integer + readOnly: true + codename: + type: string + readOnly: true + model: + type: string + title: Python model class name + readOnly: true + app_label: + type: string + readOnly: true + object_pk: + type: string + title: Object ID + readOnly: true + name: + type: string + readOnly: true + app_label_verbose: + type: string + description: Get app label from permission's model + readOnly: true + model_verbose: + type: string + description: Get model label from permission's model + readOnly: true + object_description: + type: string + nullable: true + description: |- + Get model description from attached model. This operation takes at least + one additional query, and the description is only shown if the user/role has the + view_ permission on the object + readOnly: true + required: + - app_label + - app_label_verbose + - codename + - id + - model + - model_verbose + - name + - object_description + - object_pk FilePathRequest: type: object description: Serializer to upload file @@ -30548,19 +31729,30 @@ components: type: array items: type: integer - attributes: - type: object - additionalProperties: {} users_obj: type: array items: $ref: '#/components/schemas/GroupMember' readOnly: true + attributes: + type: object + additionalProperties: {} + roles: + type: array + items: + type: string + format: uuid + roles_obj: + type: array + items: + $ref: '#/components/schemas/Role' + readOnly: true required: - name - num_pk - parent_name - pk + - roles_obj - users_obj GroupMember: type: object @@ -30661,6 +31853,11 @@ components: attributes: type: object additionalProperties: {} + roles: + type: array + items: + type: string + format: uuid required: - name IdentificationChallenge: @@ -31930,6 +33127,7 @@ components: - authentik_providers_saml.samlpropertymapping - authentik_providers_scim.scimprovider - authentik_providers_scim.scimmapping + - authentik_rbac.role - authentik_sources_ldap.ldapsource - authentik_sources_ldap.ldappropertymapping - authentik_sources_oauth.oauthsource @@ -31971,6 +33169,7 @@ components: - authentik_core.user - authentik_core.application - authentik_core.token + - authentik_enterprise.license type: string description: |- * `authentik_crypto.certificatekeypair` - Certificate-Key Pair @@ -31983,7 +33182,7 @@ components: * `authentik_flows.flowstagebinding` - Flow Stage Binding * `authentik_outposts.dockerserviceconnection` - Docker Service-Connection * `authentik_outposts.kubernetesserviceconnection` - Kubernetes Service-Connection - * `authentik_outposts.outpost` - outpost + * `authentik_outposts.outpost` - Outpost * `authentik_policies_dummy.dummypolicy` - Dummy Policy * `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy * `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy @@ -32004,6 +33203,7 @@ components: * `authentik_providers_saml.samlpropertymapping` - SAML Property Mapping * `authentik_providers_scim.scimprovider` - SCIM Provider * `authentik_providers_scim.scimmapping` - SCIM Mapping + * `authentik_rbac.role` - Role * `authentik_sources_ldap.ldapsource` - LDAP Source * `authentik_sources_ldap.ldappropertymapping` - LDAP Property Mapping * `authentik_sources_oauth.oauthsource` - OAuth Source @@ -32017,9 +33217,9 @@ components: * `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage * `authentik_stages_authenticator_sms.smsdevice` - SMS Device * `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage - * `authentik_stages_authenticator_static.staticdevice` - Static device + * `authentik_stages_authenticator_static.staticdevice` - Static Device * `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage - * `authentik_stages_authenticator_totp.totpdevice` - TOTP device + * `authentik_stages_authenticator_totp.totpdevice` - TOTP Device * `authentik_stages_authenticator_validate.authenticatorvalidatestage` - Authenticator Validation Stage * `authentik_stages_authenticator_webauthn.authenticatewebauthnstage` - WebAuthn Authenticator Setup Stage * `authentik_stages_authenticator_webauthn.webauthndevice` - WebAuthn Device @@ -32041,10 +33241,11 @@ components: * `authentik_stages_user_write.userwritestage` - User Write Stage * `authentik_tenants.tenant` - Tenant * `authentik_blueprints.blueprintinstance` - Blueprint Instance - * `authentik_core.group` - group + * `authentik_core.group` - Group * `authentik_core.user` - User * `authentik_core.application` - Application * `authentik_core.token` - Token + * `authentik_enterprise.license` - License NameIdPolicyEnum: enum: - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress @@ -33292,6 +34493,30 @@ components: required: - pagination - results + PaginatedExtraRoleObjectPermissionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/ExtraRoleObjectPermission' + required: + - pagination + - results + PaginatedExtraUserObjectPermissionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/ExtraUserObjectPermission' + required: + - pagination + - results PaginatedFlowList: type: object properties: @@ -33556,6 +34781,18 @@ components: required: - pagination - results + PaginatedPermissionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/Permission' + required: + - pagination + - results PaginatedPlexSourceConnectionList: type: object properties: @@ -33724,6 +34961,30 @@ components: required: - pagination - results + PaginatedRoleAssignedObjectPermissionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/RoleAssignedObjectPermission' + required: + - pagination + - results + PaginatedRoleList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/Role' + required: + - pagination + - results PaginatedSAMLPropertyMappingList: type: object properties: @@ -33904,6 +35165,18 @@ components: required: - pagination - results + PaginatedUserAssignedObjectPermissionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/UserAssignedObjectPermission' + required: + - pagination + - results PaginatedUserConsentList: type: object properties: @@ -34927,6 +36200,7 @@ components: * `authentik.providers.radius` - authentik Providers.Radius * `authentik.providers.saml` - authentik Providers.SAML * `authentik.providers.scim` - authentik Providers.SCIM + * `authentik.rbac` - authentik RBAC * `authentik.recovery` - authentik Recovery * `authentik.sources.ldap` - authentik Sources.LDAP * `authentik.sources.oauth` - authentik Sources.OAuth @@ -34973,7 +36247,7 @@ components: * `authentik_flows.flowstagebinding` - Flow Stage Binding * `authentik_outposts.dockerserviceconnection` - Docker Service-Connection * `authentik_outposts.kubernetesserviceconnection` - Kubernetes Service-Connection - * `authentik_outposts.outpost` - outpost + * `authentik_outposts.outpost` - Outpost * `authentik_policies_dummy.dummypolicy` - Dummy Policy * `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy * `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy @@ -34994,6 +36268,7 @@ components: * `authentik_providers_saml.samlpropertymapping` - SAML Property Mapping * `authentik_providers_scim.scimprovider` - SCIM Provider * `authentik_providers_scim.scimmapping` - SCIM Mapping + * `authentik_rbac.role` - Role * `authentik_sources_ldap.ldapsource` - LDAP Source * `authentik_sources_ldap.ldappropertymapping` - LDAP Property Mapping * `authentik_sources_oauth.oauthsource` - OAuth Source @@ -35007,9 +36282,9 @@ components: * `authentik_stages_authenticator_sms.authenticatorsmsstage` - SMS Authenticator Setup Stage * `authentik_stages_authenticator_sms.smsdevice` - SMS Device * `authentik_stages_authenticator_static.authenticatorstaticstage` - Static Authenticator Stage - * `authentik_stages_authenticator_static.staticdevice` - Static device + * `authentik_stages_authenticator_static.staticdevice` - Static Device * `authentik_stages_authenticator_totp.authenticatortotpstage` - TOTP Authenticator Setup Stage - * `authentik_stages_authenticator_totp.totpdevice` - TOTP device + * `authentik_stages_authenticator_totp.totpdevice` - TOTP Device * `authentik_stages_authenticator_validate.authenticatorvalidatestage` - Authenticator Validation Stage * `authentik_stages_authenticator_webauthn.authenticatewebauthnstage` - WebAuthn Authenticator Setup Stage * `authentik_stages_authenticator_webauthn.webauthndevice` - WebAuthn Device @@ -35031,10 +36306,11 @@ components: * `authentik_stages_user_write.userwritestage` - User Write Stage * `authentik_tenants.tenant` - Tenant * `authentik_blueprints.blueprintinstance` - Blueprint Instance - * `authentik_core.group` - group + * `authentik_core.group` - Group * `authentik_core.user` - User * `authentik_core.application` - Application * `authentik_core.token` - Token + * `authentik_enterprise.license` - License PatchedEventRequest: type: object description: Event Serializer @@ -35184,6 +36460,11 @@ components: attributes: type: object additionalProperties: {} + roles: + type: array + items: + type: string + format: uuid PatchedIdentificationStageRequest: type: object description: IdentificationStage Serializer @@ -35891,6 +37172,20 @@ components: minimum: -2147483648 description: How many attempts a user has before the flow is canceled. To lock the user out, use a reputation policy and a user_write stage. + PatchedPermissionAssignRequest: + type: object + description: Request to assign a new permission + properties: + permissions: + type: array + items: + type: string + minLength: 1 + model: + $ref: '#/components/schemas/ModelEnum' + object_pk: + type: string + minLength: 1 PatchedPlexSourceConnectionRequest: type: object description: Plex Source connection Serializer @@ -36198,6 +37493,14 @@ components: type: integer maximum: 2147483647 minimum: -2147483648 + PatchedRoleRequest: + type: object + description: Role serializer + properties: + name: + type: string + minLength: 1 + maxLength: 150 PatchedSAMLPropertyMappingRequest: type: object description: SAMLPropertyMapping Serializer @@ -36744,15 +38047,56 @@ components: maxLength: 200 Permission: type: object - description: Permission used for consent + description: Global permission properties: + id: + type: integer + readOnly: true name: type: string - id: + maxLength: 255 + codename: type: string + maxLength: 100 + model: + type: string + title: Python model class name + readOnly: true + app_label: + type: string + readOnly: true + app_label_verbose: + type: string + description: Human-readable app label + readOnly: true + model_verbose: + type: string + description: Human-readable model name + readOnly: true required: + - app_label + - app_label_verbose + - codename - id + - model + - model_verbose - name + PermissionAssignRequest: + type: object + description: Request to assign a new permission + properties: + permissions: + type: array + items: + type: string + minLength: 1 + model: + $ref: '#/components/schemas/ModelEnum' + object_pk: + type: string + minLength: 1 + required: + - permissions PlexAuthenticationChallenge: type: object description: Challenge shown to the user in identification stage @@ -38280,6 +39624,80 @@ components: * `discouraged` - Discouraged * `preferred` - Preferred * `required` - Required + Role: + type: object + description: Role serializer + properties: + pk: + type: string + format: uuid + readOnly: true + title: Uuid + name: + type: string + maxLength: 150 + required: + - name + - pk + RoleAssignedObjectPermission: + type: object + description: Roles assigned object permission serializer + properties: + role_pk: + type: string + readOnly: true + name: + type: string + readOnly: true + permissions: + type: array + items: + $ref: '#/components/schemas/RoleObjectPermission' + required: + - name + - permissions + - role_pk + RoleObjectPermission: + type: object + description: Role-bound object level permission + properties: + id: + type: integer + readOnly: true + codename: + type: string + readOnly: true + model: + type: string + title: Python model class name + readOnly: true + app_label: + type: string + readOnly: true + object_pk: + type: string + title: Object ID + readOnly: true + name: + type: string + readOnly: true + required: + - app_label + - codename + - id + - model + - name + - object_pk + RoleRequest: + type: object + description: Role serializer + properties: + name: + type: string + minLength: 1 + maxLength: 150 + required: + - name SAMLMetadata: type: object description: SAML Provider Metadata serializer @@ -40176,6 +41594,56 @@ components: type: integer required: - pk + UserAssignedObjectPermission: + type: object + description: Users assigned object permission serializer + properties: + pk: + type: integer + readOnly: true + title: ID + username: + type: string + description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ + only. + pattern: ^[\w.@+-]+$ + maxLength: 150 + name: + type: string + description: User's display name. + is_active: + type: boolean + title: Active + description: Designates whether this user should be treated as active. Unselect + this instead of deleting accounts. + last_login: + type: string + format: date-time + nullable: true + email: + type: string + format: email + title: Email address + maxLength: 254 + attributes: + type: object + additionalProperties: {} + uid: + type: string + readOnly: true + permissions: + type: array + items: + $ref: '#/components/schemas/UserObjectPermission' + is_superuser: + type: boolean + required: + - is_superuser + - name + - permissions + - pk + - uid + - username UserConsent: type: object description: UserConsent Serializer @@ -40564,6 +42032,37 @@ components: required: - identifier - user + UserObjectPermission: + type: object + description: User-bound object level permission + properties: + id: + type: integer + readOnly: true + codename: + type: string + readOnly: true + model: + type: string + title: Python model class name + readOnly: true + app_label: + type: string + readOnly: true + object_pk: + type: string + title: Object ID + readOnly: true + name: + type: string + readOnly: true + required: + - app_label + - codename + - id + - model + - name + - object_pk UserPasswordSetRequest: type: object properties: @@ -40705,6 +42204,12 @@ components: readOnly: true type: $ref: '#/components/schemas/UserTypeEnum' + system_permissions: + type: array + items: + type: string + description: Get all system permissions assigned to the user + readOnly: true required: - avatar - groups @@ -40713,6 +42218,7 @@ components: - name - pk - settings + - system_permissions - uid - username UserSelfGroups: diff --git a/web/src/admin/AdminInterface.ts b/web/src/admin/AdminInterface.ts index fa6d4efa5..44d927ae2 100644 --- a/web/src/admin/AdminInterface.ts +++ b/web/src/admin/AdminInterface.ts @@ -116,7 +116,11 @@ export class AdminInterface extends Interface { configureSentry(true); this.version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve(); this.user = await me(); - if (!this.user.user.isSuperuser && this.user.user.pk > 0) { + const canAccessAdmin = + this.user.user.isSuperuser || + // TODO: somehow add `access_admin_interface` to the API schema + this.user.user.systemPermissions.includes("access_admin_interface"); + if (!canAccessAdmin && this.user.user.pk > 0) { window.location.assign("/if/user"); } } @@ -211,6 +215,7 @@ export class AdminInterface extends Interface { [null, msg("Directory"), null, [ ["/identity/users", msg("Users"), [`^/identity/users/(?${ID_REGEX})$`]], ["/identity/groups", msg("Groups"), [`^/identity/groups/(?${UUID_REGEX})$`]], + ["/identity/roles", msg("Roles"), [`^/identity/roles/(?${UUID_REGEX})$`]], ["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?${SLUG_REGEX})$`]], ["/core/tokens", msg("Tokens and App passwords")], ["/flow/stages/invitations", msg("Invitations")]]], diff --git a/web/src/admin/Routes.ts b/web/src/admin/Routes.ts index 55a830835..1c7a9739e 100644 --- a/web/src/admin/Routes.ts +++ b/web/src/admin/Routes.ts @@ -80,6 +80,14 @@ export const ROUTES: Route[] = [ await import("@goauthentik/admin/users/UserViewPage"); return html``; }), + new Route(new RegExp("^/identity/roles$"), async () => { + await import("@goauthentik/admin/roles/RoleListPage"); + return html``; + }), + new Route(new RegExp(`^/identity/roles/(?${UUID_REGEX})$`), async (args) => { + await import("@goauthentik/admin/roles/RoleViewPage"); + return html``; + }), new Route(new RegExp("^/flow/stages/invitations$"), async () => { await import("@goauthentik/admin/stages/invitation/InvitationListPage"); return html``; diff --git a/web/src/admin/admin-overview/cards/AdminStatusCard.ts b/web/src/admin/admin-overview/cards/AdminStatusCard.ts index b773a78bc..d20f5d570 100644 --- a/web/src/admin/admin-overview/cards/AdminStatusCard.ts +++ b/web/src/admin/admin-overview/cards/AdminStatusCard.ts @@ -2,9 +2,12 @@ import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { PFSize } from "@goauthentik/elements/Spinner"; import { AggregateCard } from "@goauthentik/elements/cards/AggregateCard"; +import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { until } from "lit/directives/until.js"; +import { ResponseError } from "@goauthentik/api"; + export interface AdminStatus { icon: string; message?: TemplateResult; @@ -41,6 +44,12 @@ export abstract class AdminStatusCard extends AggregateCard { ${status.message ? html`

${status.message}

` : html``}`; + }) + .catch((exc: ResponseError) => { + return html`

+  ${exc.response.statusText} +

+

${msg("Failed to fetch")}

`; }), html``, )} diff --git a/web/src/admin/applications/ApplicationViewPage.ts b/web/src/admin/applications/ApplicationViewPage.ts index f65182ca7..84ea2f207 100644 --- a/web/src/admin/applications/ApplicationViewPage.ts +++ b/web/src/admin/applications/ApplicationViewPage.ts @@ -3,6 +3,7 @@ import "@goauthentik/admin/applications/ApplicationCheckAccessForm"; import "@goauthentik/admin/applications/ApplicationForm"; import "@goauthentik/admin/policies/BoundPoliciesList"; import { PFSize } from "@goauthentik/app/elements/Spinner"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import "@goauthentik/components/ak-app-icon"; import "@goauthentik/components/events/ObjectChangelog"; @@ -27,7 +28,12 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { Application, CoreApi, OutpostsApi } from "@goauthentik/api"; +import { + Application, + CoreApi, + OutpostsApi, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; @customElement("ak-application-view") export class ApplicationViewPage extends AKElement { @@ -299,6 +305,12 @@ export class ApplicationViewPage extends AKElement { + `; } } diff --git a/web/src/admin/blueprints/BlueprintListPage.ts b/web/src/admin/blueprints/BlueprintListPage.ts index e2e06351d..f038cc957 100644 --- a/web/src/admin/blueprints/BlueprintListPage.ts +++ b/web/src/admin/blueprints/BlueprintListPage.ts @@ -7,6 +7,7 @@ import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -18,7 +19,12 @@ import { customElement, property } from "lit/decorators.js"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; -import { BlueprintInstance, BlueprintInstanceStatusEnum, ManagedApi } from "@goauthentik/api"; +import { + BlueprintInstance, + BlueprintInstanceStatusEnum, + ManagedApi, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; export function BlueprintStatus(blueprint?: BlueprintInstance): string { if (!blueprint) return ""; @@ -151,6 +157,11 @@ export class BlueprintListPage extends TablePage { + + { diff --git a/web/src/admin/crypto/CertificateKeyPairListPage.ts b/web/src/admin/crypto/CertificateKeyPairListPage.ts index c6959ea40..990f3446e 100644 --- a/web/src/admin/crypto/CertificateKeyPairListPage.ts +++ b/web/src/admin/crypto/CertificateKeyPairListPage.ts @@ -6,6 +6,7 @@ import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -17,7 +18,11 @@ import { customElement, property } from "lit/decorators.js"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; -import { CertificateKeyPair, CryptoApi } from "@goauthentik/api"; +import { + CertificateKeyPair, + CryptoApi, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; @customElement("ak-crypto-certificate-list") export class CertificateKeyPairListPage extends TablePage { @@ -119,16 +124,21 @@ export class CertificateKeyPairListPage extends TablePage { `, html` ${item.certExpiry?.toLocaleString()} `, html` - ${msg("Update")} - ${msg("Update Certificate-Key Pair")} - - - - `, + ${msg("Update")} + ${msg("Update Certificate-Key Pair")} + + + + + + `, ]; } diff --git a/web/src/admin/enterprise/EnterpriseLicenseListPage.ts b/web/src/admin/enterprise/EnterpriseLicenseListPage.ts index 27a0c1b67..0d2f16cf3 100644 --- a/web/src/admin/enterprise/EnterpriseLicenseListPage.ts +++ b/web/src/admin/enterprise/EnterpriseLicenseListPage.ts @@ -7,6 +7,7 @@ import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/cards/AggregateCard"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -23,7 +24,13 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; -import { EnterpriseApi, License, LicenseForecast, LicenseSummary } from "@goauthentik/api"; +import { + EnterpriseApi, + License, + LicenseForecast, + LicenseSummary, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; @customElement("ak-enterprise-license-list") export class EnterpriseLicenseListPage extends TablePage { @@ -221,16 +228,21 @@ export class EnterpriseLicenseListPage extends TablePage {
${msg(str`External: ${item.externalUsers}`)}
`, html` ${item.expiry?.toLocaleString()} `, html` - ${msg("Update")} - ${msg("Update License")} - - - - `, + ${msg("Update")} + ${msg("Update License")} + + + + + + `, ]; } diff --git a/web/src/admin/events/RuleListPage.ts b/web/src/admin/events/RuleListPage.ts index 58d3e3c1b..e997903b1 100644 --- a/web/src/admin/events/RuleListPage.ts +++ b/web/src/admin/events/RuleListPage.ts @@ -6,6 +6,8 @@ import { uiConfig } from "@goauthentik/common/ui/config"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -15,7 +17,11 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { EventsApi, NotificationRule } from "@goauthentik/api"; +import { + EventsApi, + NotificationRule, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; @customElement("ak-event-rule-list") export class RuleListPage extends TablePage { @@ -88,15 +94,21 @@ export class RuleListPage extends TablePage { ? html`${item.groupObj.name}` : msg("None (rule disabled)")}`, html` - ${msg("Update")} - ${msg("Update Notification Rule")} - - - `, + ${msg("Update")} + ${msg("Update Notification Rule")} + + + + + + `, ]; } diff --git a/web/src/admin/events/TransportListPage.ts b/web/src/admin/events/TransportListPage.ts index 07f8bc57f..c36c21af9 100644 --- a/web/src/admin/events/TransportListPage.ts +++ b/web/src/admin/events/TransportListPage.ts @@ -5,6 +5,8 @@ import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -14,7 +16,11 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { EventsApi, NotificationTransport } from "@goauthentik/api"; +import { + EventsApi, + NotificationTransport, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; @customElement("ak-event-transport-list") export class TransportListPage extends TablePage { @@ -90,6 +96,12 @@ export class TransportListPage extends TablePage { + + + { diff --git a/web/src/admin/flows/FlowViewPage.ts b/web/src/admin/flows/FlowViewPage.ts index edd752c7f..c760821e5 100644 --- a/web/src/admin/flows/FlowViewPage.ts +++ b/web/src/admin/flows/FlowViewPage.ts @@ -3,6 +3,7 @@ import "@goauthentik/admin/flows/FlowDiagram"; import "@goauthentik/admin/flows/FlowForm"; import "@goauthentik/admin/policies/BoundPoliciesList"; import { DesignationToLabel } from "@goauthentik/app/admin/flows/utils"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import "@goauthentik/components/events/ObjectChangelog"; import { AKElement } from "@goauthentik/elements/Base"; @@ -22,7 +23,12 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { Flow, FlowsApi, ResponseError } from "@goauthentik/api"; +import { + Flow, + FlowsApi, + RbacPermissionsAssignedByUsersListModelEnum, + ResponseError, +} from "@goauthentik/api"; @customElement("ak-flow-view") export class FlowViewPage extends AKElement { @@ -267,6 +273,12 @@ export class FlowViewPage extends AKElement { + `; } } diff --git a/web/src/admin/groups/GroupForm.ts b/web/src/admin/groups/GroupForm.ts index 6525d1aa8..2a1cca350 100644 --- a/web/src/admin/groups/GroupForm.ts +++ b/web/src/admin/groups/GroupForm.ts @@ -11,13 +11,22 @@ import YAML from "yaml"; import { msg } from "@lit/localize"; import { CSSResult, TemplateResult, css, html } from "lit"; -import { customElement } from "lit/decorators.js"; +import { customElement, state } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; -import { CoreApi, CoreGroupsListRequest, Group } from "@goauthentik/api"; +import { + CoreApi, + CoreGroupsListRequest, + Group, + PaginatedRoleList, + RbacApi, +} from "@goauthentik/api"; @customElement("ak-group-form") export class GroupForm extends ModelForm { + @state() + roles?: PaginatedRoleList; + static get styles(): CSSResult[] { return super.styles.concat(css` .pf-c-button.pf-m-control { @@ -43,6 +52,12 @@ export class GroupForm extends ModelForm { } } + async load(): Promise { + this.roles = await new RbacApi(DEFAULT_CONFIG).rbacRolesList({ + ordering: "name", + }); + } + async send(data: Group): Promise { if (this.instance?.pk) { return new CoreApi(DEFAULT_CONFIG).coreGroupsPartialUpdate({ @@ -112,6 +127,26 @@ export class GroupForm extends ModelForm { > + + +

+ ${msg( + "Select roles to grant this groups' users' permissions from the selected roles.", + )} +

+

+ ${msg("Hold control/command to select multiple items.")} +

+
${msg("Group Info")}
-
+
+
+
+ ${msg("Roles")} +
+
+
+
    + ${this.group.rolesObj.map((role) => { + return html`
  • + ${role.name} + +
  • `; + })} +
+
+
+
+ + `; } } diff --git a/web/src/admin/outposts/OutpostListPage.ts b/web/src/admin/outposts/OutpostListPage.ts index 575319f16..390134ad0 100644 --- a/web/src/admin/outposts/OutpostListPage.ts +++ b/web/src/admin/outposts/OutpostListPage.ts @@ -10,6 +10,7 @@ import { PFSize } from "@goauthentik/elements/Spinner"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -23,7 +24,13 @@ import { ifDefined } from "lit/directives/if-defined.js"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; -import { Outpost, OutpostHealth, OutpostTypeEnum, OutpostsApi } from "@goauthentik/api"; +import { + Outpost, + OutpostHealth, + OutpostTypeEnum, + OutpostsApi, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; export function TypeToLabel(type?: OutpostTypeEnum): string { if (!type) return ""; @@ -141,6 +148,11 @@ export class OutpostListPage extends TablePage { + + ${item.managed !== "goauthentik.io/outposts/embedded" ? html` - `, + html` + + ${msg("Update")} + ${msg(str`Update ${item.verboseName}`)} + + + + + + + `, ]; } diff --git a/web/src/admin/policies/PolicyListPage.ts b/web/src/admin/policies/PolicyListPage.ts index a467f94b4..9a1dda215 100644 --- a/web/src/admin/policies/PolicyListPage.ts +++ b/web/src/admin/policies/PolicyListPage.ts @@ -13,6 +13,7 @@ import "@goauthentik/elements/forms/ConfirmationForm"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; import "@goauthentik/elements/forms/ProxyForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -92,6 +93,9 @@ export class PolicyListPage extends TablePage { + + + ${msg("Test")} ${msg("Test Policy")} diff --git a/web/src/admin/policies/reputation/ReputationListPage.ts b/web/src/admin/policies/reputation/ReputationListPage.ts index c2144a436..8c25f1514 100644 --- a/web/src/admin/policies/reputation/ReputationListPage.ts +++ b/web/src/admin/policies/reputation/ReputationListPage.ts @@ -4,6 +4,7 @@ import "@goauthentik/elements/buttons/ModalButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -13,7 +14,11 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { PoliciesApi, Reputation } from "@goauthentik/api"; +import { + PoliciesApi, + RbacPermissionsAssignedByUsersListModelEnum, + Reputation, +} from "@goauthentik/api"; @customElement("ak-policy-reputation-list") export class ReputationListPage extends TablePage { @@ -52,6 +57,7 @@ export class ReputationListPage extends TablePage { new TableColumn(msg("IP"), "ip"), new TableColumn(msg("Score"), "score"), new TableColumn(msg("Updated"), "updated"), + new TableColumn(msg("Actions")), ]; } @@ -86,6 +92,13 @@ export class ReputationListPage extends TablePage { ${item.ip}`, html`${item.score}`, html`${item.updated.toLocaleString()}`, + html` + + + `, ]; } } diff --git a/web/src/admin/property-mappings/PropertyMappingListPage.ts b/web/src/admin/property-mappings/PropertyMappingListPage.ts index 500525922..e961a744c 100644 --- a/web/src/admin/property-mappings/PropertyMappingListPage.ts +++ b/web/src/admin/property-mappings/PropertyMappingListPage.ts @@ -10,6 +10,7 @@ import { uiConfig } from "@goauthentik/common/ui/config"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; import "@goauthentik/elements/forms/ProxyForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; @@ -107,6 +108,8 @@ export class PropertyMappingListPage extends TablePage { + + ${msg("Test")} ${msg("Test Property Mapping")} diff --git a/web/src/admin/providers/ldap/LDAPProviderViewPage.ts b/web/src/admin/providers/ldap/LDAPProviderViewPage.ts index f644bb7c5..421b04334 100644 --- a/web/src/admin/providers/ldap/LDAPProviderViewPage.ts +++ b/web/src/admin/providers/ldap/LDAPProviderViewPage.ts @@ -1,5 +1,6 @@ import "@goauthentik/admin/providers/RelatedApplicationButton"; import "@goauthentik/admin/providers/ldap/LDAPProviderForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { me } from "@goauthentik/common/users"; @@ -27,7 +28,12 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { LDAPProvider, ProvidersApi, SessionUser } from "@goauthentik/api"; +import { + LDAPProvider, + ProvidersApi, + RbacPermissionsAssignedByUsersListModelEnum, + SessionUser, +} from "@goauthentik/api"; @customElement("ak-provider-ldap-view") export class LDAPProviderViewPage extends AKElement { @@ -101,6 +107,12 @@ export class LDAPProviderViewPage extends AKElement { + `; } diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts index 920e71ceb..afe5dfd58 100644 --- a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts +++ b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts @@ -33,6 +33,7 @@ import { OAuth2ProviderSetupURLs, PropertyMappingPreview, ProvidersApi, + RbacPermissionsAssignedByUsersListModelEnum, } from "@goauthentik/api"; @customElement("ak-provider-oauth2-view") @@ -128,6 +129,12 @@ export class OAuth2ProviderViewPage extends AKElement { + `; } diff --git a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts index 2451cfa1e..a0ce2792d 100644 --- a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts +++ b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts @@ -1,5 +1,6 @@ import "@goauthentik/admin/providers/RelatedApplicationButton"; import "@goauthentik/admin/providers/proxy/ProxyProviderForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { convertToSlug } from "@goauthentik/common/utils"; @@ -39,7 +40,12 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { ProvidersApi, ProxyMode, ProxyProvider } from "@goauthentik/api"; +import { + ProvidersApi, + ProxyMode, + ProxyProvider, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; export function ModeToLabel(action?: ProxyMode): string { if (!action) return ""; @@ -208,6 +214,12 @@ export class ProxyProviderViewPage extends AKElement { + `; } diff --git a/web/src/admin/providers/radius/RadiusProviderViewPage.ts b/web/src/admin/providers/radius/RadiusProviderViewPage.ts index b62600a97..3963face9 100644 --- a/web/src/admin/providers/radius/RadiusProviderViewPage.ts +++ b/web/src/admin/providers/radius/RadiusProviderViewPage.ts @@ -1,5 +1,6 @@ import "@goauthentik/admin/providers/RelatedApplicationButton"; import "@goauthentik/admin/providers/radius/RadiusProviderForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; @@ -21,10 +22,13 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css"; -import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css"; import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; -import { ProvidersApi, RadiusProvider } from "@goauthentik/api"; +import { + ProvidersApi, + RadiusProvider, + RbacPermissionsAssignedByUsersListModelEnum, +} from "@goauthentik/api"; @customElement("ak-provider-radius-view") export class RadiusProviderViewPage extends AKElement { @@ -50,7 +54,6 @@ export class RadiusProviderViewPage extends AKElement { PFBase, PFButton, PFPage, - PFFlex, PFDisplay, PFGallery, PFContent, @@ -162,6 +165,12 @@ export class RadiusProviderViewPage extends AKElement { + `; } } diff --git a/web/src/admin/providers/saml/SAMLProviderViewPage.ts b/web/src/admin/providers/saml/SAMLProviderViewPage.ts index 3e8d773ef..806a51b6b 100644 --- a/web/src/admin/providers/saml/SAMLProviderViewPage.ts +++ b/web/src/admin/providers/saml/SAMLProviderViewPage.ts @@ -1,5 +1,6 @@ import "@goauthentik/admin/providers/RelatedApplicationButton"; import "@goauthentik/admin/providers/saml/SAMLProviderForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { MessageLevel } from "@goauthentik/common/messages"; @@ -34,6 +35,7 @@ import { CertificateKeyPair, CryptoApi, ProvidersApi, + RbacPermissionsAssignedByUsersListModelEnum, SAMLMetadata, SAMLProvider, } from "@goauthentik/api"; @@ -226,6 +228,12 @@ export class SAMLProviderViewPage extends AKElement { + `; } diff --git a/web/src/admin/providers/scim/SCIMProviderViewPage.ts b/web/src/admin/providers/scim/SCIMProviderViewPage.ts index e9d0afb79..3998c7c81 100644 --- a/web/src/admin/providers/scim/SCIMProviderViewPage.ts +++ b/web/src/admin/providers/scim/SCIMProviderViewPage.ts @@ -1,4 +1,5 @@ import "@goauthentik/admin/providers/scim/SCIMProviderForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; @@ -26,7 +27,12 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { ProvidersApi, SCIMProvider, Task } from "@goauthentik/api"; +import { + ProvidersApi, + RbacPermissionsAssignedByUsersListModelEnum, + SCIMProvider, + Task, +} from "@goauthentik/api"; @customElement("ak-provider-scim-view") export class SCIMProviderViewPage extends AKElement { @@ -113,6 +119,12 @@ export class SCIMProviderViewPage extends AKElement { + `; } diff --git a/web/src/admin/roles/RoleForm.ts b/web/src/admin/roles/RoleForm.ts new file mode 100644 index 000000000..48b886b82 --- /dev/null +++ b/web/src/admin/roles/RoleForm.ts @@ -0,0 +1,56 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/elements/chips/Chip"; +import "@goauthentik/elements/chips/ChipGroup"; +import "@goauthentik/elements/forms/HorizontalFormElement"; +import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/forms/SearchSelect"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { RbacApi, Role } from "@goauthentik/api"; + +@customElement("ak-role-form") +export class RoleForm extends ModelForm { + loadInstance(pk: string): Promise { + return new RbacApi(DEFAULT_CONFIG).rbacRolesRetrieve({ + uuid: pk, + }); + } + + getSuccessMessage(): string { + if (this.instance) { + return msg("Successfully updated role."); + } else { + return msg("Successfully created role."); + } + } + + async send(data: Role): Promise { + if (this.instance?.pk) { + return new RbacApi(DEFAULT_CONFIG).rbacRolesPartialUpdate({ + uuid: this.instance.pk, + patchedRoleRequest: data, + }); + } else { + return new RbacApi(DEFAULT_CONFIG).rbacRolesCreate({ + roleRequest: data, + }); + } + } + + renderForm(): TemplateResult { + return html`
+ + + +
`; + } +} diff --git a/web/src/admin/roles/RoleListPage.ts b/web/src/admin/roles/RoleListPage.ts new file mode 100644 index 000000000..2bf8bf64e --- /dev/null +++ b/web/src/admin/roles/RoleListPage.ts @@ -0,0 +1,98 @@ +import "@goauthentik/admin/roles/RoleForm"; +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { uiConfig } from "@goauthentik/common/ui/config"; +import "@goauthentik/elements/buttons/SpinnerButton"; +import "@goauthentik/elements/forms/DeleteBulkForm"; +import "@goauthentik/elements/forms/ModalForm"; +import { PaginatedResponse } from "@goauthentik/elements/table/Table"; +import { TableColumn } from "@goauthentik/elements/table/Table"; +import { TablePage } from "@goauthentik/elements/table/TablePage"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import { RbacApi, Role } from "@goauthentik/api"; + +@customElement("ak-role-list") +export class RoleListPage extends TablePage { + checkbox = true; + searchEnabled(): boolean { + return true; + } + pageTitle(): string { + return msg("Roles"); + } + pageDescription(): string { + return msg("Manage roles which grant permissions to objects within authentik."); + } + pageIcon(): string { + return "fa fa-lock"; + } + + @property() + order = "name"; + + async apiEndpoint(page: number): Promise> { + return new RbacApi(DEFAULT_CONFIG).rbacRolesList({ + ordering: this.order, + page: page, + pageSize: (await uiConfig()).pagination.perPage, + search: this.search || "", + }); + } + + columns(): TableColumn[] { + return [new TableColumn(msg("Name"), "name"), new TableColumn(msg("Actions"))]; + } + + renderToolbarSelected(): TemplateResult { + const disabled = this.selectedElements.length < 1; + return html` { + return new RbacApi(DEFAULT_CONFIG).rbacRolesUsedByList({ + uuid: item.pk, + }); + }} + .delete=${(item: Role) => { + return new RbacApi(DEFAULT_CONFIG).rbacRolesDestroy({ + uuid: item.pk, + }); + }} + > + + `; + } + + row(item: Role): TemplateResult[] { + return [ + html`${item.name}`, + html` + ${msg("Update")} + ${msg("Update Role")} + + + `, + ]; + } + + renderObjectCreate(): TemplateResult { + return html` + + ${msg("Create")} + ${msg("Create Role")} + + + + `; + } +} diff --git a/web/src/admin/roles/RolePermissionForm.ts b/web/src/admin/roles/RolePermissionForm.ts new file mode 100644 index 000000000..f0312706a --- /dev/null +++ b/web/src/admin/roles/RolePermissionForm.ts @@ -0,0 +1,88 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/components/ak-toggle-group"; +import "@goauthentik/elements/chips/Chip"; +import "@goauthentik/elements/chips/ChipGroup"; +import "@goauthentik/elements/forms/HorizontalFormElement"; +import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/forms/Radio"; +import "@goauthentik/elements/forms/SearchSelect"; +import "@goauthentik/elements/rbac/PermissionSelectModal"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; + +import { Permission, RbacApi } from "@goauthentik/api"; + +interface RolePermissionAssign { + permissions: string[]; +} + +@customElement("ak-role-permission-form") +export class RolePermissionForm extends ModelForm { + @state() + permissionsToAdd: Permission[] = []; + + @property() + roleUuid?: string; + + async load(): Promise {} + + loadInstance(): Promise { + throw new Error("Method not implemented."); + } + + getSuccessMessage(): string { + return msg("Successfully assigned permission."); + } + + async send(data: RolePermissionAssign): Promise { + await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssignCreate({ + uuid: this.roleUuid || "", + permissionAssignRequest: { + permissions: data.permissions, + }, + }); + this.permissionsToAdd = []; + return; + } + + renderForm(): TemplateResult { + return html`
+ +
+ { + this.permissionsToAdd = items; + this.requestUpdate(); + return Promise.resolve(); + }} + > + + +
+ + ${this.permissionsToAdd.map((permission) => { + return html` { + const idx = this.permissionsToAdd.indexOf(permission); + this.permissionsToAdd.splice(idx, 1); + this.requestUpdate(); + }} + > + ${permission.name} + `; + })} + +
+
+
+
`; + } +} diff --git a/web/src/admin/roles/RolePermissionGlobalTable.ts b/web/src/admin/roles/RolePermissionGlobalTable.ts new file mode 100644 index 000000000..9a302c19c --- /dev/null +++ b/web/src/admin/roles/RolePermissionGlobalTable.ts @@ -0,0 +1,89 @@ +import "@goauthentik/admin/roles/RolePermissionForm"; +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { groupBy } from "@goauthentik/app/common/utils"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/app/elements/table/Table"; +import "@goauthentik/elements/forms/ModalForm"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { Permission, RbacApi } from "@goauthentik/api"; + +@customElement("ak-role-permissions-global-table") +export class RolePermissionGlobalTable extends Table { + @property() + roleUuid?: string; + + searchEnabled(): boolean { + return true; + } + + checkbox = true; + + order = "content_type__app_label,content_type__model"; + + apiEndpoint(page: number): Promise> { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + role: this.roleUuid, + page: page, + ordering: this.order, + search: this.search, + }); + } + + groupBy(items: Permission[]): [string, Permission[]][] { + return groupBy(items, (obj) => { + return obj.appLabelVerbose; + }); + } + + columns(): TableColumn[] { + return [ + new TableColumn("Model", "model"), + new TableColumn("Permission", ""), + new TableColumn(""), + ]; + } + + renderObjectCreate(): TemplateResult { + return html` + + ${msg("Assign")} + ${msg("Assign permission to role")} + + + + + `; + } + + renderToolbarSelected(): TemplateResult { + const disabled = this.selectedElements.length < 1; + return html` { + return new RbacApi( + DEFAULT_CONFIG, + ).rbacPermissionsAssignedByRolesUnassignPartialUpdate({ + uuid: this.roleUuid || "", + patchedPermissionAssignRequest: { + permissions: [`${item.appLabel}.${item.codename}`], + }, + }); + }} + > + + `; + } + + row(item: Permission): TemplateResult[] { + return [html`${item.modelVerbose}`, html`${item.name}`, html`✓`]; + } +} diff --git a/web/src/admin/roles/RolePermissionObjectTable.ts b/web/src/admin/roles/RolePermissionObjectTable.ts new file mode 100644 index 000000000..e8a71963a --- /dev/null +++ b/web/src/admin/roles/RolePermissionObjectTable.ts @@ -0,0 +1,94 @@ +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { groupBy } from "@goauthentik/app/common/utils"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/app/elements/table/Table"; +import "@goauthentik/elements/forms/DeleteBulkForm"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import { ExtraRoleObjectPermission, RbacApi } from "@goauthentik/api"; + +@customElement("ak-role-permissions-object-table") +export class RolePermissionObjectTable extends Table { + @property() + roleUuid?: string; + + searchEnabled(): boolean { + return true; + } + + checkbox = true; + + apiEndpoint(page: number): Promise> { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsRolesList({ + uuid: this.roleUuid || "", + page: page, + ordering: this.order, + search: this.search, + }); + } + + groupBy(items: ExtraRoleObjectPermission[]): [string, ExtraRoleObjectPermission[]][] { + return groupBy(items, (obj) => { + return obj.appLabelVerbose; + }); + } + + columns(): TableColumn[] { + return [ + new TableColumn("Model", "model"), + new TableColumn("Permission", ""), + new TableColumn("Object", ""), + new TableColumn(""), + ]; + } + + renderToolbarSelected(): TemplateResult { + const disabled = this.selectedElements.length < 1; + return html` { + return [ + { key: msg("Permission"), value: item.name }, + { key: msg("Object"), value: item.objectDescription || item.objectPk }, + ]; + }} + .delete=${(item: ExtraRoleObjectPermission) => { + return new RbacApi( + DEFAULT_CONFIG, + ).rbacPermissionsAssignedByRolesUnassignPartialUpdate({ + uuid: this.roleUuid || "", + patchedPermissionAssignRequest: { + permissions: [`${item.appLabel}.${item.codename}`], + objectPk: item.objectPk, + }, + }); + }} + > + + `; + } + + row(item: ExtraRoleObjectPermission): TemplateResult[] { + return [ + html`${item.modelVerbose}`, + html`${item.name}`, + html`${item.objectDescription + ? html`${item.objectDescription}` + : html` +
${item.objectPk}
+
`}`, + html`✓`, + ]; + } +} diff --git a/web/src/admin/roles/RoleViewPage.ts b/web/src/admin/roles/RoleViewPage.ts new file mode 100644 index 000000000..b9a77fac4 --- /dev/null +++ b/web/src/admin/roles/RoleViewPage.ts @@ -0,0 +1,144 @@ +import "@goauthentik/admin/groups/RelatedGroupList"; +import "@goauthentik/app/admin/roles/RolePermissionGlobalTable"; +import "@goauthentik/app/admin/roles/RolePermissionObjectTable"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { EVENT_REFRESH } from "@goauthentik/common/constants"; +import "@goauthentik/components/events/ObjectChangelog"; +import "@goauthentik/components/events/UserEvents"; +import { AKElement } from "@goauthentik/elements/Base"; +import "@goauthentik/elements/CodeMirror"; +import "@goauthentik/elements/PageHeader"; +import "@goauthentik/elements/Tabs"; + +import { msg, str } from "@lit/localize"; +import { CSSResult, TemplateResult, css, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; + +import PFButton from "@patternfly/patternfly/components/Button/button.css"; +import PFCard from "@patternfly/patternfly/components/Card/card.css"; +import PFContent from "@patternfly/patternfly/components/Content/content.css"; +import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; +import PFPage from "@patternfly/patternfly/components/Page/page.css"; +import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; +import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css"; + +import { RbacApi, RbacPermissionsAssignedByUsersListModelEnum, Role } from "@goauthentik/api"; + +@customElement("ak-role-view") +export class RoleViewPage extends AKElement { + @property({ type: String }) + set roleId(id: string) { + new RbacApi(DEFAULT_CONFIG) + .rbacRolesRetrieve({ + uuid: id, + }) + .then((role) => { + this._role = role; + }); + } + + @state() + _role?: Role; + + static get styles(): CSSResult[] { + return [ + PFBase, + PFPage, + PFButton, + PFDisplay, + PFGrid, + PFContent, + PFCard, + PFDescriptionList, + css` + .pf-c-description-list__description ak-action-button { + margin-right: 6px; + margin-bottom: 6px; + } + .ak-button-collection { + max-width: 12em; + } + `, + ]; + } + + constructor() { + super(); + this.addEventListener(EVENT_REFRESH, () => { + if (!this._role?.pk) return; + this.roleId = this._role?.pk; + }); + } + + render(): TemplateResult { + return html` + + ${this.renderBody()}`; + } + + renderBody(): TemplateResult { + if (!this._role) { + return html``; + } + return html` +
+
+
+
${msg("Role Info")}
+
+
+
+
+ ${msg("Name")} +
+
+
+ ${this._role.name} +
+
+
+
+
+
+
+
${msg("Assigned global permissions")}
+
+ +
+
+
+
${msg("Assigned object permissions")}
+
+ +
+
+
+
+ +
`; + } +} diff --git a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts index 2c74bc5f2..36129c3c4 100644 --- a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts +++ b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts @@ -1,4 +1,5 @@ import "@goauthentik/admin/sources/ldap/LDAPSourceForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; @@ -22,7 +23,13 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { LDAPSource, SourcesApi, Task, TaskStatusEnum } from "@goauthentik/api"; +import { + LDAPSource, + RbacPermissionsAssignedByUsersListModelEnum, + SourcesApi, + Task, + TaskStatusEnum, +} from "@goauthentik/api"; @customElement("ak-source-ldap-view") export class LDAPSourceViewPage extends AKElement { @@ -206,6 +213,12 @@ export class LDAPSourceViewPage extends AKElement { + `; } } diff --git a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts index 3bfa5cdaf..f70c13038 100644 --- a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts +++ b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts @@ -1,6 +1,7 @@ import "@goauthentik/admin/policies/BoundPoliciesList"; import "@goauthentik/admin/sources/oauth/OAuthSourceDiagram"; import "@goauthentik/admin/sources/oauth/OAuthSourceForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; @@ -22,7 +23,12 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { OAuthSource, ProviderTypeEnum, SourcesApi } from "@goauthentik/api"; +import { + OAuthSource, + ProviderTypeEnum, + RbacPermissionsAssignedByUsersListModelEnum, + SourcesApi, +} from "@goauthentik/api"; export function ProviderToLabel(provider?: ProviderTypeEnum): string { switch (provider) { @@ -238,6 +244,12 @@ export class OAuthSourceViewPage extends AKElement { + `; } } diff --git a/web/src/admin/sources/plex/PlexSourceViewPage.ts b/web/src/admin/sources/plex/PlexSourceViewPage.ts index 51db79d27..88287a8b2 100644 --- a/web/src/admin/sources/plex/PlexSourceViewPage.ts +++ b/web/src/admin/sources/plex/PlexSourceViewPage.ts @@ -1,5 +1,6 @@ import "@goauthentik/admin/policies/BoundPoliciesList"; import "@goauthentik/admin/sources/plex/PlexSourceForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; @@ -21,7 +22,11 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { PlexSource, SourcesApi } from "@goauthentik/api"; +import { + PlexSource, + RbacPermissionsAssignedByUsersListModelEnum, + SourcesApi, +} from "@goauthentik/api"; @customElement("ak-source-plex-view") export class PlexSourceViewPage extends AKElement { @@ -131,6 +136,12 @@ export class PlexSourceViewPage extends AKElement { + `; } } diff --git a/web/src/admin/sources/saml/SAMLSourceViewPage.ts b/web/src/admin/sources/saml/SAMLSourceViewPage.ts index b85768242..56a8750c9 100644 --- a/web/src/admin/sources/saml/SAMLSourceViewPage.ts +++ b/web/src/admin/sources/saml/SAMLSourceViewPage.ts @@ -1,5 +1,6 @@ import "@goauthentik/admin/policies/BoundPoliciesList"; import "@goauthentik/admin/sources/saml/SAMLSourceForm"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import "@goauthentik/components/events/ObjectChangelog"; @@ -22,7 +23,12 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { SAMLMetadata, SAMLSource, SourcesApi } from "@goauthentik/api"; +import { + RbacPermissionsAssignedByUsersListModelEnum, + SAMLMetadata, + SAMLSource, + SourcesApi, +} from "@goauthentik/api"; @customElement("ak-source-saml-view") export class SAMLSourceViewPage extends AKElement { @@ -206,6 +212,12 @@ export class SAMLSourceViewPage extends AKElement { + `; } } diff --git a/web/src/admin/stages/StageListPage.ts b/web/src/admin/stages/StageListPage.ts index 3308fb50e..fb28cf42d 100644 --- a/web/src/admin/stages/StageListPage.ts +++ b/web/src/admin/stages/StageListPage.ts @@ -24,6 +24,7 @@ import { uiConfig } from "@goauthentik/common/ui/config"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; import "@goauthentik/elements/forms/ProxyForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -149,6 +150,8 @@ export class StageListPage extends TablePage {
+ + ${this.renderStageActions(item)}`, ]; } diff --git a/web/src/admin/stages/invitation/InvitationListLink.ts b/web/src/admin/stages/invitation/InvitationListLink.ts index 6f96db4f5..a08033c87 100644 --- a/web/src/admin/stages/invitation/InvitationListLink.ts +++ b/web/src/admin/stages/invitation/InvitationListLink.ts @@ -9,7 +9,6 @@ import { until } from "lit/directives/until.js"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; -import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import { Invitation, StagesApi } from "@goauthentik/api"; @@ -23,7 +22,7 @@ export class InvitationListLink extends AKElement { selectedFlow?: string; static get styles(): CSSResult[] { - return [PFBase, PFForm, PFFormControl, PFFlex, PFDescriptionList]; + return [PFBase, PFForm, PFFormControl, PFDescriptionList]; } renderLink(): string { diff --git a/web/src/admin/stages/invitation/InvitationListPage.ts b/web/src/admin/stages/invitation/InvitationListPage.ts index 41b81e5f1..2288b0691 100644 --- a/web/src/admin/stages/invitation/InvitationListPage.ts +++ b/web/src/admin/stages/invitation/InvitationListPage.ts @@ -7,6 +7,7 @@ import "@goauthentik/elements/buttons/ModalButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -19,7 +20,12 @@ import { ifDefined } from "lit/directives/if-defined.js"; import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; -import { FlowDesignationEnum, Invitation, StagesApi } from "@goauthentik/api"; +import { + FlowDesignationEnum, + Invitation, + RbacPermissionsAssignedByUsersListModelEnum, + StagesApi, +} from "@goauthentik/api"; @customElement("ak-stage-invitation-list") export class InvitationListPage extends TablePage { @@ -124,15 +130,20 @@ export class InvitationListPage extends TablePage { html`${item.createdBy?.username}`, html`${item.expires?.toLocaleString() || msg("-")}`, html` - ${msg("Update")} - ${msg("Update Invitation")} - - - `, + ${msg("Update")} + ${msg("Update Invitation")} + + + + + `, ]; } diff --git a/web/src/admin/stages/prompt/PromptListPage.ts b/web/src/admin/stages/prompt/PromptListPage.ts index a67f566e1..c2b84a689 100644 --- a/web/src/admin/stages/prompt/PromptListPage.ts +++ b/web/src/admin/stages/prompt/PromptListPage.ts @@ -5,6 +5,7 @@ import "@goauthentik/elements/buttons/ModalButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -14,7 +15,7 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { Prompt, StagesApi } from "@goauthentik/api"; +import { Prompt, RbacPermissionsAssignedByUsersListModelEnum, StagesApi } from "@goauthentik/api"; @customElement("ak-stage-prompt-list") export class PromptListPage extends TablePage { @@ -88,15 +89,20 @@ export class PromptListPage extends TablePage { return html`
  • ${stage.name}
  • `; })}`, html` - ${msg("Update")} - ${msg("Update Prompt")} - - - `, + ${msg("Update")} + ${msg("Update Prompt")} + + + + + `, ]; } diff --git a/web/src/admin/tenants/TenantListPage.ts b/web/src/admin/tenants/TenantListPage.ts index 560b0d097..2edaeb2d4 100644 --- a/web/src/admin/tenants/TenantListPage.ts +++ b/web/src/admin/tenants/TenantListPage.ts @@ -5,6 +5,7 @@ import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -14,7 +15,7 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { CoreApi, Tenant } from "@goauthentik/api"; +import { CoreApi, RbacPermissionsAssignedByUsersListModelEnum, Tenant } from "@goauthentik/api"; @customElement("ak-tenant-list") export class TenantListPage extends TablePage { @@ -85,15 +86,21 @@ export class TenantListPage extends TablePage { ${item._default ? msg("Yes") : msg("No")} `, html` - ${msg("Update")} - ${msg("Update Tenant")} - - - `, + ${msg("Update")} + ${msg("Update Tenant")} + + + + + + `, ]; } diff --git a/web/src/admin/tokens/TokenListPage.ts b/web/src/admin/tokens/TokenListPage.ts index ea6c979df..ec4d1018d 100644 --- a/web/src/admin/tokens/TokenListPage.ts +++ b/web/src/admin/tokens/TokenListPage.ts @@ -7,6 +7,7 @@ import "@goauthentik/elements/buttons/Dropdown"; import "@goauthentik/elements/buttons/TokenCopyButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/ObjectPermissionModal"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; import { TablePage } from "@goauthentik/elements/table/TablePage"; @@ -16,7 +17,12 @@ import { msg } from "@lit/localize"; import { TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { CoreApi, IntentEnum, Token } from "@goauthentik/api"; +import { + CoreApi, + IntentEnum, + RbacPermissionsAssignedByUsersListModelEnum, + Token, +} from "@goauthentik/api"; @customElement("ak-token-list") export class TokenListPage extends TablePage { @@ -120,7 +126,19 @@ export class TokenListPage extends TablePage { ` - : html``} + : html` `} + + { } renderForm(): TemplateResult { - return html`${this.group?.isSuperuser ? html`` : html``} - -
    - { - this.usersToAdd = items; - this.requestUpdate(); - return Promise.resolve(); - }} - > - - -
    - - ${this.usersToAdd.map((user) => { - return html` { - const idx = this.usersToAdd.indexOf(user); - this.usersToAdd.splice(idx, 1); - this.requestUpdate(); - }} - > - ${UserOption(user)} - `; - })} - -
    + return html` +
    + { + this.usersToAdd = items; + this.requestUpdate(); + return Promise.resolve(); + }} + > + + +
    + + ${this.usersToAdd.map((user) => { + return html` { + const idx = this.usersToAdd.indexOf(user); + this.usersToAdd.splice(idx, 1); + this.requestUpdate(); + }} + > + ${UserOption(user)} + `; + })} +
    - `; +
    +
    `; } } diff --git a/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts b/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts new file mode 100644 index 000000000..99f171c81 --- /dev/null +++ b/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts @@ -0,0 +1,88 @@ +import "@goauthentik/admin/users/UserPermissionForm"; +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { groupBy } from "@goauthentik/app/common/utils"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/app/elements/table/Table"; +import "@goauthentik/elements/forms/DeleteBulkForm"; +import "@goauthentik/elements/forms/ModalForm"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { Permission, RbacApi } from "@goauthentik/api"; + +@customElement("ak-user-assigned-global-permissions-table") +export class UserAssignedGlobalPermissionsTable extends Table { + @property({ type: Number }) + userId?: number; + + checkbox = true; + + apiEndpoint(page: number): Promise> { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + user: this.userId || 0, + page: page, + ordering: this.order, + search: this.search, + }); + } + + groupBy(items: Permission[]): [string, Permission[]][] { + return groupBy(items, (obj) => { + return obj.appLabelVerbose; + }); + } + + columns(): TableColumn[] { + return [ + new TableColumn("Model", "model"), + new TableColumn("Permission", ""), + new TableColumn(""), + ]; + } + + renderObjectCreate(): TemplateResult { + return html` + + ${msg("Assign")} + ${msg("Assign permission to user")} + + + + + `; + } + + renderToolbarSelected(): TemplateResult { + const disabled = this.selectedElements.length < 1; + return html` { + return [{ key: msg("Permission"), value: item.name }]; + }} + .delete=${(item: Permission) => { + return new RbacApi( + DEFAULT_CONFIG, + ).rbacPermissionsAssignedByUsersUnassignPartialUpdate({ + id: this.userId || 0, + patchedPermissionAssignRequest: { + permissions: [`${item.appLabel}.${item.codename}`], + }, + }); + }} + > + + `; + } + + row(item: Permission): TemplateResult[] { + return [html`${item.modelVerbose}`, html`${item.name}`, html`✓`]; + } +} diff --git a/web/src/admin/users/UserAssignedObjectPermissionsTable.ts b/web/src/admin/users/UserAssignedObjectPermissionsTable.ts new file mode 100644 index 000000000..2b5589dc4 --- /dev/null +++ b/web/src/admin/users/UserAssignedObjectPermissionsTable.ts @@ -0,0 +1,90 @@ +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { groupBy } from "@goauthentik/app/common/utils"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/app/elements/table/Table"; +import "@goauthentik/elements/forms/DeleteBulkForm"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import { ExtraUserObjectPermission, RbacApi } from "@goauthentik/api"; + +@customElement("ak-user-assigned-object-permissions-table") +export class UserAssignedObjectPermissionsTable extends Table { + @property({ type: Number }) + userId?: number; + + checkbox = true; + + apiEndpoint(page: number): Promise> { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsUsersList({ + userId: this.userId || 0, + page: page, + ordering: this.order, + search: this.search, + }); + } + + groupBy(items: ExtraUserObjectPermission[]): [string, ExtraUserObjectPermission[]][] { + return groupBy(items, (obj) => { + return obj.appLabelVerbose; + }); + } + + columns(): TableColumn[] { + return [ + new TableColumn("Model", "model"), + new TableColumn("Permission", ""), + new TableColumn("Object", ""), + new TableColumn(""), + ]; + } + + renderToolbarSelected(): TemplateResult { + const disabled = this.selectedElements.length < 1; + return html` { + return [ + { key: msg("Permission"), value: item.name }, + { key: msg("Object"), value: item.objectDescription || item.objectPk }, + ]; + }} + .delete=${(item: ExtraUserObjectPermission) => { + return new RbacApi( + DEFAULT_CONFIG, + ).rbacPermissionsAssignedByUsersUnassignPartialUpdate({ + id: this.userId || 0, + patchedPermissionAssignRequest: { + permissions: [`${item.appLabel}.${item.codename}`], + objectPk: item.objectPk, + }, + }); + }} + > + + `; + } + + row(item: ExtraUserObjectPermission): TemplateResult[] { + return [ + html`${item.modelVerbose}`, + html`${item.name}`, + html`${item.objectDescription + ? html`${item.objectDescription}` + : html` +
    ${item.objectPk}
    +
    `}`, + html`✓`, + ]; + } +} diff --git a/web/src/admin/users/UserDevicesList.ts b/web/src/admin/users/UserDevicesTable.ts similarity index 96% rename from web/src/admin/users/UserDevicesList.ts rename to web/src/admin/users/UserDevicesTable.ts index 6db5af610..b120c3265 100644 --- a/web/src/admin/users/UserDevicesList.ts +++ b/web/src/admin/users/UserDevicesTable.ts @@ -10,8 +10,8 @@ import { customElement, property } from "lit/decorators.js"; import { AuthenticatorsApi, Device } from "@goauthentik/api"; -@customElement("ak-user-device-list") -export class UserDeviceList extends Table { +@customElement("ak-user-device-table") +export class UserDeviceTable extends Table { @property({ type: Number }) userId?: number; diff --git a/web/src/admin/users/UserPermissionForm.ts b/web/src/admin/users/UserPermissionForm.ts new file mode 100644 index 000000000..1f89045e0 --- /dev/null +++ b/web/src/admin/users/UserPermissionForm.ts @@ -0,0 +1,88 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/components/ak-toggle-group"; +import "@goauthentik/elements/chips/Chip"; +import "@goauthentik/elements/chips/ChipGroup"; +import "@goauthentik/elements/forms/HorizontalFormElement"; +import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/forms/Radio"; +import "@goauthentik/elements/forms/SearchSelect"; +import "@goauthentik/elements/rbac/PermissionSelectModal"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; + +import { Permission, RbacApi } from "@goauthentik/api"; + +interface UserPermissionAssign { + permissions: string[]; +} + +@customElement("ak-user-permission-form") +export class UserPermissionForm extends ModelForm { + @state() + permissionsToAdd: Permission[] = []; + + @property({ type: Number }) + userId?: number; + + async load(): Promise {} + + loadInstance(): Promise { + throw new Error("Method not implemented."); + } + + getSuccessMessage(): string { + return msg("Successfully assigned permission."); + } + + async send(data: UserPermissionAssign): Promise { + await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssignCreate({ + id: this.userId || 0, + permissionAssignRequest: { + permissions: data.permissions, + }, + }); + this.permissionsToAdd = []; + return; + } + + renderForm(): TemplateResult { + return html`
    + +
    + { + this.permissionsToAdd = items; + this.requestUpdate(); + return Promise.resolve(); + }} + > + + +
    + + ${this.permissionsToAdd.map((permission) => { + return html` { + const idx = this.permissionsToAdd.indexOf(permission); + this.permissionsToAdd.splice(idx, 1); + this.requestUpdate(); + }} + > + ${permission.name} + `; + })} + +
    +
    +
    +
    `; + } +} diff --git a/web/src/admin/users/UserViewPage.ts b/web/src/admin/users/UserViewPage.ts index 0212f1336..c97a20298 100644 --- a/web/src/admin/users/UserViewPage.ts +++ b/web/src/admin/users/UserViewPage.ts @@ -3,7 +3,10 @@ import "@goauthentik/admin/users/UserActiveForm"; import "@goauthentik/admin/users/UserChart"; import "@goauthentik/admin/users/UserForm"; import "@goauthentik/admin/users/UserPasswordForm"; +import "@goauthentik/app/admin/users/UserAssignedGlobalPermissionsTable"; +import "@goauthentik/app/admin/users/UserAssignedObjectPermissionsTable"; import { me } from "@goauthentik/app/common/users"; +import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { MessageLevel } from "@goauthentik/common/messages"; @@ -35,12 +38,17 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css"; -import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css"; import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; -import { CapabilitiesEnum, CoreApi, SessionUser, User } from "@goauthentik/api"; +import { + CapabilitiesEnum, + CoreApi, + RbacPermissionsAssignedByUsersListModelEnum, + SessionUser, + User, +} from "@goauthentik/api"; -import "./UserDevicesList"; +import "./UserDevicesTable"; @customElement("ak-user-view") export class UserViewPage extends AKElement { @@ -68,7 +76,6 @@ export class UserViewPage extends AKElement { return [ PFBase, PFPage, - PFFlex, PFButton, PFDisplay, PFGrid, @@ -443,7 +450,35 @@ export class UserViewPage extends AKElement { >
    - + +
    +
    + + +
    +
    +
    +
    ${msg("Assigned global permissions")}
    +
    + + +
    +
    +
    +
    ${msg("Assigned object permissions")}
    +
    + + +
    diff --git a/web/src/common/errors.ts b/web/src/common/errors.ts index 7ef4a308c..ad6156bfa 100644 --- a/web/src/common/errors.ts +++ b/web/src/common/errors.ts @@ -1,3 +1,30 @@ +import { + GenericError, + GenericErrorFromJSON, + ResponseError, + ValidationError, + ValidationErrorFromJSON, +} from "@goauthentik/api"; + export class SentryIgnoredError extends Error {} export class NotFoundError extends Error {} export class RequestError extends Error {} + +export type APIErrorTypes = ValidationError | GenericError; + +export async function parseAPIError(error: Error): Promise { + if (!(error instanceof ResponseError)) { + return error; + } + if (error.response.status < 400 && error.response.status > 499) { + return error; + } + const body = await error.response.json(); + if (error.response.status === 400) { + return ValidationErrorFromJSON(body); + } + if (error.response.status === 403) { + return GenericErrorFromJSON(body); + } + return body; +} diff --git a/web/src/common/users.ts b/web/src/common/users.ts index 5378236ce..293047572 100644 --- a/web/src/common/users.ts +++ b/web/src/common/users.ts @@ -45,6 +45,7 @@ export function me(): Promise { username: "", name: "", settings: {}, + systemPermissions: [], }, }; if (ex.response?.status === 401 || ex.response?.status === 403) { diff --git a/web/src/elements/PageHeader.ts b/web/src/elements/PageHeader.ts index 1f7539085..3f187291b 100644 --- a/web/src/elements/PageHeader.ts +++ b/web/src/elements/PageHeader.ts @@ -13,7 +13,7 @@ import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { msg } from "@lit/localize"; import { CSSResult, TemplateResult, css, html } from "lit"; -import { customElement, property } from "lit/decorators.js"; +import { customElement, property, state } from "lit/decorators.js"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css"; @@ -55,6 +55,7 @@ export class PageHeader extends AKElement { @property() description?: string; + @state() _header = ""; static get styles(): CSSResult[] { diff --git a/web/src/elements/ak-locale-context/ak-locale-context.ts b/web/src/elements/ak-locale-context/ak-locale-context.ts index af69fbf19..912d6b711 100644 --- a/web/src/elements/ak-locale-context/ak-locale-context.ts +++ b/web/src/elements/ak-locale-context/ak-locale-context.ts @@ -74,7 +74,7 @@ export class LocaleContext extends LitElement { console.warn(`Received a non-custom event at EVENT_LOCALE_REQUEST: ${ev}`); return; } - console.log("Locale update request received."); + console.debug("authentik/locale: Locale update request received."); this.updateLocale(ev.detail.locale); } diff --git a/web/src/elements/charts/Chart.ts b/web/src/elements/charts/Chart.ts index eb92dbfc3..0501db22a 100644 --- a/web/src/elements/charts/Chart.ts +++ b/web/src/elements/charts/Chart.ts @@ -22,7 +22,7 @@ import { msg, str } from "@lit/localize"; import { CSSResult, TemplateResult, css, html } from "lit"; import { property, state } from "lit/decorators.js"; -import { UiThemeEnum } from "@goauthentik/api"; +import { ResponseError, UiThemeEnum } from "@goauthentik/api"; Chart.register(Legend, Tooltip); Chart.register(LineController, BarController, DoughnutController); @@ -65,6 +65,9 @@ export abstract class AKChart extends AKElement { @state() chart?: Chart; + @state() + error?: ResponseError; + @property() centerText?: string; @@ -129,19 +132,23 @@ export abstract class AKChart extends AKElement { } firstUpdated(): void { - this.apiRequest().then((r) => { - const canvas = this.shadowRoot?.querySelector("canvas"); - if (!canvas) { - console.warn("Failed to get canvas element"); - return; - } - const ctx = canvas.getContext("2d"); - if (!ctx) { - console.warn("failed to get 2d context"); - return; - } - this.chart = this.configureChart(r, ctx); - }); + this.apiRequest() + .then((r) => { + const canvas = this.shadowRoot?.querySelector("canvas"); + if (!canvas) { + console.warn("Failed to get canvas element"); + return; + } + const ctx = canvas.getContext("2d"); + if (!ctx) { + console.warn("failed to get 2d context"); + return; + } + this.chart = this.configureChart(r, ctx); + }) + .catch((exc: ResponseError) => { + this.error = exc; + }); } getChartType(): string { @@ -204,7 +211,15 @@ export abstract class AKChart extends AKElement { render(): TemplateResult { return html`
    - ${this.chart ? html`` : html``} + ${this.error + ? html` + +

    ${this.error.response.statusText}

    +
    + ` + : html`${this.chart + ? html`` + : html``}`} ${this.centerText ? html` ${this.centerText} ` : html``}
    diff --git a/web/src/elements/forms/DeleteBulkForm.ts b/web/src/elements/forms/DeleteBulkForm.ts index 5d56f19c0..693184b71 100644 --- a/web/src/elements/forms/DeleteBulkForm.ts +++ b/web/src/elements/forms/DeleteBulkForm.ts @@ -20,7 +20,6 @@ type BulkDeleteMetadata = { key: string; value: string }[]; @customElement("ak-delete-objects-table") export class DeleteObjectsTable extends Table { - expandable = true; paginated = false; @property({ attribute: false }) @@ -70,6 +69,11 @@ export class DeleteObjectsTable extends Table { return html``; } + firstUpdated(): void { + this.expandable = this.usedBy !== undefined; + super.firstUpdated(); + } + renderExpanded(item: T): TemplateResult { const handler = async () => { if (!this.usedByData.has(item) && this.usedBy) { diff --git a/web/src/elements/rbac/ObjectPermissionModal.ts b/web/src/elements/rbac/ObjectPermissionModal.ts new file mode 100644 index 000000000..596b2b2c5 --- /dev/null +++ b/web/src/elements/rbac/ObjectPermissionModal.ts @@ -0,0 +1,74 @@ +import { AKElement } from "@goauthentik/app/elements/Base"; +import "@goauthentik/elements/forms/ModalForm"; +import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/rbac/ObjectPermissionsPage"; + +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import PFButton from "@patternfly/patternfly/components/Button/button.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +import { RbacPermissionsAssignedByUsersListModelEnum } from "@goauthentik/api"; + +/** + * This is a bit of a hack to get the viewport checking from ModelForm, + * even though we actually don't need a form here. + * #TODO: Rework this in the future + */ +@customElement("ak-rbac-object-permission-modal-form") +export class ObjectPermissionsPageForm extends ModelForm { + @property() + model?: RbacPermissionsAssignedByUsersListModelEnum; + + @property() + objectPk?: string | number; + + loadInstance(): Promise { + return Promise.resolve(); + } + send(): Promise { + return Promise.resolve(); + } + + renderForm(): TemplateResult { + return html` + `; + } +} + +@customElement("ak-rbac-object-permission-modal") +export class ObjectPermissionModal extends AKElement { + @property() + model?: RbacPermissionsAssignedByUsersListModelEnum; + + @property() + objectPk?: string | number; + + static get styles(): CSSResult[] { + return [PFBase, PFButton]; + } + + render(): TemplateResult { + return html` + + ${msg("Update Permissions")} + + + + `; + } +} diff --git a/web/src/elements/rbac/ObjectPermissionsPage.ts b/web/src/elements/rbac/ObjectPermissionsPage.ts new file mode 100644 index 000000000..5d0110bd8 --- /dev/null +++ b/web/src/elements/rbac/ObjectPermissionsPage.ts @@ -0,0 +1,68 @@ +import { AKElement } from "@goauthentik/app/elements/Base"; +import "@goauthentik/app/elements/rbac/RoleObjectPermissionTable"; +import "@goauthentik/app/elements/rbac/UserObjectPermissionTable"; +import "@goauthentik/elements/Tabs"; + +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import PFCard from "@patternfly/patternfly/components/Card/card.css"; +import PFPage from "@patternfly/patternfly/components/Page/page.css"; +import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +import { RbacPermissionsAssignedByUsersListModelEnum } from "@goauthentik/api"; + +@customElement("ak-rbac-object-permission-page") +export class ObjectPermissionPage extends AKElement { + @property() + model?: RbacPermissionsAssignedByUsersListModelEnum; + + @property() + objectPk?: string | number; + + static get styles(): CSSResult[] { + return [PFBase, PFGrid, PFPage, PFCard]; + } + render(): TemplateResult { + return html` +
    +
    +
    +
    User Object Permissions
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    Role Object Permissions
    +
    + + +
    +
    +
    +
    +
    `; + } +} diff --git a/web/src/elements/rbac/PermissionSelectModal.ts b/web/src/elements/rbac/PermissionSelectModal.ts new file mode 100644 index 000000000..648d88f17 --- /dev/null +++ b/web/src/elements/rbac/PermissionSelectModal.ts @@ -0,0 +1,95 @@ +import { groupBy } from "@goauthentik/app/common/utils"; +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import { uiConfig } from "@goauthentik/common/ui/config"; +import "@goauthentik/elements/buttons/SpinnerButton"; +import { PaginatedResponse } from "@goauthentik/elements/table/Table"; +import { TableColumn } from "@goauthentik/elements/table/Table"; +import { TableModal } from "@goauthentik/elements/table/TableModal"; + +import { msg } from "@lit/localize"; +import { CSSResult, TemplateResult, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import PFBanner from "@patternfly/patternfly/components/Banner/banner.css"; + +import { Permission, RbacApi } from "@goauthentik/api"; + +@customElement("ak-rbac-permission-select-table") +export class PermissionSelectModal extends TableModal { + checkbox = true; + checkboxChip = true; + + searchEnabled(): boolean { + return true; + } + + @property() + confirm!: (selectedItems: Permission[]) => Promise; + + order = "content_type__app_label,content_type__model"; + + static get styles(): CSSResult[] { + return super.styles.concat(PFBanner); + } + + async apiEndpoint(page: number): Promise> { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + ordering: this.order, + page: page, + pageSize: (await uiConfig()).pagination.perPage, + search: this.search || "", + }); + } + + groupBy(items: Permission[]): [string, Permission[]][] { + return groupBy(items, (perm) => { + return perm.appLabelVerbose; + }); + } + + columns(): TableColumn[] { + return [new TableColumn(msg("Name"), "codename"), new TableColumn(msg("Model"), "")]; + } + + row(item: Permission): TemplateResult[] { + return [ + html`
    +
    ${item.name}
    +
    `, + html`${item.modelVerbose}`, + ]; + } + + renderSelectedChip(item: Permission): TemplateResult { + return html`${item.name}`; + } + + renderModalInner(): TemplateResult { + return html`
    +
    +

    ${msg("Select permissions to grant")}

    +
    +
    +
    ${this.renderTable()}
    +
    + { + return this.confirm(this.selectedElements).then(() => { + this.open = false; + }); + }} + class="pf-m-primary" + > + ${msg("Add")}   + { + this.open = false; + }} + class="pf-m-secondary" + > + ${msg("Cancel")} + +
    `; + } +} diff --git a/web/src/elements/rbac/RoleObjectPermissionForm.ts b/web/src/elements/rbac/RoleObjectPermissionForm.ts new file mode 100644 index 000000000..230d30a2e --- /dev/null +++ b/web/src/elements/rbac/RoleObjectPermissionForm.ts @@ -0,0 +1,107 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/components/ak-toggle-group"; +import "@goauthentik/elements/forms/HorizontalFormElement"; +import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/forms/Radio"; +import "@goauthentik/elements/forms/SearchSelect"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; + +import { + ModelEnum, + PaginatedPermissionList, + RbacApi, + RbacRolesListRequest, + Role, +} from "@goauthentik/api"; + +interface RoleAssignData { + role: string; + permissions: { + [key: string]: boolean; + }; +} + +@customElement("ak-rbac-role-object-permission-form") +export class RoleObjectPermissionForm extends ModelForm { + @property() + model?: ModelEnum; + + @property() + objectPk?: string; + + @state() + modelPermissions?: PaginatedPermissionList; + + async load(): Promise { + const [appLabel, modelName] = (this.model || "").split("."); + this.modelPermissions = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + contentTypeModel: modelName, + contentTypeAppLabel: appLabel, + ordering: "codename", + }); + } + + loadInstance(): Promise { + throw new Error("Method not implemented."); + } + + getSuccessMessage(): string { + return msg("Successfully assigned permission."); + } + + send(data: RoleAssignData): Promise { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssignCreate({ + uuid: data.role, + permissionAssignRequest: { + permissions: Object.keys(data.permissions).filter((key) => data.permissions[key]), + model: this.model!, + objectPk: this.objectPk, + }, + }); + } + + renderForm(): TemplateResult { + if (!this.modelPermissions) { + return html``; + } + return html`
    + + => { + const args: RbacRolesListRequest = { + ordering: "name", + }; + if (query !== undefined) { + args.search = query; + } + const roles = await new RbacApi(DEFAULT_CONFIG).rbacRolesList(args); + return roles.results; + }} + .renderElement=${(role: Role): string => { + return role.name; + }} + .value=${(role: Role | undefined): string | undefined => { + return role?.pk; + }} + > + + + ${this.modelPermissions?.results.map((perm) => { + return html` + + `; + })} +
    `; + } +} diff --git a/web/src/elements/rbac/RoleObjectPermissionTable.ts b/web/src/elements/rbac/RoleObjectPermissionTable.ts new file mode 100644 index 000000000..45a807495 --- /dev/null +++ b/web/src/elements/rbac/RoleObjectPermissionTable.ts @@ -0,0 +1,97 @@ +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/app/elements/table/Table"; +import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/RoleObjectPermissionForm"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { + PaginatedPermissionList, + RbacApi, + RbacPermissionsAssignedByRolesListModelEnum, + RoleAssignedObjectPermission, +} from "@goauthentik/api"; + +@customElement("ak-rbac-role-object-permission-table") +export class RoleAssignedObjectPermissionTable extends Table { + @property() + model?: RbacPermissionsAssignedByRolesListModelEnum; + + @property() + objectPk?: string | number; + + @state() + modelPermissions?: PaginatedPermissionList; + + async apiEndpoint(page: number): Promise> { + const perms = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesList({ + page: page, + // TODO: better default + model: this.model || RbacPermissionsAssignedByRolesListModelEnum.CoreUser, + objectPk: this.objectPk?.toString(), + }); + const [appLabel, modelName] = (this.model || "").split("."); + const modelPermissions = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + contentTypeModel: modelName, + contentTypeAppLabel: appLabel, + ordering: "codename", + }); + modelPermissions.results = modelPermissions.results.filter((value) => { + return !value.codename.startsWith("add_"); + }); + this.modelPermissions = modelPermissions; + return perms; + } + + columns(): TableColumn[] { + const baseColumns = [new TableColumn("User", "user")]; + // We don't check pagination since models shouldn't need to have that many permissions? + this.modelPermissions?.results.forEach((perm) => { + baseColumns.push(new TableColumn(perm.name, perm.codename)); + }); + return baseColumns; + } + + renderObjectCreate(): TemplateResult { + return html` + ${msg("Assign")} + ${msg("Assign permission to role")} + + + + `; + } + + row(item: RoleAssignedObjectPermission): TemplateResult[] { + const baseRow = [html` ${item.name}`]; + this.modelPermissions?.results.forEach((perm) => { + const granted = + item.permissions.filter((uperm) => uperm.codename === perm.codename).length > 0; + baseRow.push(html` + { + console.log(granted); + }} + class="pf-m-link" + > + ${granted + ? html`` + : html`X`} + + `); + }); + return baseRow; + } +} diff --git a/web/src/elements/rbac/UserObjectPermissionForm.ts b/web/src/elements/rbac/UserObjectPermissionForm.ts new file mode 100644 index 000000000..3b3b66402 --- /dev/null +++ b/web/src/elements/rbac/UserObjectPermissionForm.ts @@ -0,0 +1,111 @@ +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/components/ak-toggle-group"; +import "@goauthentik/elements/forms/HorizontalFormElement"; +import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; +import "@goauthentik/elements/forms/Radio"; +import "@goauthentik/elements/forms/SearchSelect"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; + +import { + CoreApi, + CoreUsersListRequest, + ModelEnum, + PaginatedPermissionList, + RbacApi, + User, +} from "@goauthentik/api"; + +interface UserAssignData { + user: number; + permissions: { + [key: string]: boolean; + }; +} + +@customElement("ak-rbac-user-object-permission-form") +export class UserObjectPermissionForm extends ModelForm { + @property() + model?: ModelEnum; + + @property() + objectPk?: string; + + @state() + modelPermissions?: PaginatedPermissionList; + + async load(): Promise { + const [appLabel, modelName] = (this.model || "").split("."); + this.modelPermissions = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + contentTypeModel: modelName, + contentTypeAppLabel: appLabel, + ordering: "codename", + }); + } + + loadInstance(): Promise { + throw new Error("Method not implemented."); + } + + getSuccessMessage(): string { + return msg("Successfully assigned permission."); + } + + send(data: UserAssignData): Promise { + return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssignCreate({ + id: data.user, + permissionAssignRequest: { + permissions: Object.keys(data.permissions).filter((key) => data.permissions[key]), + model: this.model!, + objectPk: this.objectPk!, + }, + }); + } + + renderForm(): TemplateResult { + if (!this.modelPermissions) { + return html``; + } + return html`
    + + => { + const args: CoreUsersListRequest = { + ordering: "username", + }; + if (query !== undefined) { + args.search = query; + } + const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args); + return users.results; + }} + .renderElement=${(user: User): string => { + return user.username; + }} + .renderDescription=${(user: User): TemplateResult => { + return html`${user.name}`; + }} + .value=${(user: User | undefined): number | undefined => { + return user?.pk; + }} + > + + + ${this.modelPermissions?.results.map((perm) => { + return html` + + `; + })} +
    `; + } +} diff --git a/web/src/elements/rbac/UserObjectPermissionTable.ts b/web/src/elements/rbac/UserObjectPermissionTable.ts new file mode 100644 index 000000000..3c52ca1e6 --- /dev/null +++ b/web/src/elements/rbac/UserObjectPermissionTable.ts @@ -0,0 +1,90 @@ +import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config"; +import { PaginatedResponse, Table, TableColumn } from "@goauthentik/app/elements/table/Table"; +import "@goauthentik/elements/forms/ModalForm"; +import "@goauthentik/elements/rbac/UserObjectPermissionForm"; +import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { ifDefined } from "lit/directives/if-defined.js"; + +import { + PaginatedPermissionList, + RbacApi, + RbacPermissionsAssignedByUsersListModelEnum, + UserAssignedObjectPermission, +} from "@goauthentik/api"; + +@customElement("ak-rbac-user-object-permission-table") +export class UserAssignedObjectPermissionTable extends Table { + @property() + model?: RbacPermissionsAssignedByUsersListModelEnum; + + @property() + objectPk?: string | number; + + @state() + modelPermissions?: PaginatedPermissionList; + + async apiEndpoint(page: number): Promise> { + const perms = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersList({ + page: page, + // TODO: better default + model: this.model || RbacPermissionsAssignedByUsersListModelEnum.CoreUser, + objectPk: this.objectPk?.toString(), + }); + const [appLabel, modelName] = (this.model || "").split("."); + const modelPermissions = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ + contentTypeModel: modelName, + contentTypeAppLabel: appLabel, + ordering: "codename", + }); + modelPermissions.results = modelPermissions.results.filter((value) => { + return !value.codename.startsWith("add_"); + }); + this.modelPermissions = modelPermissions; + return perms; + } + + columns(): TableColumn[] { + const baseColumns = [new TableColumn("User", "user")]; + // We don't check pagination since models shouldn't need to have that many permissions? + this.modelPermissions?.results.forEach((perm) => { + baseColumns.push(new TableColumn(perm.name, perm.codename)); + }); + return baseColumns; + } + + renderObjectCreate(): TemplateResult { + return html` + ${msg("Assign")} + ${msg("Assign permission to user")} + + + + `; + } + + row(item: UserAssignedObjectPermission): TemplateResult[] { + const baseRow = [html` ${item.username} `]; + this.modelPermissions?.results.forEach((perm) => { + let cell = html`X`; + if (item.permissions.filter((uperm) => uperm.codename === perm.codename).length > 0) { + cell = html``; + } else if (item.isSuperuser) { + cell = html``; + } + baseRow.push(cell); + }); + return baseRow; + } +} diff --git a/web/src/elements/table/Table.ts b/web/src/elements/table/Table.ts index b9cf20b14..642af4801 100644 --- a/web/src/elements/table/Table.ts +++ b/web/src/elements/table/Table.ts @@ -1,3 +1,4 @@ +import { APIErrorTypes, parseAPIError } from "@goauthentik/app/common/errors"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { groupBy } from "@goauthentik/common/utils"; import { AKElement } from "@goauthentik/elements/Base"; @@ -148,7 +149,7 @@ export abstract class Table extends AKElement { expandedElements: T[] = []; @state() - hasError?: Error; + error?: APIErrorTypes; static get styles(): CSSResult[] { return [ @@ -191,7 +192,7 @@ export abstract class Table extends AKElement { this.isLoading = true; try { this.data = await this.apiEndpoint(this.page); - this.hasError = undefined; + this.error = undefined; this.page = this.data.pagination.current; const newSelected: T[] = []; const newExpanded: T[] = []; @@ -228,7 +229,7 @@ export abstract class Table extends AKElement { this.expandedElements = newExpanded; } catch (ex) { this.isLoading = false; - this.hasError = ex as Error; + this.error = await parseAPIError(ex as Error); } } @@ -249,25 +250,32 @@ export abstract class Table extends AKElement {
    ${inner ? inner - : html``} + : html`
    ${this.renderObjectCreate()}
    +
    `}
    `; } + renderObjectCreate(): TemplateResult { + return html``; + } + renderError(): TemplateResult { + if (!this.error) { + return html``; + } return html` - ${this.hasError instanceof ResponseError - ? html`
    ${this.hasError.message}
    ` - : html`
    ${this.hasError?.toString()}
    `} + ${this.error instanceof ResponseError + ? html`
    ${this.error.message}
    ` + : html`
    ${this.error.detail}
    `}
    `; } private renderRows(): TemplateResult[] | undefined { - if (this.hasError) { + if (this.error) { return [this.renderEmpty(this.renderError())]; } if (!this.data || this.isLoading) { @@ -277,7 +285,7 @@ export abstract class Table extends AKElement { return [this.renderEmpty()]; } const groupedResults = this.groupBy(this.data.results); - if (groupedResults.length === 1) { + if (groupedResults.length === 1 && groupedResults[0][0] === "") { return this.renderRowGroup(groupedResults[0][1]); } return groupedResults.map(([group, items]) => { @@ -397,14 +405,15 @@ export abstract class Table extends AKElement { } renderToolbar(): TemplateResult { - return html` { - return this.fetch(); - }} - class="pf-m-secondary" - > - ${msg("Refresh")}`; + return html` ${this.renderObjectCreate()} + { + return this.fetch(); + }} + class="pf-m-secondary" + > + ${msg("Refresh")}`; } renderToolbarSelected(): TemplateResult { @@ -419,18 +428,20 @@ export abstract class Table extends AKElement { if (!this.searchEnabled()) { return html``; } - return html` { - this.search = value; - this.fetch(); - updateURLParams({ - search: value, - }); - }} - > - `; + return html`
    + { + this.search = value; + this.fetch(); + updateURLParams({ + search: value, + }); + }} + > + +
    `; } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -441,7 +452,7 @@ export abstract class Table extends AKElement { renderToolbarContainer(): TemplateResult { return html`
    -
    ${this.renderSearch()}
    + ${this.renderSearch()}
    ${this.renderToolbar()}
    ${this.renderToolbarAfter()}
    ${this.renderToolbarSelected()}
    diff --git a/web/src/elements/table/TablePage.ts b/web/src/elements/table/TablePage.ts index 8ca297fe6..d3f207622 100644 --- a/web/src/elements/table/TablePage.ts +++ b/web/src/elements/table/TablePage.ts @@ -70,14 +70,6 @@ export abstract class TablePage extends Table { `; } - renderObjectCreate(): TemplateResult { - return html``; - } - - renderToolbar(): TemplateResult { - return html`${this.renderObjectCreate()}${super.renderToolbar()}`; - } - render(): TemplateResult { return html` { @@ -24,7 +28,7 @@ export class ConsentStage extends BaseStage { if (permission.name === "") { return html``; diff --git a/web/src/user/UserInterface.ts b/web/src/user/UserInterface.ts index 0d83982a1..fbd21d347 100644 --- a/web/src/user/UserInterface.ts +++ b/web/src/user/UserInterface.ts @@ -175,6 +175,10 @@ export class UserInterface extends Interface { default: userDisplay = this.me.user.username; } + const canAccessAdmin = + this.me.user.isSuperuser || + // TODO: somehow add `access_admin_interface` to the API schema + this.me.user.systemPermissions.includes("access_admin_interface"); return html`
    @@ -280,7 +284,7 @@ export class UserInterface extends Interface {
    - ${this.me.user.isSuperuser + ${canAccessAdmin ? html` Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/en.xlf b/web/xliff/en.xlf index 98ce779f9..0e80e8b12 100644 --- a/web/xliff/en.xlf +++ b/web/xliff/en.xlf @@ -6252,6 +6252,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/es.xlf b/web/xliff/es.xlf index b428ab068..d9fc14013 100644 --- a/web/xliff/es.xlf +++ b/web/xliff/es.xlf @@ -5846,6 +5846,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf index 0174261e0..178e43b15 100644 --- a/web/xliff/fr.xlf +++ b/web/xliff/fr.xlf @@ -7827,6 +7827,111 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/pl.xlf b/web/xliff/pl.xlf index ad3cb85ca..0f43daabe 100644 --- a/web/xliff/pl.xlf +++ b/web/xliff/pl.xlf @@ -6085,6 +6085,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/pseudo-LOCALE.xlf b/web/xliff/pseudo-LOCALE.xlf index 2eabcf7c5..b54aac0a8 100644 --- a/web/xliff/pseudo-LOCALE.xlf +++ b/web/xliff/pseudo-LOCALE.xlf @@ -6187,6 +6187,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/tr.xlf b/web/xliff/tr.xlf index deaddd663..a2d79415f 100644 --- a/web/xliff/tr.xlf +++ b/web/xliff/tr.xlf @@ -5839,6 +5839,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/zh-Hans.xlf b/web/xliff/zh-Hans.xlf index 9fb60a94a..cf3cfab8d 100644 --- a/web/xliff/zh-Hans.xlf +++ b/web/xliff/zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -613,9 +613,9 @@ - The URL "" was not found. - 未找到 URL " - "。 + The URL "" was not found. + 未找到 URL " + "。 @@ -1067,8 +1067,8 @@ - To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. - 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 @@ -1809,8 +1809,8 @@ - Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". - 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 @@ -3233,8 +3233,8 @@ doesn't pass when either or both of the selected options are equal or above the - Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' - 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' + Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' @@ -4026,8 +4026,8 @@ doesn't pass when either or both of the selected options are equal or above the - When using an external logging solution for archiving, this can be set to "minutes=5". - 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 + When using an external logging solution for archiving, this can be set to "minutes=5". + 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 @@ -4036,8 +4036,8 @@ doesn't pass when either or both of the selected options are equal or above the - Format: "weeks=3;days=2;hours=3,seconds=2". - 格式:"weeks=3;days=2;hours=3,seconds=2"。 + Format: "weeks=3;days=2;hours=3,seconds=2". + 格式:"weeks=3;days=2;hours=3,seconds=2"。 @@ -4233,10 +4233,10 @@ doesn't pass when either or both of the selected options are equal or above the - Are you sure you want to update ""? + Are you sure you want to update ""? 您确定要更新 - " - " 吗? + " + " 吗? @@ -5332,7 +5332,7 @@ doesn't pass when either or both of the selected options are equal or above the - A "roaming" authenticator, like a YubiKey + A "roaming" authenticator, like a YubiKey 像 YubiKey 这样的“漫游”身份验证器 @@ -5667,10 +5667,10 @@ doesn't pass when either or both of the selected options are equal or above the - ("", of type ) + ("", of type ) - (" - ",类型为 + (" + ",类型为 @@ -5719,7 +5719,7 @@ doesn't pass when either or both of the selected options are equal or above the - If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. 如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。 @@ -7836,7 +7836,112 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. 不要再显示此消息。 + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info - \ No newline at end of file + diff --git a/web/xliff/zh-Hant.xlf b/web/xliff/zh-Hant.xlf index ddb47517e..8cfcc6044 100644 --- a/web/xliff/zh-Hant.xlf +++ b/web/xliff/zh-Hant.xlf @@ -5891,6 +5891,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info diff --git a/web/xliff/zh_TW.xlf b/web/xliff/zh_TW.xlf index 5aa3bc105..8f95191e6 100644 --- a/web/xliff/zh_TW.xlf +++ b/web/xliff/zh_TW.xlf @@ -5890,6 +5890,111 @@ Bindings to groups/users are checked against the user of the event. Don't show this message again. + + + Failed to fetch + + + Failed to fetch data. + + + Successfully assigned permission. + + + Role + + + Assign + + + Assign permission to role + + + Assign to new role + + + Directly assigned + + + Assign permission to user + + + Assign to new user + + + User Object Permissions + + + Role Object Permissions + + + Roles + + + Select roles to grant this groups' users' permissions from the selected roles. + + + Update Permissions + + + Editing is disabled for managed tokens + + + Select permissions to grant + + + Permissions to add + + + Select permissions + + + Assign permission + + + Permission(s) + + + Permission + + + User doesn't have view permission so description cannot be retrieved. + + + Assigned permissions + + + Assigned global permissions + + + Assigned object permissions + + + Successfully updated role. + + + Successfully created role. + + + Manage roles which grant permissions to objects within authentik. + + + Role(s) + + + Update Role + + + Create Role + + + Role doesn't have view permission so description cannot be retrieved. + + + Role + + + Role Info From 3cda19f61b4844dc8789ac2e8bc1bc1c0eb764db Mon Sep 17 00:00:00 2001 From: "authentik-automation[bot]" <135050075+authentik-automation[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:48:41 +0000 Subject: [PATCH 14/23] web: bump API Client version (#7186) Signed-off-by: GitHub Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com> --- web/package-lock.json | 8 ++++---- web/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index e205d71be..b566f4828 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -17,7 +17,7 @@ "@codemirror/theme-one-dark": "^6.1.2", "@formatjs/intl-listformat": "^7.5.0", "@fortawesome/fontawesome-free": "^6.4.2", - "@goauthentik/api": "^2023.8.3-1696847703", + "@goauthentik/api": "^2023.8.3-1697470337", "@lit-labs/context": "^0.4.1", "@lit-labs/task": "^3.1.0", "@lit/localize": "^0.11.4", @@ -2882,9 +2882,9 @@ } }, "node_modules/@goauthentik/api": { - "version": "2023.8.3-1696847703", - "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.8.3-1696847703.tgz", - "integrity": "sha512-RsOANX4L6RHaGXvMhJNq9g+E0ZLW3cwgl/t5CyQxLYvWgmVvZU4t78hxlOF7vFREoO5nhZUZnOOlD2+n5gOqLg==" + "version": "2023.8.3-1697470337", + "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.8.3-1697470337.tgz", + "integrity": "sha512-LHFqHXOR4dkVnI2EKRLLUyFQxdfHfxvYnbu/caFNlmrFeAQ2T/KYiOfTcWAvHIQ/unK9STF5EAzeFJ18m6RIdQ==" }, "node_modules/@hcaptcha/types": { "version": "1.0.3", diff --git a/web/package.json b/web/package.json index 76e1ed5bb..4c4a14e90 100644 --- a/web/package.json +++ b/web/package.json @@ -35,7 +35,7 @@ "@codemirror/theme-one-dark": "^6.1.2", "@formatjs/intl-listformat": "^7.5.0", "@fortawesome/fontawesome-free": "^6.4.2", - "@goauthentik/api": "^2023.8.3-1696847703", + "@goauthentik/api": "^2023.8.3-1697470337", "@lit-labs/context": "^0.4.1", "@lit-labs/task": "^3.1.0", "@lit/localize": "^0.11.4", From 6deb231e0e760b136c1bb6db19c73064b53fc05c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:07:17 +0200 Subject: [PATCH 15/23] website: bump @babel/traverse from 7.21.4 to 7.23.2 in /website (#7187) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.4 to 7.23.2. - [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.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- website/package-lock.json | 357 +++++++++++++++++++++++++------------- 1 file changed, 237 insertions(+), 120 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index e7ecfe5f4..4ccdec6fd 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -211,16 +211,81 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", @@ -267,11 +332,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -390,9 +455,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } @@ -409,23 +474,23 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -546,28 +611,28 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -608,12 +673,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -685,9 +750,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1935,31 +2000,31 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1968,12 +2033,12 @@ } }, "node_modules/@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -14004,11 +14069,63 @@ } }, "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -14046,11 +14163,11 @@ } }, "@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "requires": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -14137,9 +14254,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-explode-assignable-expression": { "version": "7.18.6", @@ -14150,20 +14267,20 @@ } }, "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -14251,22 +14368,22 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.21.0", @@ -14295,12 +14412,12 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -14356,9 +14473,9 @@ } }, "@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -15166,39 +15283,39 @@ } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, From 0697e3d5a402f8396be27e975378e75150f63643 Mon Sep 17 00:00:00 2001 From: Jens L Date: Mon, 16 Oct 2023 19:42:19 +0200 Subject: [PATCH 16/23] rbac: revisions (#7188) * improve system migration logging Signed-off-by: Jens Langhammer * fix filter for internal service accounts Signed-off-by: Jens Langhammer * merge migration Signed-off-by: Jens Langhammer * bump go api Signed-off-by: Jens Langhammer * sources/ldap: check if we need to connect to ldap before connecting Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- authentik/rbac/filters.py | 7 +++++++ authentik/rbac/migrations/0002_systempermission.py | 2 ++ authentik/sources/ldap/password.py | 5 +++++ authentik/sources/ldap/signals.py | 9 ++++++--- go.mod | 2 +- go.sum | 4 ++-- lifecycle/migrate.py | 8 ++++---- .../{to_0_14_events..py => to_0_14_events.py} | 0 8 files changed, 27 insertions(+), 10 deletions(-) rename lifecycle/system_migrations/{to_0_14_events..py => to_0_14_events.py} (100%) diff --git a/authentik/rbac/filters.py b/authentik/rbac/filters.py index 395efd673..22f5a768e 100644 --- a/authentik/rbac/filters.py +++ b/authentik/rbac/filters.py @@ -4,6 +4,8 @@ from rest_framework.exceptions import PermissionDenied from rest_framework.request import Request from rest_framework_guardian.filters import ObjectPermissionsFilter +from authentik.core.models import UserTypes + class ObjectFilter(ObjectPermissionsFilter): """Object permission filter that grants global permission higher priority than @@ -19,6 +21,11 @@ class ObjectFilter(ObjectPermissionsFilter): if request.user.has_perm(permission): return queryset queryset = super().filter_queryset(request, queryset, view) + # Outposts (which are the only objects using internal service accounts) + # except requests to return an empty list when they have no objects + # assigned + if request.user.type == UserTypes.INTERNAL_SERVICE_ACCOUNT: + return queryset if not queryset.exists(): # User doesn't have direct permission to all objects # and also no object permissions assigned (directly or via role) diff --git a/authentik/rbac/migrations/0002_systempermission.py b/authentik/rbac/migrations/0002_systempermission.py index eb4a03bb4..8a08c09fb 100644 --- a/authentik/rbac/migrations/0002_systempermission.py +++ b/authentik/rbac/migrations/0002_systempermission.py @@ -26,6 +26,8 @@ class Migration(migrations.Migration): ("run_system_tasks", "Can run system tasks"), ("access_admin_interface", "Can access admin interface"), ], + "verbose_name": "System permission", + "verbose_name_plural": "System permissions", "managed": False, "default_permissions": (), }, diff --git a/authentik/sources/ldap/password.py b/authentik/sources/ldap/password.py index 43b09befc..d4e0dff4e 100644 --- a/authentik/sources/ldap/password.py +++ b/authentik/sources/ldap/password.py @@ -49,6 +49,11 @@ class LDAPPasswordChanger: self._source = source self._connection = source.connection() + @staticmethod + def should_check_user(user: User) -> bool: + """Check if the user has LDAP parameters and needs to be checked""" + return LDAP_DISTINGUISHED_NAME in user.attributes + def get_domain_root_dn(self) -> str: """Attempt to get root DN via MS specific fields or generic LDAP fields""" info = self._connection.server.info diff --git a/authentik/sources/ldap/signals.py b/authentik/sources/ldap/signals.py index a5f7ea037..5af97376d 100644 --- a/authentik/sources/ldap/signals.py +++ b/authentik/sources/ldap/signals.py @@ -41,11 +41,12 @@ def ldap_password_validate(sender, password: str, plan_context: dict[str, Any], if not sources.exists(): return source = sources.first() + user = plan_context.get(PLAN_CONTEXT_PENDING_USER, None) + if user and not LDAPPasswordChanger.should_check_user(user): + return changer = LDAPPasswordChanger(source) if changer.check_ad_password_complexity_enabled(): - passing = changer.ad_password_complexity( - password, plan_context.get(PLAN_CONTEXT_PENDING_USER, None) - ) + passing = changer.ad_password_complexity(password, user) if not passing: raise ValidationError(_("Password does not match Active Directory Complexity.")) @@ -57,6 +58,8 @@ def ldap_sync_password(sender, user: User, password: str, **_): if not sources.exists(): return source = sources.first() + if not LDAPPasswordChanger.should_check_user(user): + return try: changer = LDAPPasswordChanger(source) changer.change_password(user, password) diff --git a/go.mod b/go.mod index 0b26945b5..5f5394955 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 - goauthentik.io/api/v3 v3.2023083.6 + goauthentik.io/api/v3 v3.2023083.7 golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/oauth2 v0.13.0 golang.org/x/sync v0.4.0 diff --git a/go.sum b/go.sum index d9899e759..75d8b4a2d 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyK go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -goauthentik.io/api/v3 v3.2023083.6 h1:VYVnE/3CYhggmobeZ+V3ka0TwswrUhKasxwGPmXTq0M= -goauthentik.io/api/v3 v3.2023083.6/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= +goauthentik.io/api/v3 v3.2023083.7 h1:/nS5Cgg+daTmsHVoFNxANLUQXVsJMAu4U8P7OyxeZf0= +goauthentik.io/api/v3 v3.2023083.7/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= diff --git a/lifecycle/migrate.py b/lifecycle/migrate.py index 68d45dbc3..4476a4681 100755 --- a/lifecycle/migrate.py +++ b/lifecycle/migrate.py @@ -81,8 +81,8 @@ if __name__ == "__main__": ) curr = conn.cursor() try: - for migration in Path(__file__).parent.absolute().glob("system_migrations/*.py"): - spec = spec_from_file_location("lifecycle.system_migrations", migration) + for migration_path in Path(__file__).parent.absolute().glob("system_migrations/*.py"): + spec = spec_from_file_location("lifecycle.system_migrations", migration_path) if not spec: continue mod = module_from_spec(spec) @@ -94,9 +94,9 @@ if __name__ == "__main__": migration = sub(curr, conn) if migration.needs_migration(): wait_for_lock() - LOGGER.info("Migration needs to be applied", migration=sub) + LOGGER.info("Migration needs to be applied", migration=migration_path.name) migration.run() - LOGGER.info("Migration finished applying", migration=sub) + LOGGER.info("Migration finished applying", migration=migration_path.name) release_lock() LOGGER.info("applying django migrations") environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") diff --git a/lifecycle/system_migrations/to_0_14_events..py b/lifecycle/system_migrations/to_0_14_events.py similarity index 100% rename from lifecycle/system_migrations/to_0_14_events..py rename to lifecycle/system_migrations/to_0_14_events.py From 9e568e1e85da75a9ff411173b9c1a452544145e8 Mon Sep 17 00:00:00 2001 From: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:54:43 -0700 Subject: [PATCH 17/23] web: the return of pseudolocalization (#7190) * web: the return of pseudolocalization The move to lit-locale lost the ability to automagically pseudolocalize the UI, a useful utility for checking that additions to the UI have been properly cataloged as translation targets. This short script (barely 40 lines) digs deep into the lit-localize toolkit and produces a pretranslated translation bundle in the target format folder. * Linted, prettied, and commented. --- web/.gitignore | 2 + web/package-lock.json | 25 + web/package.json | 4 + web/scripts/pseudolocalize.ts | 47 + .../elements/ak-locale-context/definitions.ts | 6 + web/xliff/pseudo-LOCALE.xlf | 1594 ++++++++++++++++- 6 files changed, 1674 insertions(+), 4 deletions(-) create mode 100644 web/scripts/pseudolocalize.ts diff --git a/web/.gitignore b/web/.gitignore index 5fcf65536..f11bf366d 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -109,3 +109,5 @@ temp/ # End of https://www.gitignore.io/api/node api/** storybook-static/ +scripts/*.mjs +scripts/*.js diff --git a/web/package-lock.json b/web/package-lock.json index b566f4828..645eccde8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -84,6 +84,7 @@ "lit-analyzer": "^1.2.1", "npm-run-all": "^4.1.5", "prettier": "^3.0.3", + "pseudolocale": "^2.0.0", "pyright": "^1.1.331", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -19295,6 +19296,30 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/pseudolocale": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-2.0.0.tgz", + "integrity": "sha512-g1K9tCQYY4e3UGtnW8qs3kGWAOONxt7i5wuOFvf3N1EIIRhiLVIhZ9AM/ZyGTxsp231JbFywJU/EbJ5ZoqnZdg==", + "dev": true, + "dependencies": { + "commander": "^10.0.0" + }, + "bin": { + "pseudolocale": "dist/cli.mjs" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pseudolocale/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", diff --git a/web/package.json b/web/package.json index 4c4a14e90..fcc5770bc 100644 --- a/web/package.json +++ b/web/package.json @@ -21,6 +21,9 @@ "precommit": "run-s tsc lit-analyse lint:precommit lint:spelling prettier", "prettier-check": "prettier --check .", "prettier": "prettier --write .", + "pseudolocalize:build-extract-script": "cd scripts && tsc --esModuleInterop --module es2020 --moduleResolution 'node' pseudolocalize.ts && mv pseudolocalize.js pseudolocalize.mjs", + "pseudolocalize:extract": "node scripts/pseudolocalize.mjs", + "pseudolocalize": "run-s pseudolocalize:build-extract-script pseudolocalize:extract", "tsc:execute": "tsc --noEmit -p .", "tsc": "run-s build-locales tsc:execute", "storybook": "storybook dev -p 6006", @@ -102,6 +105,7 @@ "lit-analyzer": "^1.2.1", "npm-run-all": "^4.1.5", "prettier": "^3.0.3", + "pseudolocale": "^2.0.0", "pyright": "^1.1.331", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/web/scripts/pseudolocalize.ts b/web/scripts/pseudolocalize.ts new file mode 100644 index 000000000..308632ff9 --- /dev/null +++ b/web/scripts/pseudolocalize.ts @@ -0,0 +1,47 @@ +import { readFileSync } from "fs"; +import path from "path"; +import pseudolocale from "pseudolocale"; +import { fileURLToPath } from "url"; + +import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js"; +import type { Message, ProgramMessage } from "@lit/localize-tools/lib/messages.d.ts"; +import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js"; +import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js"; +import type { Config } from "@lit/localize-tools/lib/types/config.d.ts"; +import type { Locale } from "@lit/localize-tools/lib/types/locale.d.ts"; +import type { TransformOutputConfig } from "@lit/localize-tools/lib/types/modes.d.ts"; + +const __dirname = fileURLToPath(new URL(".", import.meta.url)); +const pseudoLocale: Locale = "pseudo-LOCALE" as Locale; +const targetLocales: Locale[] = [pseudoLocale]; +const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8")); + +// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter +// which Localizer we use (transformer or runtime), because all of the functionality we care about +// is in their common parent class, but I had to pick one. Everything else here is just pure +// exploitation of the lit/localize-tools internals. + +const config: Config = { + ...baseConfig, + baseDir: path.join(__dirname, ".."), + targetLocales, + output: { + ...baseConfig, + mode: "transform", + }, + resolve: (path: string) => path, +} as Config; + +const pseudoMessagify = (message: ProgramMessage) => ({ + name: message.name, + contents: message.contents.map((content) => + typeof content === "string" ? pseudolocale(content, { prepend: "", append: "" }) : content, + ), +}); + +const localizer = new TransformLitLocalizer(config as Config & { output: TransformOutputConfig }); +const { messages } = localizer.extractSourceMessages(); +const translations = messages.map(pseudoMessagify); +const sorted = sortProgramMessages([...messages]); +const formatter = makeFormatter(config); +formatter.writeOutput(sorted, new Map([[pseudoLocale, translations]])); diff --git a/web/src/elements/ak-locale-context/definitions.ts b/web/src/elements/ak-locale-context/definitions.ts index 63ec25025..e920e85b1 100644 --- a/web/src/elements/ak-locale-context/definitions.ts +++ b/web/src/elements/ak-locale-context/definitions.ts @@ -35,6 +35,11 @@ export { enLocale }; // - Text Label // - Locale loader. +// prettier-ignore +const debug: LocaleRow = [ + "pseudo-LOCALE", /^pseudo/i, () => msg("Pseudolocale (for testing)"), async () => await import("@goauthentik/locales/pseudo-LOCALE"), +]; + // prettier-ignore const LOCALE_TABLE: LocaleRow[] = [ ["en", /^en([_-]|$)/i, () => msg("English"), async () => await import("@goauthentik/locales/en")], @@ -46,6 +51,7 @@ const LOCALE_TABLE: LocaleRow[] = [ ["zh-Hant", /^zh[_-](HK|Hant)/i, () => msg("Chinese (traditional)"), async () => await import("@goauthentik/locales/zh-Hant")], ["zh_TW", /^zh[_-]TW$/i, () => msg("Taiwanese Mandarin"), async () => await import("@goauthentik/locales/zh_TW")], ["zh-Hans", /^zh(\b|_)/i, () => msg("Chinese (simplified)"), async () => await import("@goauthentik/locales/zh-Hans")], + debug ]; export const LOCALES: AkLocale[] = LOCALE_TABLE.map(([code, match, label, locale]) => ({ diff --git a/web/xliff/pseudo-LOCALE.xlf b/web/xliff/pseudo-LOCALE.xlf index b54aac0a8..c4fa2d82f 100644 --- a/web/xliff/pseudo-LOCALE.xlf +++ b/web/xliff/pseudo-LOCALE.xlf @@ -4,6189 +4,7775 @@ English + Ēńĝĺĩśĥ French + Ƒŕēńćĥ Turkish + Ţũŕķĩśĥ Spanish + Śƥàńĩśĥ Polish + Ƥōĺĩśĥ Taiwanese Mandarin + Ţàĩŵàńēśē Màńďàŕĩń Chinese (simplified) + Ćĥĩńēśē (śĩmƥĺĩƒĩēď) Chinese (traditional) + Ćĥĩńēśē (ţŕàďĩţĩōńàĺ) German + Ĝēŕmàń Loading... + Ĺōàďĩńĝ... Application + Àƥƥĺĩćàţĩōń Logins + Ĺōĝĩńś Show less + Śĥōŵ ĺēśś Show more + Śĥōŵ mōŕē UID + ŨĨĎ Name + Ńàmē App + Àƥƥ Model Name + Mōďēĺ Ńàmē Message + Mēśśàĝē Subject + ŚũƀĴēćţ From + Ƒŕōm To + Ţō Context + Ćōńţēxţ User + Ũśēŕ Affected model: + Àƒƒēćţēď mōďēĺ: Authorized application: + Àũţĥōŕĩźēď àƥƥĺĩćàţĩōń: Using flow + Ũśĩńĝ ƒĺōŵ Email info: + Ēmàĩĺ ĩńƒō: Secret: + Śēćŕēţ: Open issue on GitHub... + Ōƥēń ĩśśũē ōń ĜĩţĤũƀ... Exception + Ēxćēƥţĩōń Expression + Ēxƥŕēśśĩōń Binding + ßĩńďĩńĝ Request + Ŕēǫũēśţ Object + ŌƀĴēćţ Result + Ŕēśũĺţ Passing + Ƥàśśĩńĝ Messages + Mēśśàĝēś Using source + Ũśĩńĝ śōũŕćē Attempted to log in as + Àţţēmƥţēď ţō ĺōĝ ĩń àś No additional data available. + Ńō àďďĩţĩōńàĺ ďàţà àvàĩĺàƀĺē. Click to change value + Ćĺĩćķ ţō ćĥàńĝē vàĺũē Select an object. + Śēĺēćţ àń ōƀĴēćţ. Loading options... + Ĺōàďĩńĝ ōƥţĩōńś... Connection error, reconnecting... + Ćōńńēćţĩōń ēŕŕōŕ, ŕēćōńńēćţĩńĝ... Login + Ĺōĝĩń Failed login + Ƒàĩĺēď ĺōĝĩń Logout + Ĺōĝōũţ User was written to + Ũśēŕ ŵàś ŵŕĩţţēń ţō Suspicious request + Śũśƥĩćĩōũś ŕēǫũēśţ Password set + Ƥàśśŵōŕď śēţ Secret was viewed + Śēćŕēţ ŵàś vĩēŵēď Secret was rotated + Śēćŕēţ ŵàś ŕōţàţēď Invitation used + Ĩńvĩţàţĩōń ũśēď Application authorized + Àƥƥĺĩćàţĩōń àũţĥōŕĩźēď Source linked + Śōũŕćē ĺĩńķēď Impersonation started + Ĩmƥēŕśōńàţĩōń śţàŕţēď Impersonation ended + Ĩmƥēŕśōńàţĩōń ēńďēď Flow execution + Ƒĺōŵ ēxēćũţĩōń Policy execution + Ƥōĺĩćŷ ēxēćũţĩōń Policy exception + Ƥōĺĩćŷ ēxćēƥţĩōń Property Mapping exception + Ƥŕōƥēŕţŷ Màƥƥĩńĝ ēxćēƥţĩōń System task execution + Śŷśţēm ţàśķ ēxēćũţĩōń System task exception + Śŷśţēm ţàśķ ēxćēƥţĩōń General system exception + Ĝēńēŕàĺ śŷśţēm ēxćēƥţĩōń Configuration error + Ćōńƒĩĝũŕàţĩōń ēŕŕōŕ Model created + Mōďēĺ ćŕēàţēď Model updated + Mōďēĺ ũƥďàţēď Model deleted + Mōďēĺ ďēĺēţēď Email sent + Ēmàĩĺ śēńţ Update available + Ũƥďàţē àvàĩĺàƀĺē Unknown severity + Ũńķńōŵń śēvēŕĩţŷ Alert + Àĺēŕţ Notice + Ńōţĩćē Warning + Ŵàŕńĩńĝ no tabs defined + ńō ţàƀś ďēƒĩńēď - of + - ōƒ Go to previous page + Ĝō ţō ƥŕēvĩōũś ƥàĝē Go to next page + Ĝō ţō ńēxţ ƥàĝē Search... + Śēàŕćĥ... Loading + Ĺōàďĩńĝ No objects found. + Ńō ōƀĴēćţś ƒōũńď. Failed to fetch objects. + Ƒàĩĺēď ţō ƒēţćĥ ōƀĴēćţś. Refresh + Ŕēƒŕēśĥ Select all rows + Śēĺēćţ àĺĺ ŕōŵś Action + Àćţĩōń Creation Date + Ćŕēàţĩōń Ďàţē Client IP + Ćĺĩēńţ ĨƤ Tenant + Ţēńàńţ Recent events + Ŕēćēńţ ēvēńţś On behalf of + Ōń ƀēĥàĺƒ ōƒ - + - No Events found. + Ńō Ēvēńţś ƒōũńď. No matching events could be found. + Ńō màţćĥĩńĝ ēvēńţś ćōũĺď ƀē ƒōũńď. Embedded outpost is not configured correctly. + Ēmƀēďďēď ōũţƥōśţ ĩś ńōţ ćōńƒĩĝũŕēď ćōŕŕēćţĺŷ. Check outposts. + Ćĥēćķ ōũţƥōśţś. HTTPS is not detected correctly + ĤŢŢƤŚ ĩś ńōţ ďēţēćţēď ćōŕŕēćţĺŷ Server and client are further than 5 seconds apart. + Śēŕvēŕ àńď ćĺĩēńţ àŕē ƒũŕţĥēŕ ţĥàń 5 śēćōńďś àƥàŕţ. OK + ŌĶ Everything is ok. + Ēvēŕŷţĥĩńĝ ĩś ōķ. System status + Śŷśţēm śţàţũś Based on + ßàśēď ōń is available! + ĩś àvàĩĺàƀĺē! Up-to-date! + Ũƥ-ţō-ďàţē! Version + Vēŕśĩōń Workers + Ŵōŕķēŕś No workers connected. Background tasks will not run. + Ńō ŵōŕķēŕś ćōńńēćţēď. ßàćķĝŕōũńď ţàśķś ŵĩĺĺ ńōţ ŕũń. hour(s) ago + ĥōũŕ(ś) àĝō day(s) ago + ďàŷ(ś) àĝō Authorizations + Àũţĥōŕĩźàţĩōńś Failed Logins + Ƒàĩĺēď Ĺōĝĩńś Successful Logins + Śũććēśśƒũĺ Ĺōĝĩńś : + : Cancel + Ćàńćēĺ LDAP Source + ĹĎÀƤ Śōũŕćē SCIM Provider + ŚĆĨM Ƥŕōvĩďēŕ Healthy + Ĥēàĺţĥŷ Healthy outposts + Ĥēàĺţĥŷ ōũţƥōśţś Admin + Àďmĩń Not found + Ńōţ ƒōũńď The URL "" was not found. + Ţĥē ŨŔĹ "" ŵàś ńōţ ƒōũńď. Return home + Ŕēţũŕń ĥōmē General system status + Ĝēńēŕàĺ śŷśţēm śţàţũś Welcome, . + Ŵēĺćōmē, . Quick actions + Ǫũĩćķ àćţĩōńś Create a new application + Ćŕēàţē à ńēŵ àƥƥĺĩćàţĩōń Check the logs + Ćĥēćķ ţĥē ĺōĝś Explore integrations + Ēxƥĺōŕē ĩńţēĝŕàţĩōńś Manage users + Màńàĝē ũśēŕś Check release notes + Ćĥēćķ ŕēĺēàśē ńōţēś Outpost status + Ōũţƥōśţ śţàţũś Sync status + Śŷńć śţàţũś Logins and authorizations over the last week (per 8 hours) + Ĺōĝĩńś àńď àũţĥōŕĩźàţĩōńś ōvēŕ ţĥē ĺàśţ ŵēēķ (ƥēŕ 8 ĥōũŕś) Apps with most usage + Àƥƥś ŵĩţĥ mōśţ ũśàĝē days ago + ďàŷś àĝō Objects created + ŌƀĴēćţś ćŕēàţēď User statistics + Ũśēŕ śţàţĩśţĩćś Users created per day in the last month + Ũśēŕś ćŕēàţēď ƥēŕ ďàŷ ĩń ţĥē ĺàśţ mōńţĥ Logins per day in the last month + Ĺōĝĩńś ƥēŕ ďàŷ ĩń ţĥē ĺàśţ mōńţĥ Failed Logins per day in the last month + Ƒàĩĺēď Ĺōĝĩńś ƥēŕ ďàŷ ĩń ţĥē ĺàśţ mōńţĥ Clear search + Ćĺēàŕ śēàŕćĥ System Tasks + Śŷśţēm Ţàśķś Long-running operations which authentik executes in the background. + Ĺōńĝ-ŕũńńĩńĝ ōƥēŕàţĩōńś ŵĥĩćĥ àũţĥēńţĩķ ēxēćũţēś ĩń ţĥē ƀàćķĝŕōũńď. Identifier + Ĩďēńţĩƒĩēŕ Description + Ďēśćŕĩƥţĩōń Last run + Ĺàśţ ŕũń Status + Śţàţũś Actions + Àćţĩōńś Successful + Śũććēśśƒũĺ Error + Ēŕŕōŕ Unknown + Ũńķńōŵń Duration + Ďũŕàţĩōń seconds + śēćōńďś Authentication + Àũţĥēńţĩćàţĩōń Authorization + Àũţĥōŕĩźàţĩōń Enrollment + Ēńŕōĺĺmēńţ Invalidation + Ĩńvàĺĩďàţĩōń Recovery + Ŕēćōvēŕŷ Stage Configuration + Śţàĝē Ćōńƒĩĝũŕàţĩōń Unenrollment + Ũńēńŕōĺĺmēńţ Unknown designation + Ũńķńōŵń ďēśĩĝńàţĩōń Stacked + Śţàćķēď Content left + Ćōńţēńţ ĺēƒţ Content right + Ćōńţēńţ ŕĩĝĥţ Sidebar left + Śĩďēƀàŕ ĺēƒţ Sidebar right + Śĩďēƀàŕ ŕĩĝĥţ Unknown layout + Ũńķńōŵń ĺàŷōũţ Successfully updated provider. + Śũććēśśƒũĺĺŷ ũƥďàţēď ƥŕōvĩďēŕ. Successfully created provider. + Śũććēśśƒũĺĺŷ ćŕēàţēď ƥŕōvĩďēŕ. Bind flow + ßĩńď ƒĺōŵ Flow used for users to authenticate. + Ƒĺōŵ ũśēď ƒōŕ ũśēŕś ţō àũţĥēńţĩćàţē. Search group + Śēàŕćĥ ĝŕōũƥ Users in the selected group can do search queries. If no group is selected, no LDAP Searches are allowed. + Ũśēŕś ĩń ţĥē śēĺēćţēď ĝŕōũƥ ćàń ďō śēàŕćĥ ǫũēŕĩēś. Ĩƒ ńō ĝŕōũƥ ĩś śēĺēćţēď, ńō ĹĎÀƤ Śēàŕćĥēś àŕē àĺĺōŵēď. Bind mode + ßĩńď mōďē Cached binding + Ćàćĥēď ƀĩńďĩńĝ Flow is executed and session is cached in memory. Flow is executed when session expires + Ƒĺōŵ ĩś ēxēćũţēď àńď śēśśĩōń ĩś ćàćĥēď ĩń mēmōŕŷ. Ƒĺōŵ ĩś ēxēćũţēď ŵĥēń śēśśĩōń ēxƥĩŕēś Direct binding + Ďĩŕēćţ ƀĩńďĩńĝ Always execute the configured bind flow to authenticate the user + Àĺŵàŷś ēxēćũţē ţĥē ćōńƒĩĝũŕēď ƀĩńď ƒĺōŵ ţō àũţĥēńţĩćàţē ţĥē ũśēŕ Configure how the outpost authenticates requests. + Ćōńƒĩĝũŕē ĥōŵ ţĥē ōũţƥōśţ àũţĥēńţĩćàţēś ŕēǫũēśţś. Search mode + Śēàŕćĥ mōďē Cached querying + Ćàćĥēď ǫũēŕŷĩńĝ The outpost holds all users and groups in-memory and will refresh every 5 Minutes + Ţĥē ōũţƥōśţ ĥōĺďś àĺĺ ũśēŕś àńď ĝŕōũƥś ĩń-mēmōŕŷ àńď ŵĩĺĺ ŕēƒŕēśĥ ēvēŕŷ 5 Mĩńũţēś Direct querying + Ďĩŕēćţ ǫũēŕŷĩńĝ Always returns the latest data, but slower than cached querying + Àĺŵàŷś ŕēţũŕńś ţĥē ĺàţēśţ ďàţà, ƀũţ śĺōŵēŕ ţĥàń ćàćĥēď ǫũēŕŷĩńĝ Configure how the outpost queries the core authentik server's users. + Ćōńƒĩĝũŕē ĥōŵ ţĥē ōũţƥōśţ ǫũēŕĩēś ţĥē ćōŕē àũţĥēńţĩķ śēŕvēŕ'ś ũśēŕś. Protocol settings + Ƥŕōţōćōĺ śēţţĩńĝś Base DN + ßàśē ĎŃ LDAP DN under which bind requests and search requests can be made. + ĹĎÀƤ ĎŃ ũńďēŕ ŵĥĩćĥ ƀĩńď ŕēǫũēśţś àńď śēàŕćĥ ŕēǫũēśţś ćàń ƀē màďē. Certificate + Ćēŕţĩƒĩćàţē UID start number + ŨĨĎ śţàŕţ ńũmƀēŕ The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber + Ţĥē śţàŕţ ƒōŕ ũĩďŃũmƀēŕś, ţĥĩś ńũmƀēŕ ĩś àďďēď ţō ţĥē ũśēŕ.Ƥķ ţō màķē śũŕē ţĥàţ ţĥē ńũmƀēŕś àŕēń'ţ ţōō ĺōŵ ƒōŕ ƤŌŚĨX ũśēŕś. Ďēƒàũĺţ ĩś 2000 ţō ēńśũŕē ţĥàţ ŵē ďōń'ţ ćōĺĺĩďē ŵĩţĥ ĺōćàĺ ũśēŕś ũĩďŃũmƀēŕ GID start number + ĜĨĎ śţàŕţ ńũmƀēŕ The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber + Ţĥē śţàŕţ ƒōŕ ĝĩďŃũmƀēŕś, ţĥĩś ńũmƀēŕ ĩś àďďēď ţō à ńũmƀēŕ ĝēńēŕàţēď ƒŕōm ţĥē ĝŕōũƥ.Ƥķ ţō màķē śũŕē ţĥàţ ţĥē ńũmƀēŕś àŕēń'ţ ţōō ĺōŵ ƒōŕ ƤŌŚĨX ĝŕōũƥś. Ďēƒàũĺţ ĩś 4000 ţō ēńśũŕē ţĥàţ ŵē ďōń'ţ ćōĺĺĩďē ŵĩţĥ ĺōćàĺ ĝŕōũƥś ōŕ ũśēŕś ƥŕĩmàŕŷ ĝŕōũƥś ĝĩďŃũmƀēŕ (Format: hours=-1;minutes=-2;seconds=-3). + (Ƒōŕmàţ: ĥōũŕś=-1;mĩńũţēś=-2;śēćōńďś=-3). (Format: hours=1;minutes=2;seconds=3). + (Ƒōŕmàţ: ĥōũŕś=1;mĩńũţēś=2;śēćōńďś=3). The following keywords are supported: + Ţĥē ƒōĺĺōŵĩńĝ ķēŷŵōŕďś àŕē śũƥƥōŕţēď: Authentication flow + Àũţĥēńţĩćàţĩōń ƒĺōŵ Flow used when a user access this provider and is not authenticated. + Ƒĺōŵ ũśēď ŵĥēń à ũśēŕ àććēśś ţĥĩś ƥŕōvĩďēŕ àńď ĩś ńōţ àũţĥēńţĩćàţēď. Authorization flow + Àũţĥōŕĩźàţĩōń ƒĺōŵ Flow used when authorizing this provider. + Ƒĺōŵ ũśēď ŵĥēń àũţĥōŕĩźĩńĝ ţĥĩś ƥŕōvĩďēŕ. Client type + Ćĺĩēńţ ţŷƥē Confidential + Ćōńƒĩďēńţĩàĺ Confidential clients are capable of maintaining the confidentiality of their credentials such as client secrets + Ćōńƒĩďēńţĩàĺ ćĺĩēńţś àŕē ćàƥàƀĺē ōƒ màĩńţàĩńĩńĝ ţĥē ćōńƒĩďēńţĩàĺĩţŷ ōƒ ţĥēĩŕ ćŕēďēńţĩàĺś śũćĥ àś ćĺĩēńţ śēćŕēţś Public + Ƥũƀĺĩć Public clients are incapable of maintaining the confidentiality and should use methods like PKCE. + Ƥũƀĺĩć ćĺĩēńţś àŕē ĩńćàƥàƀĺē ōƒ màĩńţàĩńĩńĝ ţĥē ćōńƒĩďēńţĩàĺĩţŷ àńď śĥōũĺď ũśē mēţĥōďś ĺĩķē ƤĶĆĒ. Client ID + Ćĺĩēńţ ĨĎ Client Secret + Ćĺĩēńţ Śēćŕēţ Redirect URIs/Origins (RegEx) + Ŕēďĩŕēćţ ŨŔĨś/Ōŕĩĝĩńś (ŔēĝĒx) Valid redirect URLs after a successful authorization flow. Also specify any origins here for Implicit flows. + Vàĺĩď ŕēďĩŕēćţ ŨŔĹś àƒţēŕ à śũććēśśƒũĺ àũţĥōŕĩźàţĩōń ƒĺōŵ. Àĺśō śƥēćĩƒŷ àńŷ ōŕĩĝĩńś ĥēŕē ƒōŕ Ĩmƥĺĩćĩţ ƒĺōŵś. If no explicit redirect URIs are specified, the first successfully used redirect URI will be saved. + Ĩƒ ńō ēxƥĺĩćĩţ ŕēďĩŕēćţ ŨŔĨś àŕē śƥēćĩƒĩēď, ţĥē ƒĩŕśţ śũććēśśƒũĺĺŷ ũśēď ŕēďĩŕēćţ ŨŔĨ ŵĩĺĺ ƀē śàvēď. To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + Ţō àĺĺōŵ àńŷ ŕēďĩŕēćţ ŨŔĨ, śēţ ţĥĩś vàĺũē ţō ".*". ßē àŵàŕē ōƒ ţĥē ƥōśśĩƀĺē śēćũŕĩţŷ ĩmƥĺĩćàţĩōńś ţĥĩś ćàń ĥàvē. Signing Key + Śĩĝńĩńĝ Ķēŷ Key used to sign the tokens. + Ķēŷ ũśēď ţō śĩĝń ţĥē ţōķēńś. Advanced protocol settings + Àďvàńćēď ƥŕōţōćōĺ śēţţĩńĝś Access code validity + Àććēśś ćōďē vàĺĩďĩţŷ Configure how long access codes are valid for. + Ćōńƒĩĝũŕē ĥōŵ ĺōńĝ àććēśś ćōďēś àŕē vàĺĩď ƒōŕ. Access Token validity + Àććēśś Ţōķēń vàĺĩďĩţŷ Configure how long access tokens are valid for. + Ćōńƒĩĝũŕē ĥōŵ ĺōńĝ àććēśś ţōķēńś àŕē vàĺĩď ƒōŕ. Refresh Token validity + Ŕēƒŕēśĥ Ţōķēń vàĺĩďĩţŷ Configure how long refresh tokens are valid for. + Ćōńƒĩĝũŕē ĥōŵ ĺōńĝ ŕēƒŕēśĥ ţōķēńś àŕē vàĺĩď ƒōŕ. Scopes + Śćōƥēś Select which scopes can be used by the client. The client still has to specify the scope to access the data. + Śēĺēćţ ŵĥĩćĥ śćōƥēś ćàń ƀē ũśēď ƀŷ ţĥē ćĺĩēńţ. Ţĥē ćĺĩēńţ śţĩĺĺ ĥàś ţō śƥēćĩƒŷ ţĥē śćōƥē ţō àććēśś ţĥē ďàţà. Hold control/command to select multiple items. + Ĥōĺď ćōńţŕōĺ/ćōmmàńď ţō śēĺēćţ mũĺţĩƥĺē ĩţēmś. Subject mode + ŚũƀĴēćţ mōďē Based on the User's hashed ID + ßàśēď ōń ţĥē Ũśēŕ'ś ĥàśĥēď ĨĎ Based on the User's ID + ßàśēď ōń ţĥē Ũśēŕ'ś ĨĎ Based on the User's UUID + ßàśēď ōń ţĥē Ũśēŕ'ś ŨŨĨĎ Based on the User's username + ßàśēď ōń ţĥē Ũśēŕ'ś ũśēŕńàmē Based on the User's Email + ßàśēď ōń ţĥē Ũśēŕ'ś Ēmàĩĺ This is recommended over the UPN mode. + Ţĥĩś ĩś ŕēćōmmēńďēď ōvēŕ ţĥē ŨƤŃ mōďē. Based on the User's UPN + ßàśēď ōń ţĥē Ũśēŕ'ś ŨƤŃ Requires the user to have a 'upn' attribute set, and falls back to hashed user ID. Use this mode only if you have different UPN and Mail domains. + Ŕēǫũĩŕēś ţĥē ũśēŕ ţō ĥàvē à 'ũƥń' àţţŕĩƀũţē śēţ, àńď ƒàĺĺś ƀàćķ ţō ĥàśĥēď ũśēŕ ĨĎ. Ũśē ţĥĩś mōďē ōńĺŷ ĩƒ ŷōũ ĥàvē ďĩƒƒēŕēńţ ŨƤŃ àńď Màĩĺ ďōmàĩńś. Configure what data should be used as unique User Identifier. For most cases, the default should be fine. + Ćōńƒĩĝũŕē ŵĥàţ ďàţà śĥōũĺď ƀē ũśēď àś ũńĩǫũē Ũśēŕ Ĩďēńţĩƒĩēŕ. Ƒōŕ mōśţ ćàśēś, ţĥē ďēƒàũĺţ śĥōũĺď ƀē ƒĩńē. Include claims in id_token + Ĩńćĺũďē ćĺàĩmś ĩń ĩď_ţōķēń Include User claims from scopes in the id_token, for applications that don't access the userinfo endpoint. + Ĩńćĺũďē Ũśēŕ ćĺàĩmś ƒŕōm śćōƥēś ĩń ţĥē ĩď_ţōķēń, ƒōŕ àƥƥĺĩćàţĩōńś ţĥàţ ďōń'ţ àććēśś ţĥē ũśēŕĩńƒō ēńďƥōĩńţ. Issuer mode + Ĩśśũēŕ mōďē Each provider has a different issuer, based on the application slug + Ēàćĥ ƥŕōvĩďēŕ ĥàś à ďĩƒƒēŕēńţ ĩśśũēŕ, ƀàśēď ōń ţĥē àƥƥĺĩćàţĩōń śĺũĝ Same identifier is used for all providers + Śàmē ĩďēńţĩƒĩēŕ ĩś ũśēď ƒōŕ àĺĺ ƥŕōvĩďēŕś Configure how the issuer field of the ID Token should be filled. + Ćōńƒĩĝũŕē ĥōŵ ţĥē ĩśśũēŕ ƒĩēĺď ōƒ ţĥē ĨĎ Ţōķēń śĥōũĺď ƀē ƒĩĺĺēď. Machine-to-Machine authentication settings + Màćĥĩńē-ţō-Màćĥĩńē àũţĥēńţĩćàţĩōń śēţţĩńĝś Trusted OIDC Sources + Ţŕũśţēď ŌĨĎĆ Śōũŕćēś JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider. + ĵŴŢś śĩĝńēď ƀŷ ćēŕţĩƒĩćàţēś ćōńƒĩĝũŕēď ĩń ţĥē śēĺēćţēď śōũŕćēś ćàń ƀē ũśēď ţō àũţĥēńţĩćàţē ţō ţĥĩś ƥŕōvĩďēŕ. HTTP-Basic Username Key + ĤŢŢƤ-ßàśĩć Ũśēŕńàmē Ķēŷ User/Group Attribute used for the user part of the HTTP-Basic Header. If not set, the user's Email address is used. + Ũśēŕ/Ĝŕōũƥ Àţţŕĩƀũţē ũśēď ƒōŕ ţĥē ũśēŕ ƥàŕţ ōƒ ţĥē ĤŢŢƤ-ßàśĩć Ĥēàďēŕ. Ĩƒ ńōţ śēţ, ţĥē ũśēŕ'ś Ēmàĩĺ àďďŕēśś ĩś ũśēď. HTTP-Basic Password Key + ĤŢŢƤ-ßàśĩć Ƥàśśŵōŕď Ķēŷ User/Group Attribute used for the password part of the HTTP-Basic Header. + Ũśēŕ/Ĝŕōũƥ Àţţŕĩƀũţē ũśēď ƒōŕ ţĥē ƥàśśŵōŕď ƥàŕţ ōƒ ţĥē ĤŢŢƤ-ßàśĩć Ĥēàďēŕ. Proxy + Ƥŕōxŷ Forward auth (single application) + Ƒōŕŵàŕď àũţĥ (śĩńĝĺē àƥƥĺĩćàţĩōń) Forward auth (domain level) + Ƒōŕŵàŕď àũţĥ (ďōmàĩń ĺēvēĺ) This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well. + Ţĥĩś ƥŕōvĩďēŕ ŵĩĺĺ ƀēĥàvē ĺĩķē à ţŕàńśƥàŕēńţ ŕēvēŕśē-ƥŕōxŷ, ēxćēƥţ ŕēǫũēśţś mũśţ ƀē àũţĥēńţĩćàţēď. Ĩƒ ŷōũŕ ũƥśţŕēàm àƥƥĺĩćàţĩōń ũśēś ĤŢŢƤŚ, màķē śũŕē ţō ćōńńēćţ ţō ţĥē ōũţƥōśţ ũśĩńĝ ĤŢŢƤŚ àś ŵēĺĺ. External host + Ēxţēŕńàĺ ĥōśţ The external URL you'll access the application at. Include any non-standard port. + Ţĥē ēxţēŕńàĺ ŨŔĹ ŷōũ'ĺĺ àććēśś ţĥē àƥƥĺĩćàţĩōń àţ. Ĩńćĺũďē àńŷ ńōń-śţàńďàŕď ƥōŕţ. Internal host + Ĩńţēŕńàĺ ĥōśţ Upstream host that the requests are forwarded to. + Ũƥśţŕēàm ĥōśţ ţĥàţ ţĥē ŕēǫũēśţś àŕē ƒōŕŵàŕďēď ţō. Internal host SSL Validation + Ĩńţēŕńàĺ ĥōśţ ŚŚĹ Vàĺĩďàţĩōń Validate SSL Certificates of upstream servers. + Vàĺĩďàţē ŚŚĹ Ćēŕţĩƒĩćàţēś ōƒ ũƥśţŕēàm śēŕvēŕś. Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application. + Ũśē ţĥĩś ƥŕōvĩďēŕ ŵĩţĥ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ. Ōńĺŷ à śĩńĝĺē ƥŕōvĩďēŕ ĩś ŕēǫũĩŕēď ƥēŕ ŕōōţ ďōmàĩń. Ŷōũ ćàń'ţ ďō ƥēŕ-àƥƥĺĩćàţĩōń àũţĥōŕĩźàţĩōń, ƀũţ ŷōũ ďōń'ţ ĥàvē ţō ćŕēàţē à ƥŕōvĩďēŕ ƒōŕ ēàćĥ àƥƥĺĩćàţĩōń. An example setup can look like this: + Àń ēxàmƥĺē śēţũƥ ćàń ĺōōķ ĺĩķē ţĥĩś: authentik running on auth.example.com + àũţĥēńţĩķ ŕũńńĩńĝ ōń àũţĥ.ēxàmƥĺē.ćōm app1 running on app1.example.com + àƥƥ1 ŕũńńĩńĝ ōń àƥƥ1.ēxàmƥĺē.ćōm In this case, you'd set the Authentication URL to auth.example.com and Cookie domain to example.com. + Ĩń ţĥĩś ćàśē, ŷōũ'ď śēţ ţĥē Àũţĥēńţĩćàţĩōń ŨŔĹ ţō àũţĥ.ēxàmƥĺē.ćōm àńď Ćōōķĩē ďōmàĩń ţō ēxàmƥĺē.ćōm. Authentication URL + Àũţĥēńţĩćàţĩōń ŨŔĹ The external URL you'll authenticate at. The authentik core server should be reachable under this URL. + Ţĥē ēxţēŕńàĺ ŨŔĹ ŷōũ'ĺĺ àũţĥēńţĩćàţē àţ. Ţĥē àũţĥēńţĩķ ćōŕē śēŕvēŕ śĥōũĺď ƀē ŕēàćĥàƀĺē ũńďēŕ ţĥĩś ŨŔĹ. Cookie domain + Ćōōķĩē ďōmàĩń Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'. + Śēţ ţĥĩś ţō ţĥē ďōmàĩń ŷōũ ŵĩśĥ ţĥē àũţĥēńţĩćàţĩōń ţō ƀē vàĺĩď ƒōŕ. Mũśţ ƀē à ƥàŕēńţ ďōmàĩń ōƒ ţĥē ŨŔĹ àƀōvē. Ĩƒ ŷōũ'ŕē ŕũńńĩńĝ àƥƥĺĩćàţĩōńś àś àƥƥ1.ďōmàĩń.ţĺď, àƥƥ2.ďōmàĩń.ţĺď, śēţ ţĥĩś ţō 'ďōmàĩń.ţĺď'. Unknown proxy mode + Ũńķńōŵń ƥŕōxŷ mōďē Token validity + Ţōķēń vàĺĩďĩţŷ Configure how long tokens are valid for. + Ćōńƒĩĝũŕē ĥōŵ ĺōńĝ ţōķēńś àŕē vàĺĩď ƒōŕ. Additional scopes + Àďďĩţĩōńàĺ śćōƥēś Additional scope mappings, which are passed to the proxy. + Àďďĩţĩōńàĺ śćōƥē màƥƥĩńĝś, ŵĥĩćĥ àŕē ƥàśśēď ţō ţĥē ƥŕōxŷ. Unauthenticated URLs + Ũńàũţĥēńţĩćàţēď ŨŔĹś Unauthenticated Paths + Ũńàũţĥēńţĩćàţēď Ƥàţĥś Regular expressions for which authentication is not required. Each new line is interpreted as a new expression. + Ŕēĝũĺàŕ ēxƥŕēśśĩōńś ƒōŕ ŵĥĩćĥ àũţĥēńţĩćàţĩōń ĩś ńōţ ŕēǫũĩŕēď. Ēàćĥ ńēŵ ĺĩńē ĩś ĩńţēŕƥŕēţēď àś à ńēŵ ēxƥŕēśśĩōń. When using proxy or forward auth (single application) mode, the requested URL Path is checked against the regular expressions. When using forward auth (domain mode), the full requested URL including scheme and host is matched against the regular expressions. + Ŵĥēń ũśĩńĝ ƥŕōxŷ ōŕ ƒōŕŵàŕď àũţĥ (śĩńĝĺē àƥƥĺĩćàţĩōń) mōďē, ţĥē ŕēǫũēśţēď ŨŔĹ Ƥàţĥ ĩś ćĥēćķēď àĝàĩńśţ ţĥē ŕēĝũĺàŕ ēxƥŕēśśĩōńś. Ŵĥēń ũśĩńĝ ƒōŕŵàŕď àũţĥ (ďōmàĩń mōďē), ţĥē ƒũĺĺ ŕēǫũēśţēď ŨŔĹ ĩńćĺũďĩńĝ śćĥēmē àńď ĥōśţ ĩś màţćĥēď àĝàĩńśţ ţĥē ŕēĝũĺàŕ ēxƥŕēśśĩōńś. Authentication settings + Àũţĥēńţĩćàţĩōń śēţţĩńĝś Intercept header authentication + Ĩńţēŕćēƥţ ĥēàďēŕ àũţĥēńţĩćàţĩōń When enabled, authentik will intercept the Authorization header to authenticate the request. + Ŵĥēń ēńàƀĺēď, àũţĥēńţĩķ ŵĩĺĺ ĩńţēŕćēƥţ ţĥē Àũţĥōŕĩźàţĩōń ĥēàďēŕ ţō àũţĥēńţĩćàţē ţĥē ŕēǫũēśţ. Send HTTP-Basic Authentication + Śēńď ĤŢŢƤ-ßàśĩć Àũţĥēńţĩćàţĩōń Send a custom HTTP-Basic Authentication header based on values from authentik. + Śēńď à ćũśţōm ĤŢŢƤ-ßàśĩć Àũţĥēńţĩćàţĩōń ĥēàďēŕ ƀàśēď ōń vàĺũēś ƒŕōm àũţĥēńţĩķ. ACS URL + ÀĆŚ ŨŔĹ Issuer + Ĩśśũēŕ Also known as EntityID. + Àĺśō ķńōŵń àś ĒńţĩţŷĨĎ. Service Provider Binding + Śēŕvĩćē Ƥŕōvĩďēŕ ßĩńďĩńĝ Redirect + Ŕēďĩŕēćţ Post + Ƥōśţ Determines how authentik sends the response back to the Service Provider. + Ďēţēŕmĩńēś ĥōŵ àũţĥēńţĩķ śēńďś ţĥē ŕēśƥōńśē ƀàćķ ţō ţĥē Śēŕvĩćē Ƥŕōvĩďēŕ. Audience + Àũďĩēńćē Signing Certificate + Śĩĝńĩńĝ Ćēŕţĩƒĩćàţē Certificate used to sign outgoing Responses going to the Service Provider. + Ćēŕţĩƒĩćàţē ũśēď ţō śĩĝń ōũţĝōĩńĝ Ŕēśƥōńśēś ĝōĩńĝ ţō ţĥē Śēŕvĩćē Ƥŕōvĩďēŕ. Verification Certificate + Vēŕĩƒĩćàţĩōń Ćēŕţĩƒĩćàţē When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default. + Ŵĥēń śēĺēćţēď, ĩńćōmĩńĝ àśśēŕţĩōń'ś Śĩĝńàţũŕēś ŵĩĺĺ ƀē vàĺĩďàţēď àĝàĩńśţ ţĥĩś ćēŕţĩƒĩćàţē. Ţō àĺĺōŵ ũńśĩĝńēď Ŕēǫũēśţś, ĺēàvē ōń ďēƒàũĺţ. Property mappings + Ƥŕōƥēŕţŷ màƥƥĩńĝś NameID Property Mapping + ŃàmēĨĎ Ƥŕōƥēŕţŷ Màƥƥĩńĝ Configure how the NameID value will be created. When left empty, the NameIDPolicy of the incoming request will be respected. + Ćōńƒĩĝũŕē ĥōŵ ţĥē ŃàmēĨĎ vàĺũē ŵĩĺĺ ƀē ćŕēàţēď. Ŵĥēń ĺēƒţ ēmƥţŷ, ţĥē ŃàmēĨĎƤōĺĩćŷ ōƒ ţĥē ĩńćōmĩńĝ ŕēǫũēśţ ŵĩĺĺ ƀē ŕēśƥēćţēď. Assertion valid not before + Àśśēŕţĩōń vàĺĩď ńōţ ƀēƒōŕē Configure the maximum allowed time drift for an assertion. + Ćōńƒĩĝũŕē ţĥē màxĩmũm àĺĺōŵēď ţĩmē ďŕĩƒţ ƒōŕ àń àśśēŕţĩōń. Assertion valid not on or after + Àśśēŕţĩōń vàĺĩď ńōţ ōń ōŕ àƒţēŕ Assertion not valid on or after current time + this value. + Àśśēŕţĩōń ńōţ vàĺĩď ōń ōŕ àƒţēŕ ćũŕŕēńţ ţĩmē + ţĥĩś vàĺũē. Session valid not on or after + Śēśśĩōń vàĺĩď ńōţ ōń ōŕ àƒţēŕ Session not valid on or after current time + this value. + Śēśśĩōń ńōţ vàĺĩď ōń ōŕ àƒţēŕ ćũŕŕēńţ ţĩmē + ţĥĩś vàĺũē. Digest algorithm + Ďĩĝēśţ àĺĝōŕĩţĥm Signature algorithm + Śĩĝńàţũŕē àĺĝōŕĩţĥm Successfully imported provider. + Śũććēśśƒũĺĺŷ ĩmƥōŕţēď ƥŕōvĩďēŕ. Metadata + Mēţàďàţà Apply changes + Àƥƥĺŷ ćĥàńĝēś Close + Ćĺōśē Finish + Ƒĩńĩśĥ Back + ßàćķ No form found + Ńō ƒōŕm ƒōũńď Form didn't return a promise for submitting + Ƒōŕm ďĩďń'ţ ŕēţũŕń à ƥŕōmĩśē ƒōŕ śũƀmĩţţĩńĝ Select type + Śēĺēćţ ţŷƥē Try the new application wizard + Ţŕŷ ţĥē ńēŵ àƥƥĺĩćàţĩōń ŵĩźàŕď The new application wizard greatly simplifies the steps required to create applications and providers. + Ţĥē ńēŵ àƥƥĺĩćàţĩōń ŵĩźàŕď ĝŕēàţĺŷ śĩmƥĺĩƒĩēś ţĥē śţēƥś ŕēǫũĩŕēď ţō ćŕēàţē àƥƥĺĩćàţĩōńś àńď ƥŕōvĩďēŕś. Try it now + Ţŕŷ ĩţ ńōŵ Create + Ćŕēàţē New provider + Ńēŵ ƥŕōvĩďēŕ Create a new provider. + Ćŕēàţē à ńēŵ ƥŕōvĩďēŕ. Create + Ćŕēàţē Shared secret + Śĥàŕēď śēćŕēţ Client Networks + Ćĺĩēńţ Ńēţŵōŕķś List of CIDRs (comma-seperated) that clients can connect from. A more specific CIDR will match before a looser one. Clients connecting from a non-specified CIDR will be dropped. + Ĺĩśţ ōƒ ĆĨĎŔś (ćōmmà-śēƥēŕàţēď) ţĥàţ ćĺĩēńţś ćàń ćōńńēćţ ƒŕōm. À mōŕē śƥēćĩƒĩć + ĆĨĎŔ ŵĩĺĺ màţćĥ ƀēƒōŕē à ĺōōśēŕ ōńē. Ćĺĩēńţś ćōńńēćţĩńĝ ƒŕōm à ńōń-śƥēćĩƒĩēď ĆĨĎŔ + ŵĩĺĺ ƀē ďŕōƥƥēď. URL + ŨŔĹ SCIM base url, usually ends in /v2. + ŚĆĨM ƀàśē ũŕĺ, ũśũàĺĺŷ ēńďś ĩń /v2. Token + Ţōķēń Token to authenticate with. Currently only bearer authentication is supported. + Ţōķēń ţō àũţĥēńţĩćàţē ŵĩţĥ. Ćũŕŕēńţĺŷ ōńĺŷ ƀēàŕēŕ àũţĥēńţĩćàţĩōń ĩś śũƥƥōŕţēď. User filtering + Ũśēŕ ƒĩĺţēŕĩńĝ Exclude service accounts + Ēxćĺũďē śēŕvĩćē àććōũńţś Group + Ĝŕōũƥ Only sync users within the selected group. + Ōńĺŷ śŷńć ũśēŕś ŵĩţĥĩń ţĥē śēĺēćţēď ĝŕōũƥ. Attribute mapping + Àţţŕĩƀũţē màƥƥĩńĝ User Property Mappings + Ũśēŕ Ƥŕōƥēŕţŷ Màƥƥĩńĝś Property mappings used to user mapping. + Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ţō ũśēŕ màƥƥĩńĝ. Group Property Mappings + Ĝŕōũƥ Ƥŕōƥēŕţŷ Màƥƥĩńĝś Property mappings used to group creation. + Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ţō ĝŕōũƥ ćŕēàţĩōń. Not used by any other object. + Ńōţ ũśēď ƀŷ àńŷ ōţĥēŕ ōƀĴēćţ. object will be DELETED + ōƀĴēćţ ŵĩĺĺ ƀē ĎĒĹĒŢĒĎ connection will be deleted + ćōńńēćţĩōń ŵĩĺĺ ƀē ďēĺēţēď reference will be reset to default value + ŕēƒēŕēńćē ŵĩĺĺ ƀē ŕēśēţ ţō ďēƒàũĺţ vàĺũē reference will be set to an empty value + ŕēƒēŕēńćē ŵĩĺĺ ƀē śēţ ţō àń ēmƥţŷ vàĺũē () + () ID + ĨĎ Successfully deleted + Śũććēśśƒũĺĺŷ ďēĺēţēď Failed to delete : + Ƒàĩĺēď ţō ďēĺēţē : Delete + Ďēĺēţē Are you sure you want to delete ? + Àŕē ŷōũ śũŕē ŷōũ ŵàńţ ţō ďēĺēţē ? Delete + Ďēĺēţē Providers + Ƥŕōvĩďēŕś Provide support for protocols like SAML and OAuth to assigned applications. + Ƥŕōvĩďē śũƥƥōŕţ ƒōŕ ƥŕōţōćōĺś ĺĩķē ŚÀMĹ àńď ŌÀũţĥ ţō àśśĩĝńēď àƥƥĺĩćàţĩōńś. Type + Ţŷƥē Provider(s) + Ƥŕōvĩďēŕ(ś) Assigned to application + Àśśĩĝńēď ţō àƥƥĺĩćàţĩōń Assigned to application (backchannel) + Àśśĩĝńēď ţō àƥƥĺĩćàţĩōń (ƀàćķćĥàńńēĺ) Warning: Provider not assigned to any application. + Ŵàŕńĩńĝ: Ƥŕōvĩďēŕ ńōţ àśśĩĝńēď ţō àńŷ àƥƥĺĩćàţĩōń. Update + Ũƥďàţē Update + Ũƥďàţē Select providers to add to application + Śēĺēćţ ƥŕōvĩďēŕś ţō àďď ţō àƥƥĺĩćàţĩōń Add + Àďď Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + Ēĩţĥēŕ ĩńƥũţ à ƒũĺĺ ŨŔĹ, à ŕēĺàţĩvē ƥàţĥ, ōŕ ũśē 'ƒà://ƒà-ţēśţ' ţō ũśē ţĥē Ƒōńţ Àŵēśōmē ĩćōń "ƒà-ţēśţ". Path template for users created. Use placeholders like `%(slug)s` to insert the source slug. + Ƥàţĥ ţēmƥĺàţē ƒōŕ ũśēŕś ćŕēàţēď. Ũśē ƥĺàćēĥōĺďēŕś ĺĩķē `%(śĺũĝ)ś` ţō ĩńśēŕţ ţĥē śōũŕćē śĺũĝ. Successfully updated application. + Śũććēśśƒũĺĺŷ ũƥďàţēď àƥƥĺĩćàţĩōń. Successfully created application. + Śũććēśśƒũĺĺŷ ćŕēàţēď àƥƥĺĩćàţĩōń. Application's display Name. + Àƥƥĺĩćàţĩōń'ś ďĩśƥĺàŷ Ńàmē. Slug + Śĺũĝ Internal application name, used in URLs. + Ĩńţēŕńàĺ àƥƥĺĩćàţĩōń ńàmē, ũśēď ĩń ŨŔĹś. Optionally enter a group name. Applications with identical groups are shown grouped together. + Ōƥţĩōńàĺĺŷ ēńţēŕ à ĝŕōũƥ ńàmē. Àƥƥĺĩćàţĩōńś ŵĩţĥ ĩďēńţĩćàĺ ĝŕōũƥś àŕē śĥōŵń ĝŕōũƥēď ţōĝēţĥēŕ. Provider + Ƥŕōvĩďēŕ Select a provider that this application should use. + Śēĺēćţ à ƥŕōvĩďēŕ ţĥàţ ţĥĩś àƥƥĺĩćàţĩōń śĥōũĺď ũśē. Backchannel providers + ßàćķćĥàńńēĺ ƥŕōvĩďēŕś Select backchannel providers which augment the functionality of the main provider. + Śēĺēćţ ƀàćķćĥàńńēĺ ƥŕōvĩďēŕś ŵĥĩćĥ àũĝmēńţ ţĥē ƒũńćţĩōńàĺĩţŷ ōƒ ţĥē màĩń ƥŕōvĩďēŕ. Policy engine mode + Ƥōĺĩćŷ ēńĝĩńē mōďē Any policy must match to grant access + Àńŷ ƥōĺĩćŷ mũśţ màţćĥ ţō ĝŕàńţ àććēśś All policies must match to grant access + Àĺĺ ƥōĺĩćĩēś mũśţ màţćĥ ţō ĝŕàńţ àććēśś UI settings + ŨĨ śēţţĩńĝś Launch URL + Ĺàũńćĥ ŨŔĹ If left empty, authentik will try to extract the launch URL based on the selected provider. + Ĩƒ ĺēƒţ ēmƥţŷ, àũţĥēńţĩķ ŵĩĺĺ ţŕŷ ţō ēxţŕàćţ ţĥē ĺàũńćĥ ŨŔĹ ƀàśēď ōń ţĥē śēĺēćţēď ƥŕōvĩďēŕ. Open in new tab + Ōƥēń ĩń ńēŵ ţàƀ If checked, the launch URL will open in a new browser tab or window from the user's application library. + Ĩƒ ćĥēćķēď, ţĥē ĺàũńćĥ ŨŔĹ ŵĩĺĺ ōƥēń ĩń à ńēŵ ƀŕōŵśēŕ ţàƀ ōŕ ŵĩńďōŵ ƒŕōm ţĥē ũśēŕ'ś àƥƥĺĩćàţĩōń ĺĩƀŕàŕŷ. Icon + Ĩćōń Currently set to: + Ćũŕŕēńţĺŷ śēţ ţō: Clear icon + Ćĺēàŕ ĩćōń Publisher + Ƥũƀĺĩśĥēŕ Create Application + Ćŕēàţē Àƥƥĺĩćàţĩōń Overview + Ōvēŕvĩēŵ Changelog + Ćĥàńĝēĺōĝ Warning: Provider is not used by any Outpost. + Ŵàŕńĩńĝ: Ƥŕōvĩďēŕ ĩś ńōţ ũśēď ƀŷ àńŷ Ōũţƥōśţ. Assigned to application + Àśśĩĝńēď ţō àƥƥĺĩćàţĩōń Update LDAP Provider + Ũƥďàţē ĹĎÀƤ Ƥŕōvĩďēŕ Edit + Ēďĩţ How to connect + Ĥōŵ ţō ćōńńēćţ Connect to the LDAP Server on port 389: + Ćōńńēćţ ţō ţĥē ĹĎÀƤ Śēŕvēŕ ōń ƥōŕţ 389: Check the IP of the Kubernetes service, or + Ćĥēćķ ţĥē ĨƤ ōƒ ţĥē Ķũƀēŕńēţēś śēŕvĩćē, ōŕ The Host IP of the docker host + Ţĥē Ĥōśţ ĨƤ ōƒ ţĥē ďōćķēŕ ĥōśţ Bind DN + ßĩńď ĎŃ Bind Password + ßĩńď Ƥàśśŵōŕď Search base + Śēàŕćĥ ƀàśē Preview + Ƥŕēvĩēŵ Warning: Provider is not used by an Application. + Ŵàŕńĩńĝ: Ƥŕōvĩďēŕ ĩś ńōţ ũśēď ƀŷ àń Àƥƥĺĩćàţĩōń. Redirect URIs + Ŕēďĩŕēćţ ŨŔĨś Update OAuth2 Provider + Ũƥďàţē ŌÀũţĥ2 Ƥŕōvĩďēŕ OpenID Configuration URL + ŌƥēńĨĎ Ćōńƒĩĝũŕàţĩōń ŨŔĹ OpenID Configuration Issuer + ŌƥēńĨĎ Ćōńƒĩĝũŕàţĩōń Ĩśśũēŕ Authorize URL + Àũţĥōŕĩźē ŨŔĹ Token URL + Ţōķēń ŨŔĹ Userinfo URL + Ũśēŕĩńƒō ŨŔĹ Logout URL + Ĺōĝōũţ ŨŔĹ JWKS URL + ĵŴĶŚ ŨŔĹ Example JWT payload (for currently authenticated user) + Ēxàmƥĺē ĵŴŢ ƥàŷĺōàď (ƒōŕ ćũŕŕēńţĺŷ àũţĥēńţĩćàţēď ũśēŕ) Forward auth (domain-level) + Ƒōŕŵàŕď àũţĥ (ďōmàĩń-ĺēvēĺ) Nginx (Ingress) + Ńĝĩńx (Ĩńĝŕēśś) Nginx (Proxy Manager) + Ńĝĩńx (Ƥŕōxŷ Màńàĝēŕ) Nginx (standalone) + Ńĝĩńx (śţàńďàĺōńē) Traefik (Ingress) + Ţŕàēƒĩķ (Ĩńĝŕēśś) Traefik (Compose) + Ţŕàēƒĩķ (Ćōmƥōśē) Traefik (Standalone) + Ţŕàēƒĩķ (Śţàńďàĺōńē) Caddy (Standalone) + Ćàďďŷ (Śţàńďàĺōńē) Internal Host + Ĩńţēŕńàĺ Ĥōśţ External Host + Ēxţēŕńàĺ Ĥōśţ Basic-Auth + ßàśĩć-Àũţĥ Yes + Ŷēś Mode + Mōďē Update Proxy Provider + Ũƥďàţē Ƥŕōxŷ Ƥŕōvĩďēŕ Protocol Settings + Ƥŕōţōćōĺ Śēţţĩńĝś Allowed Redirect URIs + Àĺĺōŵēď Ŕēďĩŕēćţ ŨŔĨś Setup + Śēţũƥ No additional setup is required. + Ńō àďďĩţĩōńàĺ śēţũƥ ĩś ŕēǫũĩŕēď. Update Radius Provider + Ũƥďàţē Ŕàďĩũś Ƥŕōvĩďēŕ Download + Ďōŵńĺōàď Copy download URL + Ćōƥŷ ďōŵńĺōàď ŨŔĹ Download signing certificate + Ďōŵńĺōàď śĩĝńĩńĝ ćēŕţĩƒĩćàţē Related objects + Ŕēĺàţēď ōƀĴēćţś Update SAML Provider + Ũƥďàţē ŚÀMĹ Ƥŕōvĩďēŕ SAML Configuration + ŚÀMĹ Ćōńƒĩĝũŕàţĩōń EntityID/Issuer + ĒńţĩţŷĨĎ/Ĩśśũēŕ SSO URL (Post) + ŚŚŌ ŨŔĹ (Ƥōśţ) SSO URL (Redirect) + ŚŚŌ ŨŔĹ (Ŕēďĩŕēćţ) SSO URL (IdP-initiated Login) + ŚŚŌ ŨŔĹ (ĨďƤ-ĩńĩţĩàţēď Ĺōĝĩń) SLO URL (Post) + ŚĹŌ ŨŔĹ (Ƥōśţ) SLO URL (Redirect) + ŚĹŌ ŨŔĹ (Ŕēďĩŕēćţ) SAML Metadata + ŚÀMĹ Mēţàďàţà Example SAML attributes + Ēxàmƥĺē ŚÀMĹ àţţŕĩƀũţēś NameID attribute - - - - SCIM provider is in preview. + ŃàmēĨĎ àţţŕĩƀũţē Warning: Provider is not assigned to an application as backchannel provider. + Ŵàŕńĩńĝ: Ƥŕōvĩďēŕ ĩś ńōţ àśśĩĝńēď ţō àń àƥƥĺĩćàţĩōń àś ƀàćķćĥàńńēĺ ƥŕōvĩďēŕ. Update SCIM Provider + Ũƥďàţē ŚĆĨM Ƥŕōvĩďēŕ Sync not run yet. + Śŷńć ńōţ ŕũń ŷēţ. Run sync again + Ŕũń śŷńć àĝàĩń Application details + Àƥƥĺĩćàţĩōń ďēţàĩĺś Create application + Ćŕēàţē àƥƥĺĩćàţĩōń Additional UI settings + Àďďĩţĩōńàĺ ŨĨ śēţţĩńĝś OAuth2/OIDC + ŌÀũţĥ2/ŌĨĎĆ Modern applications, APIs and Single-page applications. + Mōďēŕń àƥƥĺĩćàţĩōńś, ÀƤĨś àńď Śĩńĝĺē-ƥàĝē àƥƥĺĩćàţĩōńś. SAML + ŚÀMĹ XML-based SSO standard. Use this if your application only supports SAML. + XMĹ-ƀàśēď ŚŚŌ śţàńďàŕď. Ũśē ţĥĩś ĩƒ ŷōũŕ àƥƥĺĩćàţĩōń ōńĺŷ śũƥƥōŕţś ŚÀMĹ. Legacy applications which don't natively support SSO. + Ĺēĝàćŷ àƥƥĺĩćàţĩōńś ŵĥĩćĥ ďōń'ţ ńàţĩvēĺŷ śũƥƥōŕţ ŚŚŌ. LDAP + ĹĎÀƤ Provide an LDAP interface for applications and users to authenticate against. + Ƥŕōvĩďē àń ĹĎÀƤ ĩńţēŕƒàćē ƒōŕ àƥƥĺĩćàţĩōńś àńď ũśēŕś ţō àũţĥēńţĩćàţē àĝàĩńśţ. Link + Ĺĩńķ Authentication method + Àũţĥēńţĩćàţĩōń mēţĥōď LDAP details + ĹĎÀƤ ďēţàĩĺś Create service account + Ćŕēàţē śēŕvĩćē àććōũńţ Create provider + Ćŕēàţē ƥŕōvĩďēŕ Application Link + Àƥƥĺĩćàţĩōń Ĺĩńķ URL which will be opened when a user clicks on the application. + ŨŔĹ ŵĥĩćĥ ŵĩĺĺ ƀē ōƥēńēď ŵĥēń à ũśēŕ ćĺĩćķś ōń ţĥē àƥƥĺĩćàţĩōń. Method details + Mēţĥōď ďēţàĩĺś This configuration can be used to authenticate to authentik with other APIs other otherwise programmatically. + Ţĥĩś ćōńƒĩĝũŕàţĩōń ćàń ƀē ũśēď ţō àũţĥēńţĩćàţē ţō àũţĥēńţĩķ ŵĩţĥ ōţĥēŕ ÀƤĨś ōţĥēŕ ōţĥēŕŵĩśē ƥŕōĝŕàmmàţĩćàĺĺŷ. By default, all service accounts can authenticate as this application, as long as they have a valid token of the type app-password. + ßŷ ďēƒàũĺţ, àĺĺ śēŕvĩćē àććōũńţś ćàń àũţĥēńţĩćàţē àś ţĥĩś àƥƥĺĩćàţĩōń, àś ĺōńĝ àś ţĥēŷ ĥàvē à vàĺĩď ţōķēń ōƒ ţĥē ţŷƥē àƥƥ-ƥàśśŵōŕď. Web application + Ŵēƀ àƥƥĺĩćàţĩōń Applications which handle the authentication server-side (for example, Python, Go, Rust, Java, PHP) + Àƥƥĺĩćàţĩōńś ŵĥĩćĥ ĥàńďĺē ţĥē àũţĥēńţĩćàţĩōń śēŕvēŕ-śĩďē (ƒōŕ ēxàmƥĺē, Ƥŷţĥōń, Ĝō, Ŕũśţ, ĵàvà, ƤĤƤ) Single-page applications + Śĩńĝĺē-ƥàĝē àƥƥĺĩćàţĩōńś Single-page applications which handle authentication in the browser (for example, Javascript, Angular, React, Vue) + Śĩńĝĺē-ƥàĝē àƥƥĺĩćàţĩōńś ŵĥĩćĥ ĥàńďĺē àũţĥēńţĩćàţĩōń ĩń ţĥē ƀŕōŵśēŕ (ƒōŕ ēxàmƥĺē, ĵàvàśćŕĩƥţ, Àńĝũĺàŕ, Ŕēàćţ, Vũē) Native application + Ńàţĩvē àƥƥĺĩćàţĩōń Applications which redirect users to a non-web callback (for example, Android, iOS) + Àƥƥĺĩćàţĩōńś ŵĥĩćĥ ŕēďĩŕēćţ ũśēŕś ţō à ńōń-ŵēƀ ćàĺĺƀàćķ (ƒōŕ ēxàmƥĺē, Àńďŕōĩď, ĩŌŚ) API + ÀƤĨ Authentication without user interaction, or machine-to-machine authentication. + Àũţĥēńţĩćàţĩōń ŵĩţĥōũţ ũśēŕ ĩńţēŕàćţĩōń, ōŕ màćĥĩńē-ţō-màćĥĩńē àũţĥēńţĩćàţĩōń. Application type + Àƥƥĺĩćàţĩōń ţŷƥē Flow used when users access this application. + Ƒĺōŵ ũśēď ŵĥēń ũśēŕś àććēśś ţĥĩś àƥƥĺĩćàţĩōń. Proxy details + Ƥŕōxŷ ďēţàĩĺś External domain + Ēxţēŕńàĺ ďōmàĩń External domain you will be accessing the domain from. + Ēxţēŕńàĺ ďōmàĩń ŷōũ ŵĩĺĺ ƀē àććēśśĩńĝ ţĥē ďōmàĩń ƒŕōm. Import SAML Metadata + Ĩmƥōŕţ ŚÀMĹ Mēţàďàţà Import the metadata document of the applicaation you want to configure. + Ĩmƥōŕţ ţĥē mēţàďàţà ďōćũmēńţ ōƒ ţĥē àƥƥĺĩćààţĩōń ŷōũ ŵàńţ ţō ćōńƒĩĝũŕē. Manual configuration + Màńũàĺ ćōńƒĩĝũŕàţĩōń Manually configure SAML + Màńũàĺĺŷ ćōńƒĩĝũŕē ŚÀMĹ SAML details + ŚÀMĹ ďēţàĩĺś URL that authentik will redirect back to after successful authentication. + ŨŔĹ ţĥàţ àũţĥēńţĩķ ŵĩĺĺ ŕēďĩŕēćţ ƀàćķ ţō àƒţēŕ śũććēśśƒũĺ àũţĥēńţĩćàţĩōń. Import SAML metadata + Ĩmƥōŕţ ŚÀMĹ mēţàďàţà New application + Ńēŵ àƥƥĺĩćàţĩōń Create a new application. + Ćŕēàţē à ńēŵ àƥƥĺĩćàţĩōń. Applications + Àƥƥĺĩćàţĩōńś External Applications which use authentik as Identity-Provider, utilizing protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access. + Ēxţēŕńàĺ Àƥƥĺĩćàţĩōńś ŵĥĩćĥ ũśē àũţĥēńţĩķ àś Ĩďēńţĩţŷ-Ƥŕōvĩďēŕ, ũţĩĺĩźĩńĝ ƥŕōţōćōĺś ĺĩķē ŌÀũţĥ2 àńď ŚÀMĹ. Àĺĺ àƥƥĺĩćàţĩōńś àŕē śĥōŵń ĥēŕē, ēvēń ōńēś ŷōũ ćàńńōţ àććēśś. Provider Type + Ƥŕōvĩďēŕ Ţŷƥē Application(s) + Àƥƥĺĩćàţĩōń(ś) Application Icon + Àƥƥĺĩćàţĩōń Ĩćōń Update Application + Ũƥďàţē Àƥƥĺĩćàţĩōń Successfully sent test-request. + Śũććēśśƒũĺĺŷ śēńţ ţēśţ-ŕēǫũēśţ. Log messages + Ĺōĝ mēśśàĝēś No log messages. + Ńō ĺōĝ mēśśàĝēś. Active + Àćţĩvē Last login + Ĺàśţ ĺōĝĩń Select users to add + Śēĺēćţ ũśēŕś ţō àďď Successfully updated group. + Śũććēśśƒũĺĺŷ ũƥďàţēď ĝŕōũƥ. Successfully created group. + Śũććēśśƒũĺĺŷ ćŕēàţēď ĝŕōũƥ. Is superuser + Ĩś śũƥēŕũśēŕ Users added to this group will be superusers. + Ũśēŕś àďďēď ţō ţĥĩś ĝŕōũƥ ŵĩĺĺ ƀē śũƥēŕũśēŕś. Parent + Ƥàŕēńţ Attributes + Àţţŕĩƀũţēś Set custom attributes using YAML or JSON. + Śēţ ćũśţōm àţţŕĩƀũţēś ũśĩńĝ ŶÀMĹ ōŕ ĵŚŌŃ. Successfully updated binding. + Śũććēśśƒũĺĺŷ ũƥďàţēď ƀĩńďĩńĝ. Successfully created binding. + Śũććēśśƒũĺĺŷ ćŕēàţēď ƀĩńďĩńĝ. Policy + Ƥōĺĩćŷ Group mappings can only be checked if a user is already logged in when trying to access this source. + Ĝŕōũƥ màƥƥĩńĝś ćàń ōńĺŷ ƀē ćĥēćķēď ĩƒ à ũśēŕ ĩś àĺŕēàďŷ ĺōĝĝēď ĩń ŵĥēń ţŕŷĩńĝ ţō àććēśś ţĥĩś śōũŕćē. User mappings can only be checked if a user is already logged in when trying to access this source. + Ũśēŕ màƥƥĩńĝś ćàń ōńĺŷ ƀē ćĥēćķēď ĩƒ à ũśēŕ ĩś àĺŕēàďŷ ĺōĝĝēď ĩń ŵĥēń ţŕŷĩńĝ ţō àććēśś ţĥĩś śōũŕćē. Enabled + Ēńàƀĺēď Negate result + Ńēĝàţē ŕēśũĺţ Negates the outcome of the binding. Messages are unaffected. + Ńēĝàţēś ţĥē ōũţćōmē ōƒ ţĥē ƀĩńďĩńĝ. Mēśśàĝēś àŕē ũńàƒƒēćţēď. Order + Ōŕďēŕ Timeout + Ţĩmēōũţ Successfully updated policy. + Śũććēśśƒũĺĺŷ ũƥďàţēď ƥōĺĩćŷ. Successfully created policy. + Śũććēśśƒũĺĺŷ ćŕēàţēď ƥōĺĩćŷ. A policy used for testing. Always returns the same result as specified below after waiting a random duration. + À ƥōĺĩćŷ ũśēď ƒōŕ ţēśţĩńĝ. Àĺŵàŷś ŕēţũŕńś ţĥē śàmē ŕēśũĺţ àś śƥēćĩƒĩēď ƀēĺōŵ àƒţēŕ ŵàĩţĩńĝ à ŕàńďōm ďũŕàţĩōń. Execution logging + Ēxēćũţĩōń ĺōĝĝĩńĝ When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged. + Ŵĥēń ţĥĩś ōƥţĩōń ĩś ēńàƀĺēď, àĺĺ ēxēćũţĩōńś ōƒ ţĥĩś ƥōĺĩćŷ ŵĩĺĺ ƀē ĺōĝĝēď. ßŷ ďēƒàũĺţ, ōńĺŷ ēxēćũţĩōń ēŕŕōŕś àŕē ĺōĝĝēď. Policy-specific settings + Ƥōĺĩćŷ-śƥēćĩƒĩć śēţţĩńĝś Pass policy? + Ƥàśś ƥōĺĩćŷ? Wait (min) + Ŵàĩţ (mĩń) The policy takes a random time to execute. This controls the minimum time it will take. + Ţĥē ƥōĺĩćŷ ţàķēś à ŕàńďōm ţĩmē ţō ēxēćũţē. Ţĥĩś ćōńţŕōĺś ţĥē mĩńĩmũm ţĩmē ĩţ ŵĩĺĺ ţàķē. Wait (max) + Ŵàĩţ (màx) Matches an event against a set of criteria. If any of the configured values match, the policy passes. + Màţćĥēś àń ēvēńţ àĝàĩńśţ à śēţ ōƒ ćŕĩţēŕĩà. Ĩƒ àńŷ ōƒ ţĥē ćōńƒĩĝũŕēď vàĺũēś màţćĥ, ţĥē ƥōĺĩćŷ ƥàśśēś. Match created events with this action type. When left empty, all action types will be matched. + Màţćĥ ćŕēàţēď ēvēńţś ŵĩţĥ ţĥĩś àćţĩōń ţŷƥē. Ŵĥēń ĺēƒţ ēmƥţŷ, àĺĺ àćţĩōń ţŷƥēś ŵĩĺĺ ƀē màţćĥēď. Matches Event's Client IP (strict matching, for network matching use an Expression Policy. + Màţćĥēś Ēvēńţ'ś Ćĺĩēńţ ĨƤ (śţŕĩćţ màţćĥĩńĝ, ƒōŕ ńēţŵōŕķ màţćĥĩńĝ ũśē àń Ēxƥŕēśśĩōń Ƥōĺĩćŷ. Match events created by selected application. When left empty, all applications are matched. + Màţćĥ ēvēńţś ćŕēàţēď ƀŷ śēĺēćţēď àƥƥĺĩćàţĩōń. Ŵĥēń ĺēƒţ ēmƥţŷ, àĺĺ àƥƥĺĩćàţĩōńś àŕē màţćĥēď. Checks if the request's user's password has been changed in the last x days, and denys based on settings. + Ćĥēćķś ĩƒ ţĥē ŕēǫũēśţ'ś ũśēŕ'ś ƥàśśŵōŕď ĥàś ƀēēń ćĥàńĝēď ĩń ţĥē ĺàśţ x ďàŷś, àńď ďēńŷś ƀàśēď ōń śēţţĩńĝś. Maximum age (in days) + Màxĩmũm àĝē (ĩń ďàŷś) Only fail the policy, don't invalidate user's password + Ōńĺŷ ƒàĩĺ ţĥē ƥōĺĩćŷ, ďōń'ţ ĩńvàĺĩďàţē ũśēŕ'ś ƥàśśŵōŕď Executes the python snippet to determine whether to allow or deny a request. + Ēxēćũţēś ţĥē ƥŷţĥōń śńĩƥƥēţ ţō ďēţēŕmĩńē ŵĥēţĥēŕ ţō àĺĺōŵ ōŕ ďēńŷ à ŕēǫũēśţ. Expression using Python. + Ēxƥŕēśśĩōń ũśĩńĝ Ƥŷţĥōń. See documentation for a list of all variables. + Śēē ďōćũmēńţàţĩōń ƒōŕ à ĺĩśţ ōƒ àĺĺ vàŕĩàƀĺēś. Static rules + Śţàţĩć ŕũĺēś Minimum length + Mĩńĩmũm ĺēńĝţĥ Minimum amount of Uppercase Characters + Mĩńĩmũm àmōũńţ ōƒ Ũƥƥēŕćàśē Ćĥàŕàćţēŕś Minimum amount of Lowercase Characters + Mĩńĩmũm àmōũńţ ōƒ Ĺōŵēŕćàśē Ćĥàŕàćţēŕś Minimum amount of Digits + Mĩńĩmũm àmōũńţ ōƒ Ďĩĝĩţś Minimum amount of Symbols Characters + Mĩńĩmũm àmōũńţ ōƒ Śŷmƀōĺś Ćĥàŕàćţēŕś Error message + Ēŕŕōŕ mēśśàĝē Symbol charset + Śŷmƀōĺ ćĥàŕśēţ Characters which are considered as symbols. + Ćĥàŕàćţēŕś ŵĥĩćĥ àŕē ćōńśĩďēŕēď àś śŷmƀōĺś. HaveIBeenPwned settings + ĤàvēĨßēēńƤŵńēď śēţţĩńĝś Allowed count + Àĺĺōŵēď ćōũńţ Allow up to N occurrences in the HIBP database. + Àĺĺōŵ ũƥ ţō Ń ōććũŕŕēńćēś ĩń ţĥē ĤĨßƤ ďàţàƀàśē. zxcvbn settings + źxćvƀń śēţţĩńĝś Score threshold + Śćōŕē ţĥŕēśĥōĺď If the password's score is less than or equal this value, the policy will fail. + Ĩƒ ţĥē ƥàśśŵōŕď'ś śćōŕē ĩś ĺēśś ţĥàń ōŕ ēǫũàĺ ţĥĩś vàĺũē, ţĥē ƥōĺĩćŷ ŵĩĺĺ ƒàĩĺ. 0: Too guessable: risky password. (guesses < 10^3) + 0: Ţōō ĝũēśśàƀĺē: ŕĩśķŷ ƥàśśŵōŕď. (ĝũēśśēś < 10^3) 1: Very guessable: protection from throttled online attacks. (guesses < 10^6) + 1: Vēŕŷ ĝũēśśàƀĺē: ƥŕōţēćţĩōń ƒŕōm ţĥŕōţţĺēď ōńĺĩńē àţţàćķś. (ĝũēśśēś < 10^6) 2: Somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8) + 2: Śōmēŵĥàţ ĝũēśśàƀĺē: ƥŕōţēćţĩōń ƒŕōm ũńţĥŕōţţĺēď ōńĺĩńē àţţàćķś. (ĝũēśśēś < 10^8) 3: Safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10) + 3: Śàƒēĺŷ ũńĝũēśśàƀĺē: mōďēŕàţē ƥŕōţēćţĩōń ƒŕōm ōƒƒĺĩńē śĺōŵ-ĥàśĥ śćēńàŕĩō. (ĝũēśśēś < 10^10) 4: Very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10) + 4: Vēŕŷ ũńĝũēśśàƀĺē: śţŕōńĝ ƥŕōţēćţĩōń ƒŕōm ōƒƒĺĩńē śĺōŵ-ĥàśĥ śćēńàŕĩō. (ĝũēśśēś >= 10^10) Checks the value from the policy request against several rules, mostly used to ensure password strength. + Ćĥēćķś ţĥē vàĺũē ƒŕōm ţĥē ƥōĺĩćŷ ŕēǫũēśţ àĝàĩńśţ śēvēŕàĺ ŕũĺēś, mōśţĺŷ ũśēď ţō ēńśũŕē ƥàśśŵōŕď śţŕēńĝţĥ. Password field + Ƥàśśŵōŕď ƒĩēĺď Field key to check, field keys defined in Prompt stages are available. + Ƒĩēĺď ķēŷ ţō ćĥēćķ, ƒĩēĺď ķēŷś ďēƒĩńēď ĩń Ƥŕōmƥţ śţàĝēś àŕē àvàĩĺàƀĺē. Check static rules + Ćĥēćķ śţàţĩć ŕũĺēś Check haveibeenpwned.com + Ćĥēćķ ĥàvēĩƀēēńƥŵńēď.ćōm For more info see: + Ƒōŕ mōŕē ĩńƒō śēē: Check zxcvbn + Ćĥēćķ źxćvƀń Password strength estimator created by Dropbox, see: + Ƥàśśŵōŕď śţŕēńĝţĥ ēśţĩmàţōŕ ćŕēàţēď ƀŷ Ďŕōƥƀōx, śēē: Allows/denys requests based on the users and/or the IPs reputation. + Àĺĺōŵś/ďēńŷś ŕēǫũēśţś ƀàśēď ōń ţĥē ũśēŕś àńď/ōŕ ţĥē ĨƤś ŕēƥũţàţĩōń. Invalid login attempts will decrease the score for the client's IP, and the username they are attempting to login as, by one. + Ĩńvàĺĩď ĺōĝĩń àţţēmƥţś ŵĩĺĺ ďēćŕēàśē ţĥē śćōŕē ƒōŕ ţĥē ćĺĩēńţ'ś ĨƤ, àńď ţĥē +ũśēŕńàmē ţĥēŷ àŕē àţţēmƥţĩńĝ ţō ĺōĝĩń àś, ƀŷ ōńē. The policy passes when the reputation score is below the threshold, and doesn't pass when either or both of the selected options are equal or above the threshold. + Ţĥē ƥōĺĩćŷ ƥàśśēś ŵĥēń ţĥē ŕēƥũţàţĩōń śćōŕē ĩś ƀēĺōŵ ţĥē ţĥŕēśĥōĺď, àńď +ďōēśń'ţ ƥàśś ŵĥēń ēĩţĥēŕ ōŕ ƀōţĥ ōƒ ţĥē śēĺēćţēď ōƥţĩōńś àŕē ēǫũàĺ ōŕ àƀōvē ţĥē ţĥŕēśĥōĺď. Check IP + Ćĥēćķ ĨƤ Check Username + Ćĥēćķ Ũśēŕńàmē Threshold + Ţĥŕēśĥōĺď New policy + Ńēŵ ƥōĺĩćŷ Create a new policy. + Ćŕēàţē à ńēŵ ƥōĺĩćŷ. Create Binding + Ćŕēàţē ßĩńďĩńĝ Superuser + Śũƥēŕũśēŕ Members + Mēmƀēŕś Select groups to add user to + Śēĺēćţ ĝŕōũƥś ţō àďď ũśēŕ ţō Warning: Adding the user to the selected group(s) will give them superuser permissions. + Ŵàŕńĩńĝ: Àďďĩńĝ ţĥē ũśēŕ ţō ţĥē śēĺēćţēď ĝŕōũƥ(ś) ŵĩĺĺ ĝĩvē ţĥēm śũƥēŕũśēŕ ƥēŕmĩśśĩōńś. Successfully updated user. + Śũććēśśƒũĺĺŷ ũƥďàţēď ũśēŕ. Successfully created user. + Śũććēśśƒũĺĺŷ ćŕēàţēď ũśēŕ. Username + Ũśēŕńàmē User's primary identifier. 150 characters or fewer. + Ũśēŕ'ś ƥŕĩmàŕŷ ĩďēńţĩƒĩēŕ. 150 ćĥàŕàćţēŕś ōŕ ƒēŵēŕ. User's display name. + Ũśēŕ'ś ďĩśƥĺàŷ ńàmē. Email + Ēmàĩĺ Is active + Ĩś àćţĩvē Designates whether this user should be treated as active. Unselect this instead of deleting accounts. + Ďēśĩĝńàţēś ŵĥēţĥēŕ ţĥĩś ũśēŕ śĥōũĺď ƀē ţŕēàţēď àś àćţĩvē. Ũńśēĺēćţ ţĥĩś ĩńśţēàď ōƒ ďēĺēţĩńĝ àććōũńţś. Path + Ƥàţĥ Policy / User / Group + Ƥōĺĩćŷ / Ũśēŕ / Ĝŕōũƥ Policy + Ƥōĺĩćŷ Group + Ĝŕōũƥ User + Ũśēŕ Edit Policy + Ēďĩţ Ƥōĺĩćŷ Update Group + Ũƥďàţē Ĝŕōũƥ Edit Group + Ēďĩţ Ĝŕōũƥ Update User + Ũƥďàţē Ũśēŕ Edit User + Ēďĩţ Ũśēŕ Policy binding(s) + Ƥōĺĩćŷ ƀĩńďĩńĝ(ś) Update Binding + Ũƥďàţē ßĩńďĩńĝ Edit Binding + Ēďĩţ ßĩńďĩńĝ No Policies bound. + Ńō Ƥōĺĩćĩēś ƀōũńď. No policies are currently bound to this object. + Ńō ƥōĺĩćĩēś àŕē ćũŕŕēńţĺŷ ƀōũńď ţō ţĥĩś ōƀĴēćţ. Bind existing policy + ßĩńď ēxĩśţĩńĝ ƥōĺĩćŷ Warning: Application is not used by any Outpost. + Ŵàŕńĩńĝ: Àƥƥĺĩćàţĩōń ĩś ńōţ ũśēď ƀŷ àńŷ Ōũţƥōśţ. Related + Ŕēĺàţēď Backchannel Providers + ßàćķćĥàńńēĺ Ƥŕōvĩďēŕś Check access + Ćĥēćķ àććēśś Check + Ćĥēćķ Check Application access + Ćĥēćķ Àƥƥĺĩćàţĩōń àććēśś Test + Ţēśţ Launch + Ĺàũńćĥ Logins over the last week (per 8 hours) + Ĺōĝĩńś ōvēŕ ţĥē ĺàśţ ŵēēķ (ƥēŕ 8 ĥōũŕś) Policy / Group / User Bindings + Ƥōĺĩćŷ / Ĝŕōũƥ / Ũśēŕ ßĩńďĩńĝś These policies control which users can access this application. + Ţĥēśē ƥōĺĩćĩēś ćōńţŕōĺ ŵĥĩćĥ ũśēŕś ćàń àććēśś ţĥĩś àƥƥĺĩćàţĩōń. Successfully updated source. + Śũććēśśƒũĺĺŷ ũƥďàţēď śōũŕćē. Successfully created source. + Śũććēśśƒũĺĺŷ ćŕēàţēď śōũŕćē. Sync users + Śŷńć ũśēŕś User password writeback + Ũśēŕ ƥàśśŵōŕď ŵŕĩţēƀàćķ Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP. + Ĺōĝĩń ƥàśśŵōŕď ĩś śŷńćēď ƒŕōm ĹĎÀƤ ĩńţō àũţĥēńţĩķ àũţōmàţĩćàĺĺŷ. Ēńàƀĺē ţĥĩś ōƥţĩōń ōńĺŷ ţō ŵŕĩţē ƥàśśŵōŕď ćĥàńĝēś ĩń àũţĥēńţĩķ ƀàćķ ţō ĹĎÀƤ. Sync groups + Śŷńć ĝŕōũƥś Connection settings + Ćōńńēćţĩōń śēţţĩńĝś Server URI + Śēŕvēŕ ŨŔĨ Specify multiple server URIs by separating them with a comma. + Śƥēćĩƒŷ mũĺţĩƥĺē śēŕvēŕ ŨŔĨś ƀŷ śēƥàŕàţĩńĝ ţĥēm ŵĩţĥ à ćōmmà. Enable StartTLS + Ēńàƀĺē ŚţàŕţŢĹŚ To use SSL instead, use 'ldaps://' and disable this option. + Ţō ũśē ŚŚĹ ĩńśţēàď, ũśē 'ĺďàƥś://' àńď ďĩśàƀĺē ţĥĩś ōƥţĩōń. TLS Verification Certificate + ŢĹŚ Vēŕĩƒĩćàţĩōń Ćēŕţĩƒĩćàţē When connecting to an LDAP Server with TLS, certificates are not checked by default. Specify a keypair to validate the remote certificate. + Ŵĥēń ćōńńēćţĩńĝ ţō àń ĹĎÀƤ Śēŕvēŕ ŵĩţĥ ŢĹŚ, ćēŕţĩƒĩćàţēś àŕē ńōţ ćĥēćķēď ƀŷ ďēƒàũĺţ. Śƥēćĩƒŷ à ķēŷƥàĩŕ ţō vàĺĩďàţē ţĥē ŕēmōţē ćēŕţĩƒĩćàţē. Bind CN + ßĩńď ĆŃ LDAP Attribute mapping + ĹĎÀƤ Àţţŕĩƀũţē màƥƥĩńĝ Property mappings used to user creation. + Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ţō ũśēŕ ćŕēàţĩōń. Additional settings + Àďďĩţĩōńàĺ śēţţĩńĝś Parent group for all the groups imported from LDAP. + Ƥàŕēńţ ĝŕōũƥ ƒōŕ àĺĺ ţĥē ĝŕōũƥś ĩmƥōŕţēď ƒŕōm ĹĎÀƤ. User path + Ũśēŕ ƥàţĥ Addition User DN + Àďďĩţĩōń Ũśēŕ ĎŃ Additional user DN, prepended to the Base DN. + Àďďĩţĩōńàĺ ũśēŕ ĎŃ, ƥŕēƥēńďēď ţō ţĥē ßàśē ĎŃ. Addition Group DN + Àďďĩţĩōń Ĝŕōũƥ ĎŃ Additional group DN, prepended to the Base DN. + Àďďĩţĩōńàĺ ĝŕōũƥ ĎŃ, ƥŕēƥēńďēď ţō ţĥē ßàśē ĎŃ. User object filter + Ũśēŕ ōƀĴēćţ ƒĩĺţēŕ Consider Objects matching this filter to be Users. + Ćōńśĩďēŕ ŌƀĴēćţś màţćĥĩńĝ ţĥĩś ƒĩĺţēŕ ţō ƀē Ũśēŕś. Group object filter + Ĝŕōũƥ ōƀĴēćţ ƒĩĺţēŕ Consider Objects matching this filter to be Groups. + Ćōńśĩďēŕ ŌƀĴēćţś màţćĥĩńĝ ţĥĩś ƒĩĺţēŕ ţō ƀē Ĝŕōũƥś. Group membership field + Ĝŕōũƥ mēmƀēŕśĥĩƥ ƒĩēĺď Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + Ƒĩēĺď ŵĥĩćĥ ćōńţàĩńś mēmƀēŕś ōƒ à ĝŕōũƥ. Ńōţē ţĥàţ ĩƒ ũśĩńĝ ţĥē "mēmƀēŕŨĩď" ƒĩēĺď, ţĥē vàĺũē ĩś àśśũmēď ţō ćōńţàĩń à ŕēĺàţĩvē ďĩśţĩńĝũĩśĥēď ńàmē. ē.ĝ. 'mēmƀēŕŨĩď=śōmē-ũśēŕ' ĩńśţēàď ōƒ 'mēmƀēŕŨĩď=ćń=śōmē-ũśēŕ,ōũ=ĝŕōũƥś,...' Object uniqueness field + ŌƀĴēćţ ũńĩǫũēńēśś ƒĩēĺď Field which contains a unique Identifier. + Ƒĩēĺď ŵĥĩćĥ ćōńţàĩńś à ũńĩǫũē Ĩďēńţĩƒĩēŕ. Link users on unique identifier + Ĺĩńķ ũśēŕś ōń ũńĩǫũē ĩďēńţĩƒĩēŕ Link to a user with identical email address. Can have security implications when a source doesn't validate email addresses + Ĺĩńķ ţō à ũśēŕ ŵĩţĥ ĩďēńţĩćàĺ ēmàĩĺ àďďŕēśś. Ćàń ĥàvē śēćũŕĩţŷ ĩmƥĺĩćàţĩōńś ŵĥēń à śōũŕćē ďōēśń'ţ vàĺĩďàţē ēmàĩĺ àďďŕēśśēś Use the user's email address, but deny enrollment when the email address already exists + Ũśē ţĥē ũśēŕ'ś ēmàĩĺ àďďŕēśś, ƀũţ ďēńŷ ēńŕōĺĺmēńţ ŵĥēń ţĥē ēmàĩĺ àďďŕēśś àĺŕēàďŷ ēxĩśţś Link to a user with identical username. Can have security implications when a username is used with another source + Ĺĩńķ ţō à ũśēŕ ŵĩţĥ ĩďēńţĩćàĺ ũśēŕńàmē. Ćàń ĥàvē śēćũŕĩţŷ ĩmƥĺĩćàţĩōńś ŵĥēń à ũśēŕńàmē ĩś ũśēď ŵĩţĥ àńōţĥēŕ śōũŕćē Use the user's username, but deny enrollment when the username already exists + Ũśē ţĥē ũśēŕ'ś ũśēŕńàmē, ƀũţ ďēńŷ ēńŕōĺĺmēńţ ŵĥēń ţĥē ũśēŕńàmē àĺŕēàďŷ ēxĩśţś Unknown user matching mode + Ũńķńōŵń ũśēŕ màţćĥĩńĝ mōďē URL settings + ŨŔĹ śēţţĩńĝś Authorization URL + Àũţĥōŕĩźàţĩōń ŨŔĹ URL the user is redirect to to consent the authorization. + ŨŔĹ ţĥē ũśēŕ ĩś ŕēďĩŕēćţ ţō ţō ćōńśēńţ ţĥē àũţĥōŕĩźàţĩōń. Access token URL + Àććēśś ţōķēń ŨŔĹ URL used by authentik to retrieve tokens. + ŨŔĹ ũśēď ƀŷ àũţĥēńţĩķ ţō ŕēţŕĩēvē ţōķēńś. Profile URL + Ƥŕōƒĩĺē ŨŔĹ URL used by authentik to get user information. + ŨŔĹ ũśēď ƀŷ àũţĥēńţĩķ ţō ĝēţ ũśēŕ ĩńƒōŕmàţĩōń. Request token URL + Ŕēǫũēśţ ţōķēń ŨŔĹ URL used to request the initial token. This URL is only required for OAuth 1. + ŨŔĹ ũśēď ţō ŕēǫũēśţ ţĥē ĩńĩţĩàĺ ţōķēń. Ţĥĩś ŨŔĹ ĩś ōńĺŷ ŕēǫũĩŕēď ƒōŕ ŌÀũţĥ 1. OIDC Well-known URL + ŌĨĎĆ Ŵēĺĺ-ķńōŵń ŨŔĹ OIDC well-known configuration URL. Can be used to automatically configure the URLs above. + ŌĨĎĆ ŵēĺĺ-ķńōŵń ćōńƒĩĝũŕàţĩōń ŨŔĹ. Ćàń ƀē ũśēď ţō àũţōmàţĩćàĺĺŷ ćōńƒĩĝũŕē ţĥē ŨŔĹś àƀōvē. OIDC JWKS URL + ŌĨĎĆ ĵŴĶŚ ŨŔĹ JSON Web Key URL. Keys from the URL will be used to validate JWTs from this source. + ĵŚŌŃ Ŵēƀ Ķēŷ ŨŔĹ. Ķēŷś ƒŕōm ţĥē ŨŔĹ ŵĩĺĺ ƀē ũśēď ţō vàĺĩďàţē ĵŴŢś ƒŕōm ţĥĩś śōũŕćē. OIDC JWKS + ŌĨĎĆ ĵŴĶŚ Raw JWKS data. + Ŕàŵ ĵŴĶŚ ďàţà. User matching mode + Ũśēŕ màţćĥĩńĝ mōďē Delete currently set icon. + Ďēĺēţē ćũŕŕēńţĺŷ śēţ ĩćōń. Consumer key + Ćōńśũmēŕ ķēŷ Consumer secret + Ćōńśũmēŕ śēćŕēţ Additional scopes to be passed to the OAuth Provider, separated by space. To replace existing scopes, prefix with *. + Àďďĩţĩōńàĺ śćōƥēś ţō ƀē ƥàśśēď ţō ţĥē ŌÀũţĥ Ƥŕōvĩďēŕ, śēƥàŕàţēď ƀŷ śƥàćē. Ţō ŕēƥĺàćē ēxĩśţĩńĝ śćōƥēś, ƥŕēƒĩx ŵĩţĥ *. Flow settings + Ƒĺōŵ śēţţĩńĝś Flow to use when authenticating existing users. + Ƒĺōŵ ţō ũśē ŵĥēń àũţĥēńţĩćàţĩńĝ ēxĩśţĩńĝ ũśēŕś. Enrollment flow + Ēńŕōĺĺmēńţ ƒĺōŵ Flow to use when enrolling new users. + Ƒĺōŵ ţō ũśē ŵĥēń ēńŕōĺĺĩńĝ ńēŵ ũśēŕś. Load servers + Ĺōàď śēŕvēŕś Re-authenticate with plex + Ŕē-àũţĥēńţĩćàţē ŵĩţĥ ƥĺēx Allow friends to authenticate via Plex, even if you don't share any servers + Àĺĺōŵ ƒŕĩēńďś ţō àũţĥēńţĩćàţē vĩà Ƥĺēx, ēvēń ĩƒ ŷōũ ďōń'ţ śĥàŕē àńŷ śēŕvēŕś Allowed servers + Àĺĺōŵēď śēŕvēŕś Select which server a user has to be a member of to be allowed to authenticate. + Śēĺēćţ ŵĥĩćĥ śēŕvēŕ à ũśēŕ ĥàś ţō ƀē à mēmƀēŕ ōƒ ţō ƀē àĺĺōŵēď ţō àũţĥēńţĩćàţē. SSO URL + ŚŚŌ ŨŔĹ URL that the initial Login request is sent to. + ŨŔĹ ţĥàţ ţĥē ĩńĩţĩàĺ Ĺōĝĩń ŕēǫũēśţ ĩś śēńţ ţō. SLO URL + ŚĹŌ ŨŔĹ Optional URL if the IDP supports Single-Logout. + Ōƥţĩōńàĺ ŨŔĹ ĩƒ ţĥē ĨĎƤ śũƥƥōŕţś Śĩńĝĺē-Ĺōĝōũţ. Also known as Entity ID. Defaults the Metadata URL. + Àĺśō ķńōŵń àś Ēńţĩţŷ ĨĎ. Ďēƒàũĺţś ţĥē Mēţàďàţà ŨŔĹ. Binding Type + ßĩńďĩńĝ Ţŷƥē Redirect binding + Ŕēďĩŕēćţ ƀĩńďĩńĝ Post-auto binding + Ƥōśţ-àũţō ƀĩńďĩńĝ Post binding but the request is automatically sent and the user doesn't have to confirm. + Ƥōśţ ƀĩńďĩńĝ ƀũţ ţĥē ŕēǫũēśţ ĩś àũţōmàţĩćàĺĺŷ śēńţ àńď ţĥē ũśēŕ ďōēśń'ţ ĥàvē ţō ćōńƒĩŕm. Post binding + Ƥōśţ ƀĩńďĩńĝ Signing keypair + Śĩĝńĩńĝ ķēŷƥàĩŕ Keypair which is used to sign outgoing requests. Leave empty to disable signing. + Ķēŷƥàĩŕ ŵĥĩćĥ ĩś ũśēď ţō śĩĝń ōũţĝōĩńĝ ŕēǫũēśţś. Ĺēàvē ēmƥţŷ ţō ďĩśàƀĺē śĩĝńĩńĝ. Allow IDP-initiated logins + Àĺĺōŵ ĨĎƤ-ĩńĩţĩàţēď ĺōĝĩńś Allows authentication flows initiated by the IdP. This can be a security risk, as no validation of the request ID is done. + Àĺĺōŵś àũţĥēńţĩćàţĩōń ƒĺōŵś ĩńĩţĩàţēď ƀŷ ţĥē ĨďƤ. Ţĥĩś ćàń ƀē à śēćũŕĩţŷ ŕĩśķ, àś ńō vàĺĩďàţĩōń ōƒ ţĥē ŕēǫũēśţ ĨĎ ĩś ďōńē. NameID Policy + ŃàmēĨĎ Ƥōĺĩćŷ Persistent + Ƥēŕśĩśţēńţ Email address + Ēmàĩĺ àďďŕēśś Windows + Ŵĩńďōŵś X509 Subject + X509 ŚũƀĴēćţ Transient + Ţŕàńśĩēńţ Delete temporary users after + Ďēĺēţē ţēmƥōŕàŕŷ ũśēŕś àƒţēŕ Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. + Ţĩmē ōƒƒśēţ ŵĥēń ţēmƥōŕàŕŷ ũśēŕś śĥōũĺď ƀē ďēĺēţēď. Ţĥĩś ōńĺŷ àƥƥĺĩēś ĩƒ ŷōũŕ ĨĎƤ ũśēś ţĥē ŃàmēĨĎ Ƒōŕmàţ 'ţŕàńśĩēńţ', àńď ţĥē ũśēŕ ďōēśń'ţ ĺōĝ ōũţ màńũàĺĺŷ. Pre-authentication flow + Ƥŕē-àũţĥēńţĩćàţĩōń ƒĺōŵ Flow used before authentication. + Ƒĺōŵ ũśēď ƀēƒōŕē àũţĥēńţĩćàţĩōń. New source + Ńēŵ śōũŕćē Create a new source. + Ćŕēàţē à ńēŵ śōũŕćē. Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves. + Śōũŕćēś ōƒ ĩďēńţĩţĩēś, ŵĥĩćĥ ćàń ēĩţĥēŕ ƀē śŷńćēď ĩńţō àũţĥēńţĩķ'ś ďàţàƀàśē, ōŕ ćàń ƀē ũśēď ƀŷ ũśēŕś ţō àũţĥēńţĩćàţē àńď ēńŕōĺĺ ţĥēmśēĺvēś. Source(s) + Śōũŕćē(ś) Disabled + Ďĩśàƀĺēď Built-in + ßũĩĺţ-ĩń Update LDAP Source + Ũƥďàţē ĹĎÀƤ Śōũŕćē Not synced yet. + Ńōţ śŷńćēď ŷēţ. Task finished with warnings + Ţàśķ ƒĩńĩśĥēď ŵĩţĥ ŵàŕńĩńĝś Task finished with errors + Ţàśķ ƒĩńĩśĥēď ŵĩţĥ ēŕŕōŕś Last sync: + Ĺàśţ śŷńć: OAuth Source + ŌÀũţĥ Śōũŕćē Generic OpenID Connect + Ĝēńēŕĩć ŌƥēńĨĎ Ćōńńēćţ Unknown provider type + Ũńķńōŵń ƥŕōvĩďēŕ ţŷƥē Details + Ďēţàĩĺś Callback URL + Ćàĺĺƀàćķ ŨŔĹ Access Key + Àććēśś Ķēŷ Update OAuth Source + Ũƥďàţē ŌÀũţĥ Śōũŕćē Diagram + Ďĩàĝŕàm Policy Bindings + Ƥōĺĩćŷ ßĩńďĩńĝś These bindings control which users can access this source. You can only use policies here as access is checked before the user is authenticated. + Ţĥēśē ƀĩńďĩńĝś ćōńţŕōĺ ŵĥĩćĥ ũśēŕś ćàń àććēśś ţĥĩś śōũŕćē. + Ŷōũ ćàń ōńĺŷ ũśē ƥōĺĩćĩēś ĥēŕē àś àććēśś ĩś ćĥēćķēď ƀēƒōŕē ţĥē ũśēŕ ĩś àũţĥēńţĩćàţēď. Update Plex Source + Ũƥďàţē Ƥĺēx Śōũŕćē Update SAML Source + Ũƥďàţē ŚÀMĹ Śōũŕćē Successfully updated mapping. + Śũććēśśƒũĺĺŷ ũƥďàţēď màƥƥĩńĝ. Successfully created mapping. + Śũććēśśƒũĺĺŷ ćŕēàţēď màƥƥĩńĝ. Object field + ŌƀĴēćţ ƒĩēĺď Field of the user object this value is written to. + Ƒĩēĺď ōƒ ţĥē ũśēŕ ōƀĴēćţ ţĥĩś vàĺũē ĩś ŵŕĩţţēń ţō. SAML Attribute Name + ŚÀMĹ Àţţŕĩƀũţē Ńàmē Attribute name used for SAML Assertions. Can be a URN OID, a schema reference, or a any other string. If this property mapping is used for NameID Property, this field is discarded. + Àţţŕĩƀũţē ńàmē ũśēď ƒōŕ ŚÀMĹ Àśśēŕţĩōńś. Ćàń ƀē à ŨŔŃ ŌĨĎ, à śćĥēmà ŕēƒēŕēńćē, ōŕ à àńŷ ōţĥēŕ śţŕĩńĝ. Ĩƒ ţĥĩś ƥŕōƥēŕţŷ màƥƥĩńĝ ĩś ũśēď ƒōŕ ŃàmēĨĎ Ƥŕōƥēŕţŷ, ţĥĩś ƒĩēĺď ĩś ďĩśćàŕďēď. Friendly Name + Ƒŕĩēńďĺŷ Ńàmē Optionally set the 'FriendlyName' value of the Assertion attribute. + Ōƥţĩōńàĺĺŷ śēţ ţĥē 'ƑŕĩēńďĺŷŃàmē' vàĺũē ōƒ ţĥē Àśśēŕţĩōń àţţŕĩƀũţē. Scope name + Śćōƥē ńàmē Scope which the client can specify to access these properties. + Śćōƥē ŵĥĩćĥ ţĥē ćĺĩēńţ ćàń śƥēćĩƒŷ ţō àććēśś ţĥēśē ƥŕōƥēŕţĩēś. Description shown to the user when consenting. If left empty, the user won't be informed. + Ďēśćŕĩƥţĩōń śĥōŵń ţō ţĥē ũśēŕ ŵĥēń ćōńśēńţĩńĝ. Ĩƒ ĺēƒţ ēmƥţŷ, ţĥē ũśēŕ ŵōń'ţ ƀē ĩńƒōŕmēď. Example context data + Ēxàmƥĺē ćōńţēxţ ďàţà Active Directory User + Àćţĩvē Ďĩŕēćţōŕŷ Ũśēŕ Active Directory Group + Àćţĩvē Ďĩŕēćţōŕŷ Ĝŕōũƥ New property mapping + Ńēŵ ƥŕōƥēŕţŷ màƥƥĩńĝ Create a new property mapping. + Ćŕēàţē à ńēŵ ƥŕōƥēŕţŷ màƥƥĩńĝ. Property Mappings + Ƥŕōƥēŕţŷ Màƥƥĩńĝś Control how authentik exposes and interprets information. + Ćōńţŕōĺ ĥōŵ àũţĥēńţĩķ ēxƥōśēś àńď ĩńţēŕƥŕēţś ĩńƒōŕmàţĩōń. Property Mapping(s) + Ƥŕōƥēŕţŷ Màƥƥĩńĝ(ś) Test Property Mapping + Ţēśţ Ƥŕōƥēŕţŷ Màƥƥĩńĝ Hide managed mappings + Ĥĩďē màńàĝēď màƥƥĩńĝś Successfully updated token. + Śũććēśśƒũĺĺŷ ũƥďàţēď ţōķēń. Successfully created token. + Śũććēśśƒũĺĺŷ ćŕēàţēď ţōķēń. Unique identifier the token is referenced by. + Ũńĩǫũē ĩďēńţĩƒĩēŕ ţĥē ţōķēń ĩś ŕēƒēŕēńćēď ƀŷ. Intent + Ĩńţēńţ API Token + ÀƤĨ Ţōķēń Used to access the API programmatically + Ũśēď ţō àććēśś ţĥē ÀƤĨ ƥŕōĝŕàmmàţĩćàĺĺŷ App password. + Àƥƥ ƥàśśŵōŕď. Used to login using a flow executor + Ũśēď ţō ĺōĝĩń ũśĩńĝ à ƒĺōŵ ēxēćũţōŕ Expiring + Ēxƥĩŕĩńĝ If this is selected, the token will expire. Upon expiration, the token will be rotated. + Ĩƒ ţĥĩś ĩś śēĺēćţēď, ţĥē ţōķēń ŵĩĺĺ ēxƥĩŕē. Ũƥōń ēxƥĩŕàţĩōń, ţĥē ţōķēń ŵĩĺĺ ƀē ŕōţàţēď. Expires on + Ēxƥĩŕēś ōń API Access + ÀƤĨ Àććēśś App password + Àƥƥ ƥàśśŵōŕď Verification + Vēŕĩƒĩćàţĩōń Unknown intent + Ũńķńōŵń ĩńţēńţ Tokens + Ţōķēńś Tokens are used throughout authentik for Email validation stages, Recovery keys and API access. + Ţōķēńś àŕē ũśēď ţĥŕōũĝĥōũţ àũţĥēńţĩķ ƒōŕ Ēmàĩĺ vàĺĩďàţĩōń śţàĝēś, Ŕēćōvēŕŷ ķēŷś àńď ÀƤĨ àććēśś. Expires? + Ēxƥĩŕēś? Expiry date + Ēxƥĩŕŷ ďàţē Token(s) + Ţōķēń(ś) Create Token + Ćŕēàţē Ţōķēń Token is managed by authentik. + Ţōķēń ĩś màńàĝēď ƀŷ àũţĥēńţĩķ. Update Token + Ũƥďàţē Ţōķēń Successfully updated tenant. + Śũććēśśƒũĺĺŷ ũƥďàţēď ţēńàńţ. Successfully created tenant. + Śũććēśśƒũĺĺŷ ćŕēàţēď ţēńàńţ. Domain + Ďōmàĩń Matching is done based on domain suffix, so if you enter domain.tld, foo.domain.tld will still match. + Màţćĥĩńĝ ĩś ďōńē ƀàśēď ōń ďōmàĩń śũƒƒĩx, śō ĩƒ ŷōũ ēńţēŕ ďōmàĩń.ţĺď, ƒōō.ďōmàĩń.ţĺď ŵĩĺĺ śţĩĺĺ màţćĥ. Default + Ďēƒàũĺţ Use this tenant for each domain that doesn't have a dedicated tenant. + Ũśē ţĥĩś ţēńàńţ ƒōŕ ēàćĥ ďōmàĩń ţĥàţ ďōēśń'ţ ĥàvē à ďēďĩćàţēď ţēńàńţ. Branding settings + ßŕàńďĩńĝ śēţţĩńĝś Title + Ţĩţĺē Branding shown in page title and several other places. + ßŕàńďĩńĝ śĥōŵń ĩń ƥàĝē ţĩţĺē àńď śēvēŕàĺ ōţĥēŕ ƥĺàćēś. Logo + Ĺōĝō Icon shown in sidebar/header and flow executor. + Ĩćōń śĥōŵń ĩń śĩďēƀàŕ/ĥēàďēŕ àńď ƒĺōŵ ēxēćũţōŕ. Favicon + Ƒàvĩćōń Icon shown in the browser tab. + Ĩćōń śĥōŵń ĩń ţĥē ƀŕōŵśēŕ ţàƀ. Default flows + Ďēƒàũĺţ ƒĺōŵś Flow used to authenticate users. If left empty, the first applicable flow sorted by the slug is used. + Ƒĺōŵ ũśēď ţō àũţĥēńţĩćàţē ũśēŕś. Ĩƒ ĺēƒţ ēmƥţŷ, ţĥē ƒĩŕśţ àƥƥĺĩćàƀĺē ƒĺōŵ śōŕţēď ƀŷ ţĥē śĺũĝ ĩś ũśēď. Invalidation flow + Ĩńvàĺĩďàţĩōń ƒĺōŵ Flow used to logout. If left empty, the first applicable flow sorted by the slug is used. + Ƒĺōŵ ũśēď ţō ĺōĝōũţ. Ĩƒ ĺēƒţ ēmƥţŷ, ţĥē ƒĩŕśţ àƥƥĺĩćàƀĺē ƒĺōŵ śōŕţēď ƀŷ ţĥē śĺũĝ ĩś ũśēď. Recovery flow + Ŕēćōvēŕŷ ƒĺōŵ Recovery flow. If left empty, the first applicable flow sorted by the slug is used. + Ŕēćōvēŕŷ ƒĺōŵ. Ĩƒ ĺēƒţ ēmƥţŷ, ţĥē ƒĩŕśţ àƥƥĺĩćàƀĺē ƒĺōŵ śōŕţēď ƀŷ ţĥē śĺũĝ ĩś ũśēď. Unenrollment flow + Ũńēńŕōĺĺmēńţ ƒĺōŵ If set, users are able to unenroll themselves using this flow. If no flow is set, option is not shown. + Ĩƒ śēţ, ũśēŕś àŕē àƀĺē ţō ũńēńŕōĺĺ ţĥēmśēĺvēś ũśĩńĝ ţĥĩś ƒĺōŵ. Ĩƒ ńō ƒĺōŵ ĩś śēţ, ōƥţĩōń ĩś ńōţ śĥōŵń. User settings flow + Ũśēŕ śēţţĩńĝś ƒĺōŵ If set, users are able to configure details of their profile. + Ĩƒ śēţ, ũśēŕś àŕē àƀĺē ţō ćōńƒĩĝũŕē ďēţàĩĺś ōƒ ţĥēĩŕ ƥŕōƒĩĺē. Device code flow + Ďēvĩćē ćōďē ƒĺōŵ If set, the OAuth Device Code profile can be used, and the selected flow will be used to enter the code. + Ĩƒ śēţ, ţĥē ŌÀũţĥ Ďēvĩćē Ćōďē ƥŕōƒĩĺē ćàń ƀē ũśēď, àńď ţĥē śēĺēćţēď ƒĺōŵ ŵĩĺĺ ƀē ũśēď ţō ēńţēŕ ţĥē ćōďē. Other global settings + Ōţĥēŕ ĝĺōƀàĺ śēţţĩńĝś Web Certificate + Ŵēƀ Ćēŕţĩƒĩćàţē Event retention + Ēvēńţ ŕēţēńţĩōń Duration after which events will be deleted from the database. + Ďũŕàţĩōń àƒţēŕ ŵĥĩćĥ ēvēńţś ŵĩĺĺ ƀē ďēĺēţēď ƒŕōm ţĥē ďàţàƀàśē. When using an external logging solution for archiving, this can be set to "minutes=5". + Ŵĥēń ũśĩńĝ àń ēxţēŕńàĺ ĺōĝĝĩńĝ śōĺũţĩōń ƒōŕ àŕćĥĩvĩńĝ, ţĥĩś ćàń ƀē śēţ ţō "mĩńũţēś=5". This setting only affects new Events, as the expiration is saved per-event. + Ţĥĩś śēţţĩńĝ ōńĺŷ àƒƒēćţś ńēŵ Ēvēńţś, àś ţĥē ēxƥĩŕàţĩōń ĩś śàvēď ƥēŕ-ēvēńţ. Format: "weeks=3;days=2;hours=3,seconds=2". + Ƒōŕmàţ: "ŵēēķś=3;ďàŷś=2;ĥōũŕś=3,śēćōńďś=2". Set custom attributes using YAML or JSON. Any attributes set here will be inherited by users, if the request is handled by this tenant. + Śēţ ćũśţōm àţţŕĩƀũţēś ũśĩńĝ ŶÀMĹ ōŕ ĵŚŌŃ. Àńŷ àţţŕĩƀũţēś śēţ ĥēŕē ŵĩĺĺ ƀē ĩńĥēŕĩţēď ƀŷ ũśēŕś, ĩƒ ţĥē ŕēǫũēśţ ĩś ĥàńďĺēď ƀŷ ţĥĩś ţēńàńţ. Tenants + Ţēńàńţś Configure visual settings and defaults for different domains. + Ćōńƒĩĝũŕē vĩśũàĺ śēţţĩńĝś àńď ďēƒàũĺţś ƒōŕ ďĩƒƒēŕēńţ ďōmàĩńś. Default? + Ďēƒàũĺţ? Tenant(s) + Ţēńàńţ(ś) Update Tenant + Ũƥďàţē Ţēńàńţ Create Tenant + Ćŕēàţē Ţēńàńţ Policies + Ƥōĺĩćĩēś Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages. + Àĺĺōŵ ũśēŕś ţō ũśē Àƥƥĺĩćàţĩōńś ƀàśēď ōń ƥŕōƥēŕţĩēś, ēńƒōŕćē Ƥàśśŵōŕď Ćŕĩţēŕĩà àńď śēĺēćţĩvēĺŷ àƥƥĺŷ Śţàĝēś. Assigned to object(s). + Àśśĩĝńēď ţō ōƀĴēćţ(ś). Warning: Policy is not assigned. + Ŵàŕńĩńĝ: Ƥōĺĩćŷ ĩś ńōţ àśśĩĝńēď. Test Policy + Ţēśţ Ƥōĺĩćŷ Policy / Policies + Ƥōĺĩćŷ / Ƥōĺĩćĩēś Successfully cleared policy cache + Śũććēśśƒũĺĺŷ ćĺēàŕēď ƥōĺĩćŷ ćàćĥē Failed to delete policy cache + Ƒàĩĺēď ţō ďēĺēţē ƥōĺĩćŷ ćàćĥē Clear cache + Ćĺēàŕ ćàćĥē Clear Policy cache + Ćĺēàŕ Ƥōĺĩćŷ ćàćĥē Are you sure you want to clear the policy cache? This will cause all policies to be re-evaluated on their next usage. + Àŕē ŷōũ śũŕē ŷōũ ŵàńţ ţō ćĺēàŕ ţĥē ƥōĺĩćŷ ćàćĥē? Ţĥĩś ŵĩĺĺ ćàũśē àĺĺ ƥōĺĩćĩēś ţō ƀē ŕē-ēvàĺũàţēď ōń ţĥēĩŕ ńēxţ ũśàĝē. Reputation scores + Ŕēƥũţàţĩōń śćōŕēś Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login. + Ŕēƥũţàţĩōń ƒōŕ ĨƤ àńď ũśēŕ ĩďēńţĩƒĩēŕś. Śćōŕēś àŕē ďēćŕēàśēď ƒōŕ ēàćĥ ƒàĩĺēď ĺōĝĩń àńď ĩńćŕēàśēď ƒōŕ ēàćĥ śũććēśśƒũĺ ĺōĝĩń. IP + ĨƤ Score + Śćōŕē Updated + Ũƥďàţēď Reputation + Ŕēƥũţàţĩōń Groups + Ĝŕōũƥś Group users together and give them permissions based on the membership. + Ĝŕōũƥ ũśēŕś ţōĝēţĥēŕ àńď ĝĩvē ţĥēm ƥēŕmĩśśĩōńś ƀàśēď ōń ţĥē mēmƀēŕśĥĩƥ. Superuser privileges? + Śũƥēŕũśēŕ ƥŕĩvĩĺēĝēś? Group(s) + Ĝŕōũƥ(ś) Create Group + Ćŕēàţē Ĝŕōũƥ Create group + Ćŕēàţē ĝŕōũƥ Enabling this toggle will create a group named after the user, with the user as member. + Ēńàƀĺĩńĝ ţĥĩś ţōĝĝĺē ŵĩĺĺ ćŕēàţē à ĝŕōũƥ ńàmēď àƒţēŕ ţĥē ũśēŕ, ŵĩţĥ ţĥē ũśēŕ àś mēmƀēŕ. Use the username and password below to authenticate. The password can be retrieved later on the Tokens page. + Ũśē ţĥē ũśēŕńàmē àńď ƥàśśŵōŕď ƀēĺōŵ ţō àũţĥēńţĩćàţē. Ţĥē ƥàśśŵōŕď ćàń ƀē ŕēţŕĩēvēď ĺàţēŕ ōń ţĥē Ţōķēńś ƥàĝē. Password + Ƥàśśŵōŕď Valid for 360 days, after which the password will automatically rotate. You can copy the password from the Token List. + Vàĺĩď ƒōŕ 360 ďàŷś, àƒţēŕ ŵĥĩćĥ ţĥē ƥàśśŵōŕď ŵĩĺĺ àũţōmàţĩćàĺĺŷ ŕōţàţē. Ŷōũ ćàń ćōƥŷ ţĥē ƥàśśŵōŕď ƒŕōm ţĥē Ţōķēń Ĺĩśţ. The following objects use + Ţĥē ƒōĺĺōŵĩńĝ ōƀĴēćţś ũśē connecting object will be deleted + ćōńńēćţĩńĝ ōƀĴēćţ ŵĩĺĺ ƀē ďēĺēţēď Successfully updated + Śũććēśśƒũĺĺŷ ũƥďàţēď Failed to update : + Ƒàĩĺēď ţō ũƥďàţē : Are you sure you want to update ""? + Àŕē ŷōũ śũŕē ŷōũ ŵàńţ ţō ũƥďàţē ""? Successfully updated password. + Śũććēśśƒũĺĺŷ ũƥďàţēď ƥàśśŵōŕď. Successfully sent email. + Śũććēśśƒũĺĺŷ śēńţ ēmàĩĺ. Email stage + Ēmàĩĺ śţàĝē Successfully added user(s). + Śũććēśśƒũĺĺŷ àďďēď ũśēŕ(ś). Users to add + Ũśēŕś ţō àďď User(s) + Ũśēŕ(ś) Remove Users(s) + Ŕēmōvē Ũśēŕś(ś) Are you sure you want to remove the selected users from the group ? + Àŕē ŷōũ śũŕē ŷōũ ŵàńţ ţō ŕēmōvē ţĥē śēĺēćţēď ũśēŕś ƒŕōm ţĥē ĝŕōũƥ ? Remove + Ŕēmōvē Impersonate + Ĩmƥēŕśōńàţē User status + Ũśēŕ śţàţũś Change status + Ćĥàńĝē śţàţũś Deactivate + Ďēàćţĩvàţē Update password + Ũƥďàţē ƥàśśŵōŕď Set password + Śēţ ƥàśśŵōŕď Successfully generated recovery link + Śũććēśśƒũĺĺŷ ĝēńēŕàţēď ŕēćōvēŕŷ ĺĩńķ No recovery flow is configured. + Ńō ŕēćōvēŕŷ ƒĺōŵ ĩś ćōńƒĩĝũŕēď. Copy recovery link + Ćōƥŷ ŕēćōvēŕŷ ĺĩńķ Send link + Śēńď ĺĩńķ Send recovery link to user + Śēńď ŕēćōvēŕŷ ĺĩńķ ţō ũśēŕ Email recovery link + Ēmàĩĺ ŕēćōvēŕŷ ĺĩńķ Recovery link cannot be emailed, user has no email address saved. + Ŕēćōvēŕŷ ĺĩńķ ćàńńōţ ƀē ēmàĩĺēď, ũśēŕ ĥàś ńō ēmàĩĺ àďďŕēśś śàvēď. To let a user directly reset a their password, configure a recovery flow on the currently active tenant. + Ţō ĺēţ à ũśēŕ ďĩŕēćţĺŷ ŕēśēţ à ţĥēĩŕ ƥàśśŵōŕď, ćōńƒĩĝũŕē à ŕēćōvēŕŷ ƒĺōŵ ōń ţĥē ćũŕŕēńţĺŷ àćţĩvē ţēńàńţ. Add User + Àďď Ũśēŕ Warning: This group is configured with superuser access. Added users will have superuser access. + Ŵàŕńĩńĝ: Ţĥĩś ĝŕōũƥ ĩś ćōńƒĩĝũŕēď ŵĩţĥ śũƥēŕũśēŕ àććēśś. Àďďēď ũśēŕś ŵĩĺĺ ĥàvē śũƥēŕũśēŕ àććēśś. Add existing user + Àďď ēxĩśţĩńĝ ũśēŕ Create user + Ćŕēàţē ũśēŕ Create User + Ćŕēàţē Ũśēŕ Create Service account + Ćŕēàţē Śēŕvĩćē àććōũńţ Hide service-accounts + Ĥĩďē śēŕvĩćē-àććōũńţś Group Info + Ĝŕōũƥ Ĩńƒō Notes + Ńōţēś Edit the notes attribute of this group to add notes here. + Ēďĩţ ţĥē ńōţēś àţţŕĩƀũţē ōƒ ţĥĩś ĝŕōũƥ ţō àďď ńōţēś ĥēŕē. Users + Ũśēŕś Root + Ŕōōţ Warning: You're about to delete the user you're logged in as (). Proceed at your own risk. + Ŵàŕńĩńĝ: Ŷōũ'ŕē àƀōũţ ţō ďēĺēţē ţĥē ũśēŕ ŷōũ'ŕē ĺōĝĝēď ĩń àś (). Ƥŕōćēēď àţ ŷōũŕ ōŵń ŕĩśķ. Hide deactivated user + Ĥĩďē ďēàćţĩvàţēď ũśēŕ User folders + Ũśēŕ ƒōĺďēŕś Successfully added user to group(s). + Śũććēśśƒũĺĺŷ àďďēď ũśēŕ ţō ĝŕōũƥ(ś). Groups to add + Ĝŕōũƥś ţō àďď Remove from Group(s) + Ŕēmōvē ƒŕōm Ĝŕōũƥ(ś) Are you sure you want to remove user from the following groups? + Àŕē ŷōũ śũŕē ŷōũ ŵàńţ ţō ŕēmōvē ũśēŕ ƒŕōm ţĥē ƒōĺĺōŵĩńĝ ĝŕōũƥś? Add Group + Àďď Ĝŕōũƥ Add to existing group + Àďď ţō ēxĩśţĩńĝ ĝŕōũƥ Add new group + Àďď ńēŵ ĝŕōũƥ Application authorizations + Àƥƥĺĩćàţĩōń àũţĥōŕĩźàţĩōńś Revoked? + Ŕēvōķēď? Expires + Ēxƥĩŕēś ID Token + ĨĎ Ţōķēń Refresh Tokens(s) + Ŕēƒŕēśĥ Ţōķēńś(ś) Last IP + Ĺàśţ ĨƤ Session(s) + Śēśśĩōń(ś) Expiry + Ēxƥĩŕŷ (Current session) + (Ćũŕŕēńţ śēśśĩōń) Permissions + Ƥēŕmĩśśĩōńś Consent(s) + Ćōńśēńţ(ś) Successfully updated device. + Śũććēśśƒũĺĺŷ ũƥďàţēď ďēvĩćē. Static tokens + Śţàţĩć ţōķēńś TOTP Device + ŢŌŢƤ Ďēvĩćē Enroll + Ēńŕōĺĺ Device(s) + Ďēvĩćē(ś) Update Device + Ũƥďàţē Ďēvĩćē Confirmed + Ćōńƒĩŕmēď User Info + Ũśēŕ Ĩńƒō To create a recovery link, the current tenant needs to have a recovery flow configured. + Ţō ćŕēàţē à ŕēćōvēŕŷ ĺĩńķ, ţĥē ćũŕŕēńţ ţēńàńţ ńēēďś ţō ĥàvē à ŕēćōvēŕŷ ƒĺōŵ ćōńƒĩĝũŕēď. Reset Password + Ŕēśēţ Ƥàśśŵōŕď Actions over the last week (per 8 hours) + Àćţĩōńś ōvēŕ ţĥē ĺàśţ ŵēēķ (ƥēŕ 8 ĥōũŕś) Edit the notes attribute of this user to add notes here. + Ēďĩţ ţĥē ńōţēś àţţŕĩƀũţē ōƒ ţĥĩś ũśēŕ ţō àďď ńōţēś ĥēŕē. Sessions + Śēśśĩōńś User events + Ũśēŕ ēvēńţś Explicit Consent + Ēxƥĺĩćĩţ Ćōńśēńţ OAuth Refresh Tokens + ŌÀũţĥ Ŕēƒŕēśĥ Ţōķēńś MFA Authenticators + MƑÀ Àũţĥēńţĩćàţōŕś Successfully updated invitation. + Śũććēśśƒũĺĺŷ ũƥďàţēď ĩńvĩţàţĩōń. Successfully created invitation. + Śũććēśśƒũĺĺŷ ćŕēàţēď ĩńvĩţàţĩōń. Flow + Ƒĺōŵ When selected, the invite will only be usable with the flow. By default the invite is accepted on all flows with invitation stages. + Ŵĥēń śēĺēćţēď, ţĥē ĩńvĩţē ŵĩĺĺ ōńĺŷ ƀē ũśàƀĺē ŵĩţĥ ţĥē ƒĺōŵ. ßŷ ďēƒàũĺţ ţĥē ĩńvĩţē ĩś àććēƥţēď ōń àĺĺ ƒĺōŵś ŵĩţĥ ĩńvĩţàţĩōń śţàĝēś. Optional data which is loaded into the flow's 'prompt_data' context variable. YAML or JSON. + Ōƥţĩōńàĺ ďàţà ŵĥĩćĥ ĩś ĺōàďēď ĩńţō ţĥē ƒĺōŵ'ś 'ƥŕōmƥţ_ďàţà' ćōńţēxţ vàŕĩàƀĺē. ŶÀMĹ ōŕ ĵŚŌŃ. Single use + Śĩńĝĺē ũśē When enabled, the invitation will be deleted after usage. + Ŵĥēń ēńàƀĺēď, ţĥē ĩńvĩţàţĩōń ŵĩĺĺ ƀē ďēĺēţēď àƒţēŕ ũśàĝē. Select an enrollment flow + Śēĺēćţ àń ēńŕōĺĺmēńţ ƒĺōŵ Link to use the invitation. + Ĺĩńķ ţō ũśē ţĥē ĩńvĩţàţĩōń. Invitations + Ĩńvĩţàţĩōńś Create Invitation Links to enroll Users, and optionally force specific attributes of their account. + Ćŕēàţē Ĩńvĩţàţĩōń Ĺĩńķś ţō ēńŕōĺĺ Ũśēŕś, àńď ōƥţĩōńàĺĺŷ ƒōŕćē śƥēćĩƒĩć àţţŕĩƀũţēś ōƒ ţĥēĩŕ àććōũńţ. Created by + Ćŕēàţēď ƀŷ Invitation(s) + Ĩńvĩţàţĩōń(ś) Invitation not limited to any flow, and can be used with any enrollment flow. + Ĩńvĩţàţĩōń ńōţ ĺĩmĩţēď ţō àńŷ ƒĺōŵ, àńď ćàń ƀē ũśēď ŵĩţĥ àńŷ ēńŕōĺĺmēńţ ƒĺōŵ. Update Invitation + Ũƥďàţē Ĩńvĩţàţĩōń Create Invitation + Ćŕēàţē Ĩńvĩţàţĩōń Warning: No invitation stage is bound to any flow. Invitations will not work as expected. + Ŵàŕńĩńĝ: Ńō ĩńvĩţàţĩōń śţàĝē ĩś ƀōũńď ţō àńŷ ƒĺōŵ. Ĩńvĩţàţĩōńś ŵĩĺĺ ńōţ ŵōŕķ àś ēxƥēćţēď. Auto-detect (based on your browser) + Àũţō-ďēţēćţ (ƀàśēď ōń ŷōũŕ ƀŕōŵśēŕ) Required. + Ŕēǫũĩŕēď. Continue + Ćōńţĩńũē Successfully updated prompt. + Śũććēśśƒũĺĺŷ ũƥďàţēď ƥŕōmƥţ. Successfully created prompt. + Śũććēśśƒũĺĺŷ ćŕēàţēď ƥŕōmƥţ. Text: Simple Text input + Ţēxţ: Śĩmƥĺē Ţēxţ ĩńƥũţ Text Area: Multiline text input + Ţēxţ Àŕēà: Mũĺţĩĺĩńē ţēxţ ĩńƥũţ Text (read-only): Simple Text input, but cannot be edited. + Ţēxţ (ŕēàď-ōńĺŷ): Śĩmƥĺē Ţēxţ ĩńƥũţ, ƀũţ ćàńńōţ ƀē ēďĩţēď. Text Area (read-only): Multiline text input, but cannot be edited. + Ţēxţ Àŕēà (ŕēàď-ōńĺŷ): Mũĺţĩĺĩńē ţēxţ ĩńƥũţ, ƀũţ ćàńńōţ ƀē ēďĩţēď. Username: Same as Text input, but checks for and prevents duplicate usernames. + Ũśēŕńàmē: Śàmē àś Ţēxţ ĩńƥũţ, ƀũţ ćĥēćķś ƒōŕ àńď ƥŕēvēńţś ďũƥĺĩćàţē ũśēŕńàmēś. Email: Text field with Email type. + Ēmàĩĺ: Ţēxţ ƒĩēĺď ŵĩţĥ Ēmàĩĺ ţŷƥē. Password: Masked input, multiple inputs of this type on the same prompt need to be identical. + Ƥàśśŵōŕď: Màśķēď ĩńƥũţ, mũĺţĩƥĺē ĩńƥũţś ōƒ ţĥĩś ţŷƥē ōń ţĥē śàmē ƥŕōmƥţ ńēēď ţō ƀē ĩďēńţĩćàĺ. Number + Ńũmƀēŕ Checkbox + Ćĥēćķƀōx Radio Button Group (fixed choice) + Ŕàďĩō ßũţţōń Ĝŕōũƥ (ƒĩxēď ćĥōĩćē) Dropdown (fixed choice) + Ďŕōƥďōŵń (ƒĩxēď ćĥōĩćē) Date + Ďàţē Date Time + Ďàţē Ţĩmē File + Ƒĩĺē Separator: Static Separator Line + Śēƥàŕàţōŕ: Śţàţĩć Śēƥàŕàţōŕ Ĺĩńē Hidden: Hidden field, can be used to insert data into form. + Ĥĩďďēń: Ĥĩďďēń ƒĩēĺď, ćàń ƀē ũśēď ţō ĩńśēŕţ ďàţà ĩńţō ƒōŕm. Static: Static value, displayed as-is. + Śţàţĩć: Śţàţĩć vàĺũē, ďĩśƥĺàŷēď àś-ĩś. authentik: Locale: Displays a list of locales authentik supports. + àũţĥēńţĩķ: Ĺōćàĺē: Ďĩśƥĺàŷś à ĺĩśţ ōƒ ĺōćàĺēś àũţĥēńţĩķ śũƥƥōŕţś. Preview errors + Ƥŕēvĩēŵ ēŕŕōŕś Data preview + Ďàţà ƥŕēvĩēŵ Unique name of this field, used for selecting fields in prompt stages. + Ũńĩǫũē ńàmē ōƒ ţĥĩś ƒĩēĺď, ũśēď ƒōŕ śēĺēćţĩńĝ ƒĩēĺďś ĩń ƥŕōmƥţ śţàĝēś. Field Key + Ƒĩēĺď Ķēŷ Name of the form field, also used to store the value. + Ńàmē ōƒ ţĥē ƒōŕm ƒĩēĺď, àĺśō ũśēď ţō śţōŕē ţĥē vàĺũē. When used in conjunction with a User Write stage, use attributes.foo to write attributes. + Ŵĥēń ũśēď ĩń ćōńĴũńćţĩōń ŵĩţĥ à Ũśēŕ Ŵŕĩţē śţàĝē, ũśē àţţŕĩƀũţēś.ƒōō ţō ŵŕĩţē àţţŕĩƀũţēś. Label + Ĺàƀēĺ Label shown next to/above the prompt. + Ĺàƀēĺ śĥōŵń ńēxţ ţō/àƀōvē ţĥē ƥŕōmƥţ. Required + Ŕēǫũĩŕēď Interpret placeholder as expression + Ĩńţēŕƥŕēţ ƥĺàćēĥōĺďēŕ àś ēxƥŕēśśĩōń When checked, the placeholder will be evaluated in the same way a property mapping is. If the evaluation fails, the placeholder itself is returned. + Ŵĥēń ćĥēćķēď, ţĥē ƥĺàćēĥōĺďēŕ ŵĩĺĺ ƀē ēvàĺũàţēď ĩń ţĥē śàmē ŵàŷ à ƥŕōƥēŕţŷ màƥƥĩńĝ ĩś. + Ĩƒ ţĥē ēvàĺũàţĩōń ƒàĩĺś, ţĥē ƥĺàćēĥōĺďēŕ ĩţśēĺƒ ĩś ŕēţũŕńēď. Placeholder + Ƥĺàćēĥōĺďēŕ Optionally provide a short hint that describes the expected input value. When creating a fixed choice field, enable interpreting as expression and return a list to return multiple choices. + Ōƥţĩōńàĺĺŷ ƥŕōvĩďē à śĥōŕţ ĥĩńţ ţĥàţ ďēśćŕĩƀēś ţĥē ēxƥēćţēď ĩńƥũţ vàĺũē. + Ŵĥēń ćŕēàţĩńĝ à ƒĩxēď ćĥōĩćē ƒĩēĺď, ēńàƀĺē ĩńţēŕƥŕēţĩńĝ àś ēxƥŕēśśĩōń àńď ŕēţũŕń à + ĺĩśţ ţō ŕēţũŕń mũĺţĩƥĺē ćĥōĩćēś. Interpret initial value as expression + Ĩńţēŕƥŕēţ ĩńĩţĩàĺ vàĺũē àś ēxƥŕēśśĩōń When checked, the initial value will be evaluated in the same way a property mapping is. If the evaluation fails, the initial value itself is returned. + Ŵĥēń ćĥēćķēď, ţĥē ĩńĩţĩàĺ vàĺũē ŵĩĺĺ ƀē ēvàĺũàţēď ĩń ţĥē śàmē ŵàŷ à ƥŕōƥēŕţŷ màƥƥĩńĝ ĩś. + Ĩƒ ţĥē ēvàĺũàţĩōń ƒàĩĺś, ţĥē ĩńĩţĩàĺ vàĺũē ĩţśēĺƒ ĩś ŕēţũŕńēď. Initial value + Ĩńĩţĩàĺ vàĺũē Optionally pre-fill the input with an initial value. When creating a fixed choice field, enable interpreting as expression and return a list to return multiple default choices. + Ōƥţĩōńàĺĺŷ ƥŕē-ƒĩĺĺ ţĥē ĩńƥũţ ŵĩţĥ àń ĩńĩţĩàĺ vàĺũē. + Ŵĥēń ćŕēàţĩńĝ à ƒĩxēď ćĥōĩćē ƒĩēĺď, ēńàƀĺē ĩńţēŕƥŕēţĩńĝ àś ēxƥŕēśśĩōń àńď + ŕēţũŕń à ĺĩśţ ţō ŕēţũŕń mũĺţĩƥĺē ďēƒàũĺţ ćĥōĩćēś. Help text + Ĥēĺƥ ţēxţ Any HTML can be used. + Àńŷ ĤŢMĹ ćàń ƀē ũśēď. Prompts + Ƥŕōmƥţś Single Prompts that can be used for Prompt Stages. + Śĩńĝĺē Ƥŕōmƥţś ţĥàţ ćàń ƀē ũśēď ƒōŕ Ƥŕōmƥţ Śţàĝēś. Field + Ƒĩēĺď Stages + Śţàĝēś Prompt(s) + Ƥŕōmƥţ(ś) Update Prompt + Ũƥďàţē Ƥŕōmƥţ Create Prompt + Ćŕēàţē Ƥŕōmƥţ Target + Ţàŕĝēţ Stage + Śţàĝē Evaluate when flow is planned + Ēvàĺũàţē ŵĥēń ƒĺōŵ ĩś ƥĺàńńēď Evaluate policies during the Flow planning process. + Ēvàĺũàţē ƥōĺĩćĩēś ďũŕĩńĝ ţĥē Ƒĺōŵ ƥĺàńńĩńĝ ƥŕōćēśś. Evaluate when stage is run + Ēvàĺũàţē ŵĥēń śţàĝē ĩś ŕũń Evaluate policies before the Stage is present to the user. + Ēvàĺũàţē ƥōĺĩćĩēś ƀēƒōŕē ţĥē Śţàĝē ĩś ƥŕēśēńţ ţō ţĥē ũśēŕ. Invalid response behavior + Ĩńvàĺĩď ŕēśƥōńśē ƀēĥàvĩōŕ Returns the error message and a similar challenge to the executor + Ŕēţũŕńś ţĥē ēŕŕōŕ mēśśàĝē àńď à śĩmĩĺàŕ ćĥàĺĺēńĝē ţō ţĥē ēxēćũţōŕ Restarts the flow from the beginning + Ŕēśţàŕţś ţĥē ƒĺōŵ ƒŕōm ţĥē ƀēĝĩńńĩńĝ Restarts the flow from the beginning, while keeping the flow context + Ŕēśţàŕţś ţĥē ƒĺōŵ ƒŕōm ţĥē ƀēĝĩńńĩńĝ, ŵĥĩĺē ķēēƥĩńĝ ţĥē ƒĺōŵ ćōńţēxţ Configure how the flow executor should handle an invalid response to a challenge given by this bound stage. + Ćōńƒĩĝũŕē ĥōŵ ţĥē ƒĺōŵ ēxēćũţōŕ śĥōũĺď ĥàńďĺē àń ĩńvàĺĩď ŕēśƥōńśē ţō à ćĥàĺĺēńĝē ĝĩvēń ƀŷ ţĥĩś ƀōũńď śţàĝē. Successfully updated stage. + Śũććēśśƒũĺĺŷ ũƥďàţēď śţàĝē. Successfully created stage. + Śũććēśśƒũĺĺŷ ćŕēàţēď śţàĝē. Stage used to configure a duo-based authenticator. This stage should be used for configuration flows. + Śţàĝē ũśēď ţō ćōńƒĩĝũŕē à ďũō-ƀàśēď àũţĥēńţĩćàţōŕ. Ţĥĩś śţàĝē śĥōũĺď ƀē ũśēď ƒōŕ ćōńƒĩĝũŕàţĩōń ƒĺōŵś. Authenticator type name + Àũţĥēńţĩćàţōŕ ţŷƥē ńàmē Display name of this authenticator, used by users when they enroll an authenticator. + Ďĩśƥĺàŷ ńàmē ōƒ ţĥĩś àũţĥēńţĩćàţōŕ, ũśēď ƀŷ ũśēŕś ŵĥēń ţĥēŷ ēńŕōĺĺ àń àũţĥēńţĩćàţōŕ. API Hostname + ÀƤĨ Ĥōśţńàmē Duo Auth API + Ďũō Àũţĥ ÀƤĨ Integration key + Ĩńţēĝŕàţĩōń ķēŷ Secret key + Śēćŕēţ ķēŷ Duo Admin API (optional) + Ďũō Àďmĩń ÀƤĨ (ōƥţĩōńàĺ) When using a Duo MFA, Access or Beyond plan, an Admin API application can be created. This will allow authentik to import devices automatically. + Ŵĥēń ũśĩńĝ à Ďũō MƑÀ, Àććēśś ōŕ ßēŷōńď ƥĺàń, àń Àďmĩń ÀƤĨ àƥƥĺĩćàţĩōń ćàń ƀē ćŕēàţēď. + Ţĥĩś ŵĩĺĺ àĺĺōŵ àũţĥēńţĩķ ţō ĩmƥōŕţ ďēvĩćēś àũţōmàţĩćàĺĺŷ. Stage-specific settings + Śţàĝē-śƥēćĩƒĩć śēţţĩńĝś Configuration flow + Ćōńƒĩĝũŕàţĩōń ƒĺōŵ Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage. + Ƒĺōŵ ũśēď ƀŷ àń àũţĥēńţĩćàţēď ũśēŕ ţō ćōńƒĩĝũŕē ţĥĩś Śţàĝē. Ĩƒ ēmƥţŷ, ũśēŕ ŵĩĺĺ ńōţ ƀē àƀĺē ţō ćōńƒĩĝũŕē ţĥĩś śţàĝē. Twilio Account SID + Ţŵĩĺĩō Àććōũńţ ŚĨĎ Get this value from https://console.twilio.com + Ĝēţ ţĥĩś vàĺũē ƒŕōm ĥţţƥś://ćōńśōĺē.ţŵĩĺĩō.ćōm Twilio Auth Token + Ţŵĩĺĩō Àũţĥ Ţōķēń Authentication Type + Àũţĥēńţĩćàţĩōń Ţŷƥē Basic Auth + ßàśĩć Àũţĥ Bearer Token + ßēàŕēŕ Ţōķēń External API URL + Ēxţēŕńàĺ ÀƤĨ ŨŔĹ This is the full endpoint to send POST requests to. + Ţĥĩś ĩś ţĥē ƒũĺĺ ēńďƥōĩńţ ţō śēńď ƤŌŚŢ ŕēǫũēśţś ţō. API Auth Username + ÀƤĨ Àũţĥ Ũśēŕńàmē This is the username to be used with basic auth or the token when used with bearer token + Ţĥĩś ĩś ţĥē ũśēŕńàmē ţō ƀē ũśēď ŵĩţĥ ƀàśĩć àũţĥ ōŕ ţĥē ţōķēń ŵĥēń ũśēď ŵĩţĥ ƀēàŕēŕ ţōķēń API Auth password + ÀƤĨ Àũţĥ ƥàśśŵōŕď This is the password to be used with basic auth + Ţĥĩś ĩś ţĥē ƥàśśŵōŕď ţō ƀē ũśēď ŵĩţĥ ƀàśĩć àũţĥ Mapping + Màƥƥĩńĝ Modify the payload sent to the custom provider. + Mōďĩƒŷ ţĥē ƥàŷĺōàď śēńţ ţō ţĥē ćũśţōm ƥŕōvĩďēŕ. Stage used to configure an SMS-based TOTP authenticator. + Śţàĝē ũśēď ţō ćōńƒĩĝũŕē àń ŚMŚ-ƀàśēď ŢŌŢƤ àũţĥēńţĩćàţōŕ. Twilio + Ţŵĩĺĩō Generic + Ĝēńēŕĩć From number + Ƒŕōm ńũmƀēŕ Number the SMS will be sent from. + Ńũmƀēŕ ţĥē ŚMŚ ŵĩĺĺ ƀē śēńţ ƒŕōm. Hash phone number + Ĥàśĥ ƥĥōńē ńũmƀēŕ If enabled, only a hash of the phone number will be saved. This can be done for data-protection reasons. Devices created from a stage with this enabled cannot be used with the authenticator validation stage. + Ĩƒ ēńàƀĺēď, ōńĺŷ à ĥàśĥ ōƒ ţĥē ƥĥōńē ńũmƀēŕ ŵĩĺĺ ƀē śàvēď. Ţĥĩś ćàń ƀē ďōńē ƒōŕ ďàţà-ƥŕōţēćţĩōń ŕēàśōńś. Ďēvĩćēś ćŕēàţēď ƒŕōm à śţàĝē ŵĩţĥ ţĥĩś ēńàƀĺēď ćàńńōţ ƀē ũśēď ŵĩţĥ ţĥē àũţĥēńţĩćàţōŕ vàĺĩďàţĩōń śţàĝē. Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows. + Śţàĝē ũśēď ţō ćōńƒĩĝũŕē à śţàţĩć àũţĥēńţĩćàţōŕ (ĩ.ē. śţàţĩć ţōķēńś). Ţĥĩś śţàĝē śĥōũĺď ƀē ũśēď ƒōŕ ćōńƒĩĝũŕàţĩōń ƒĺōŵś. Token count + Ţōķēń ćōũńţ Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenticator). + Śţàĝē ũśēď ţō ćōńƒĩĝũŕē à ŢŌŢƤ àũţĥēńţĩćàţōŕ (ĩ.ē. Àũţĥŷ/Ĝōōĝĺē Àũţĥēńţĩćàţōŕ). Digits + Ďĩĝĩţś 6 digits, widely compatible + 6 ďĩĝĩţś, ŵĩďēĺŷ ćōmƥàţĩƀĺē 8 digits, not compatible with apps like Google Authenticator + 8 ďĩĝĩţś, ńōţ ćōmƥàţĩƀĺē ŵĩţĥ àƥƥś ĺĩķē Ĝōōĝĺē Àũţĥēńţĩćàţōŕ Stage used to validate any authenticator. This stage should be used during authentication or authorization flows. + Śţàĝē ũśēď ţō vàĺĩďàţē àńŷ àũţĥēńţĩćàţōŕ. Ţĥĩś śţàĝē śĥōũĺď ƀē ũśēď ďũŕĩńĝ àũţĥēńţĩćàţĩōń ōŕ àũţĥōŕĩźàţĩōń ƒĺōŵś. Device classes + Ďēvĩćē ćĺàśśēś Static Tokens + Śţàţĩć Ţōķēńś TOTP Authenticators + ŢŌŢƤ Àũţĥēńţĩćàţōŕś WebAuthn Authenticators + ŴēƀÀũţĥń Àũţĥēńţĩćàţōŕś Duo Authenticators + Ďũō Àũţĥēńţĩćàţōŕś SMS-based Authenticators + ŚMŚ-ƀàśēď Àũţĥēńţĩćàţōŕś Device classes which can be used to authenticate. + Ďēvĩćē ćĺàśśēś ŵĥĩćĥ ćàń ƀē ũśēď ţō àũţĥēńţĩćàţē. Last validation threshold + Ĺàśţ vàĺĩďàţĩōń ţĥŕēśĥōĺď If any of the devices user of the types selected above have been used within this duration, this stage will be skipped. + Ĩƒ àńŷ ōƒ ţĥē ďēvĩćēś ũśēŕ ōƒ ţĥē ţŷƥēś śēĺēćţēď àƀōvē ĥàvē ƀēēń ũśēď ŵĩţĥĩń ţĥĩś ďũŕàţĩōń, ţĥĩś śţàĝē ŵĩĺĺ ƀē śķĩƥƥēď. Not configured action + Ńōţ ćōńƒĩĝũŕēď àćţĩōń Force the user to configure an authenticator + Ƒōŕćē ţĥē ũśēŕ ţō ćōńƒĩĝũŕē àń àũţĥēńţĩćàţōŕ Deny the user access + Ďēńŷ ţĥē ũśēŕ àććēśś WebAuthn User verification + ŴēƀÀũţĥń Ũśēŕ vēŕĩƒĩćàţĩōń User verification must occur. + Ũśēŕ vēŕĩƒĩćàţĩōń mũśţ ōććũŕ. User verification is preferred if available, but not required. + Ũśēŕ vēŕĩƒĩćàţĩōń ĩś ƥŕēƒēŕŕēď ĩƒ àvàĩĺàƀĺē, ƀũţ ńōţ ŕēǫũĩŕēď. User verification should not occur. + Ũśēŕ vēŕĩƒĩćàţĩōń śĥōũĺď ńōţ ōććũŕ. Configuration stages + Ćōńƒĩĝũŕàţĩōń śţàĝēś Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again. + Śţàĝēś ũśēď ţō ćōńƒĩĝũŕē Àũţĥēńţĩćàţōŕ ŵĥēń ũśēŕ ďōēśń'ţ ĥàvē àńŷ ćōmƥàţĩƀĺē ďēvĩćēś. Àƒţēŕ ţĥĩś ćōńƒĩĝũŕàţĩōń Śţàĝē ƥàśśēś, ţĥē ũśēŕ ĩś ńōţ ƥŕōmƥţēď àĝàĩń. When multiple stages are selected, the user can choose which one they want to enroll. + Ŵĥēń mũĺţĩƥĺē śţàĝēś àŕē śēĺēćţēď, ţĥē ũśēŕ ćàń ćĥōōśē ŵĥĩćĥ ōńē ţĥēŷ ŵàńţ ţō ēńŕōĺĺ. User verification + Ũśēŕ vēŕĩƒĩćàţĩōń Resident key requirement + Ŕēśĩďēńţ ķēŷ ŕēǫũĩŕēmēńţ Authenticator Attachment + Àũţĥēńţĩćàţōŕ Àţţàćĥmēńţ No preference is sent + Ńō ƥŕēƒēŕēńćē ĩś śēńţ A non-removable authenticator, like TouchID or Windows Hello + À ńōń-ŕēmōvàƀĺē àũţĥēńţĩćàţōŕ, ĺĩķē ŢōũćĥĨĎ ōŕ Ŵĩńďōŵś Ĥēĺĺō A "roaming" authenticator, like a YubiKey + À "ŕōàmĩńĝ" àũţĥēńţĩćàţōŕ, ĺĩķē à ŶũƀĩĶēŷ This stage checks the user's current session against the Google reCaptcha (or compatible) service. + Ţĥĩś śţàĝē ćĥēćķś ţĥē ũśēŕ'ś ćũŕŕēńţ śēśśĩōń àĝàĩńśţ ţĥē Ĝōōĝĺē ŕēĆàƥţćĥà (ōŕ ćōmƥàţĩƀĺē) śēŕvĩćē. Public Key + Ƥũƀĺĩć Ķēŷ Public key, acquired from https://www.google.com/recaptcha/intro/v3.html. + Ƥũƀĺĩć ķēŷ, àćǫũĩŕēď ƒŕōm ĥţţƥś://ŵŵŵ.ĝōōĝĺē.ćōm/ŕēćàƥţćĥà/ĩńţŕō/v3.ĥţmĺ. Private Key + Ƥŕĩvàţē Ķēŷ Private key, acquired from https://www.google.com/recaptcha/intro/v3.html. + Ƥŕĩvàţē ķēŷ, àćǫũĩŕēď ƒŕōm ĥţţƥś://ŵŵŵ.ĝōōĝĺē.ćōm/ŕēćàƥţćĥà/ĩńţŕō/v3.ĥţmĺ. Advanced settings + Àďvàńćēď śēţţĩńĝś JS URL + ĵŚ ŨŔĹ URL to fetch JavaScript from, defaults to recaptcha. Can be replaced with any compatible alternative. + ŨŔĹ ţō ƒēţćĥ ĵàvàŚćŕĩƥţ ƒŕōm, ďēƒàũĺţś ţō ŕēćàƥţćĥà. Ćàń ƀē ŕēƥĺàćēď ŵĩţĥ àńŷ ćōmƥàţĩƀĺē àĺţēŕńàţĩvē. API URL + ÀƤĨ ŨŔĹ URL used to validate captcha response, defaults to recaptcha. Can be replaced with any compatible alternative. + ŨŔĹ ũśēď ţō vàĺĩďàţē ćàƥţćĥà ŕēśƥōńśē, ďēƒàũĺţś ţō ŕēćàƥţćĥà. Ćàń ƀē ŕēƥĺàćēď ŵĩţĥ àńŷ ćōmƥàţĩƀĺē àĺţēŕńàţĩvē. Prompt for the user's consent. The consent can either be permanent or expire in a defined amount of time. + Ƥŕōmƥţ ƒōŕ ţĥē ũśēŕ'ś ćōńśēńţ. Ţĥē ćōńśēńţ ćàń ēĩţĥēŕ ƀē ƥēŕmàńēńţ ōŕ ēxƥĩŕē ĩń à ďēƒĩńēď àmōũńţ ōƒ ţĩmē. Always require consent + Àĺŵàŷś ŕēǫũĩŕē ćōńśēńţ Consent given last indefinitely + Ćōńśēńţ ĝĩvēń ĺàśţ ĩńďēƒĩńĩţēĺŷ Consent expires. + Ćōńśēńţ ēxƥĩŕēś. Consent expires in + Ćōńśēńţ ēxƥĩŕēś ĩń Offset after which consent expires. + Ōƒƒśēţ àƒţēŕ ŵĥĩćĥ ćōńśēńţ ēxƥĩŕēś. Dummy stage used for testing. Shows a simple continue button and always passes. + Ďũmmŷ śţàĝē ũśēď ƒōŕ ţēśţĩńĝ. Śĥōŵś à śĩmƥĺē ćōńţĩńũē ƀũţţōń àńď àĺŵàŷś ƥàśśēś. Throw error? + Ţĥŕōŵ ēŕŕōŕ? SMTP Host + ŚMŢƤ Ĥōśţ SMTP Port + ŚMŢƤ Ƥōŕţ SMTP Username + ŚMŢƤ Ũśēŕńàmē SMTP Password + ŚMŢƤ Ƥàśśŵōŕď Use TLS + Ũśē ŢĹŚ Use SSL + Ũśē ŚŚĹ From address + Ƒŕōm àďďŕēśś Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity. + Vēŕĩƒŷ ţĥē ũśēŕ'ś ēmàĩĺ àďďŕēśś ƀŷ śēńďĩńĝ ţĥēm à ōńē-ţĩmē-ĺĩńķ. Ćàń àĺśō ƀē ũśēď ƒōŕ ŕēćōvēŕŷ ţō vēŕĩƒŷ ţĥē ũśēŕ'ś àũţĥēńţĩćĩţŷ. Activate pending user on success + Àćţĩvàţē ƥēńďĩńĝ ũśēŕ ōń śũććēśś When a user returns from the email successfully, their account will be activated. + Ŵĥēń à ũśēŕ ŕēţũŕńś ƒŕōm ţĥē ēmàĩĺ śũććēśśƒũĺĺŷ, ţĥēĩŕ àććōũńţ ŵĩĺĺ ƀē àćţĩvàţēď. Use global settings + Ũśē ĝĺōƀàĺ śēţţĩńĝś When enabled, global Email connection settings will be used and connection settings below will be ignored. + Ŵĥēń ēńàƀĺēď, ĝĺōƀàĺ Ēmàĩĺ ćōńńēćţĩōń śēţţĩńĝś ŵĩĺĺ ƀē ũśēď àńď ćōńńēćţĩōń śēţţĩńĝś ƀēĺōŵ ŵĩĺĺ ƀē ĩĝńōŕēď. Token expiry + Ţōķēń ēxƥĩŕŷ Time in minutes the token sent is valid. + Ţĩmē ĩń mĩńũţēś ţĥē ţōķēń śēńţ ĩś vàĺĩď. Template + Ţēmƥĺàţē Let the user identify themselves with their username or Email address. + Ĺēţ ţĥē ũśēŕ ĩďēńţĩƒŷ ţĥēmśēĺvēś ŵĩţĥ ţĥēĩŕ ũśēŕńàmē ōŕ Ēmàĩĺ àďďŕēśś. User fields + Ũśēŕ ƒĩēĺďś UPN + ŨƤŃ Fields a user can identify themselves with. If no fields are selected, the user will only be able to use sources. + Ƒĩēĺďś à ũśēŕ ćàń ĩďēńţĩƒŷ ţĥēmśēĺvēś ŵĩţĥ. Ĩƒ ńō ƒĩēĺďś àŕē śēĺēćţēď, ţĥē ũśēŕ ŵĩĺĺ ōńĺŷ ƀē àƀĺē ţō ũśē śōũŕćēś. Password stage + Ƥàśśŵōŕď śţàĝē When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks. + Ŵĥēń śēĺēćţēď, à ƥàśśŵōŕď ƒĩēĺď ĩś śĥōŵń ōń ţĥē śàmē ƥàĝē ĩńśţēàď ōƒ à śēƥàŕàţē ƥàĝē. Ţĥĩś ƥŕēvēńţś ũśēŕńàmē ēńũmēŕàţĩōń àţţàćķś. Case insensitive matching + Ćàśē ĩńśēńśĩţĩvē màţćĥĩńĝ When enabled, user fields are matched regardless of their casing. + Ŵĥēń ēńàƀĺēď, ũśēŕ ƒĩēĺďś àŕē màţćĥēď ŕēĝàŕďĺēśś ōƒ ţĥēĩŕ ćàśĩńĝ. Show matched user + Śĥōŵ màţćĥēď ũśēŕ When a valid username/email has been entered, and this option is enabled, the user's username and avatar will be shown. Otherwise, the text that the user entered will be shown. + Ŵĥēń à vàĺĩď ũśēŕńàmē/ēmàĩĺ ĥàś ƀēēń ēńţēŕēď, àńď ţĥĩś ōƥţĩōń ĩś ēńàƀĺēď, ţĥē ũśēŕ'ś ũśēŕńàmē àńď àvàţàŕ ŵĩĺĺ ƀē śĥōŵń. Ōţĥēŕŵĩśē, ţĥē ţēxţ ţĥàţ ţĥē ũśēŕ ēńţēŕēď ŵĩĺĺ ƀē śĥōŵń. Source settings + Śōũŕćē śēţţĩńĝś Sources + Śōũŕćēś Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP. + Śēĺēćţ śōũŕćēś śĥōũĺď ƀē śĥōŵń ƒōŕ ũśēŕś ţō àũţĥēńţĩćàţē ŵĩţĥ. Ţĥĩś ōńĺŷ àƒƒēćţś ŵēƀ-ƀàśēď śōũŕćēś, ńōţ ĹĎÀƤ. Show sources' labels + Śĥōŵ śōũŕćēś' ĺàƀēĺś By default, only icons are shown for sources. Enable this to show their full names. + ßŷ ďēƒàũĺţ, ōńĺŷ ĩćōńś àŕē śĥōŵń ƒōŕ śōũŕćēś. Ēńàƀĺē ţĥĩś ţō śĥōŵ ţĥēĩŕ ƒũĺĺ ńàmēś. Passwordless flow + Ƥàśśŵōŕďĺēśś ƒĺōŵ Optional passwordless flow, which is linked at the bottom of the page. When configured, users can use this flow to authenticate with a WebAuthn authenticator, without entering any details. + Ōƥţĩōńàĺ ƥàśśŵōŕďĺēśś ƒĺōŵ, ŵĥĩćĥ ĩś ĺĩńķēď àţ ţĥē ƀōţţōm ōƒ ţĥē ƥàĝē. Ŵĥēń ćōńƒĩĝũŕēď, ũśēŕś ćàń ũśē ţĥĩś ƒĺōŵ ţō àũţĥēńţĩćàţē ŵĩţĥ à ŴēƀÀũţĥń àũţĥēńţĩćàţōŕ, ŵĩţĥōũţ ēńţēŕĩńĝ àńŷ ďēţàĩĺś. Optional enrollment flow, which is linked at the bottom of the page. + Ōƥţĩōńàĺ ēńŕōĺĺmēńţ ƒĺōŵ, ŵĥĩćĥ ĩś ĺĩńķēď àţ ţĥē ƀōţţōm ōƒ ţĥē ƥàĝē. Optional recovery flow, which is linked at the bottom of the page. + Ōƥţĩōńàĺ ŕēćōvēŕŷ ƒĺōŵ, ŵĥĩćĥ ĩś ĺĩńķēď àţ ţĥē ƀōţţōm ōƒ ţĥē ƥàĝē. This stage can be included in enrollment flows to accept invitations. + Ţĥĩś śţàĝē ćàń ƀē ĩńćĺũďēď ĩń ēńŕōĺĺmēńţ ƒĺōŵś ţō àććēƥţ ĩńvĩţàţĩōńś. Continue flow without invitation + Ćōńţĩńũē ƒĺōŵ ŵĩţĥōũţ ĩńvĩţàţĩōń 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. + Ĩƒ ţĥĩś ƒĺàĝ ĩś śēţ, ţĥĩś Śţàĝē ŵĩĺĺ Ĵũmƥ ţō ţĥē ńēxţ Śţàĝē ŵĥēń ńō Ĩńvĩţàţĩōń ĩś ĝĩvēń. ßŷ ďēƒàũĺţ ţĥĩś Śţàĝē ŵĩĺĺ ćàńćēĺ ţĥē Ƒĺōŵ ŵĥēń ńō ĩńvĩţàţĩōń ĩś ĝĩvēń. Validate the user's password against the selected backend(s). + Vàĺĩďàţē ţĥē ũśēŕ'ś ƥàśśŵōŕď àĝàĩńśţ ţĥē śēĺēćţēď ƀàćķēńď(ś). Backends + ßàćķēńďś User database + standard password + Ũśēŕ ďàţàƀàśē + śţàńďàŕď ƥàśśŵōŕď User database + app passwords + Ũśēŕ ďàţàƀàśē + àƥƥ ƥàśśŵōŕďś User database + LDAP password + Ũśēŕ ďàţàƀàśē + ĹĎÀƤ ƥàśśŵōŕď Selection of backends to test the password against. + Śēĺēćţĩōń ōƒ ƀàćķēńďś ţō ţēśţ ţĥē ƥàśśŵōŕď àĝàĩńśţ. Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password. + Ƒĺōŵ ũśēď ƀŷ àń àũţĥēńţĩćàţēď ũśēŕ ţō ćōńƒĩĝũŕē ţĥēĩŕ ƥàśśŵōŕď. Ĩƒ ēmƥţŷ, ũśēŕ ŵĩĺĺ ńōţ ƀē àƀĺē ţō ćōńƒĩĝũŕē ćĥàńĝē ţĥēĩŕ ƥàśśŵōŕď. Failed attempts before cancel + Ƒàĩĺēď àţţēmƥţś ƀēƒōŕē ćàńćēĺ How many attempts a user has before the flow is canceled. To lock the user out, use a reputation policy and a user_write stage. + Ĥōŵ màńŷ àţţēmƥţś à ũśēŕ ĥàś ƀēƒōŕē ţĥē ƒĺōŵ ĩś ćàńćēĺēď. Ţō ĺōćķ ţĥē ũśēŕ ōũţ, ũśē à ŕēƥũţàţĩōń ƥōĺĩćŷ àńď à ũśēŕ_ŵŕĩţē śţàĝē. Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable. + Śĥōŵ àŕƀĩţŕàŕŷ ĩńƥũţ ƒĩēĺďś ţō ţĥē ũśēŕ, ƒōŕ ēxàmƥĺē ďũŕĩńĝ ēńŕōĺĺmēńţ. Ďàţà ĩś śàvēď ĩń ţĥē ƒĺōŵ ćōńţēxţ ũńďēŕ ţĥē 'ƥŕōmƥţ_ďàţà' vàŕĩàƀĺē. Fields + Ƒĩēĺďś ("", of type ) + ("", ōƒ ţŷƥē ) Validation Policies + Vàĺĩďàţĩōń Ƥōĺĩćĩēś Selected policies are executed when the stage is submitted to validate the data. + Śēĺēćţēď ƥōĺĩćĩēś àŕē ēxēćũţēď ŵĥēń ţĥē śţàĝē ĩś śũƀmĩţţēď ţō vàĺĩďàţē ţĥē ďàţà. Delete the currently pending user. CAUTION, this stage does not ask for confirmation. Use a consent stage to ensure the user is aware of their actions. + Ďēĺēţē ţĥē ćũŕŕēńţĺŷ ƥēńďĩńĝ ũśēŕ. ĆÀŨŢĨŌŃ, ţĥĩś śţàĝē ďōēś ńōţ àśķ ƒōŕ ćōńƒĩŕmàţĩōń. Ũśē à ćōńśēńţ śţàĝē ţō ēńśũŕē ţĥē ũśēŕ ĩś àŵàŕē ōƒ ţĥēĩŕ àćţĩōńś. Log the currently pending user in. + Ĺōĝ ţĥē ćũŕŕēńţĺŷ ƥēńďĩńĝ ũśēŕ ĩń. Session duration + Śēśśĩōń ďũŕàţĩōń Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed. + Ďēţēŕmĩńēś ĥōŵ ĺōńĝ à śēśśĩōń ĺàśţś. Ďēƒàũĺţ ōƒ 0 śēćōńďś mēàńś ţĥàţ ţĥē śēśśĩōńś ĺàśţś ũńţĩĺ ţĥē ƀŕōŵśēŕ ĩś ćĺōśēď. Different browsers handle session cookies differently, and might not remove them even when the browser is closed. + Ďĩƒƒēŕēńţ ƀŕōŵśēŕś ĥàńďĺē śēśśĩōń ćōōķĩēś ďĩƒƒēŕēńţĺŷ, àńď mĩĝĥţ ńōţ ŕēmōvē ţĥēm ēvēń ŵĥēń ţĥē ƀŕōŵśēŕ ĩś ćĺōśēď. See here. + Śēē ĥēŕē. Stay signed in offset + Śţàŷ śĩĝńēď ĩń ōƒƒśēţ If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + Ĩƒ śēţ ţō à ďũŕàţĩōń àƀōvē 0, ţĥē ũśēŕ ŵĩĺĺ ĥàvē ţĥē ōƥţĩōń ţō ćĥōōśē ţō "śţàŷ śĩĝńēď ĩń", ŵĥĩćĥ ŵĩĺĺ ēxţēńď ţĥēĩŕ śēśśĩōń ƀŷ ţĥē ţĩmē śƥēćĩƒĩēď ĥēŕē. Terminate other sessions + Ţēŕmĩńàţē ōţĥēŕ śēśśĩōńś When enabled, all previous sessions of the user will be terminated. + Ŵĥēń ēńàƀĺēď, àĺĺ ƥŕēvĩōũś śēśśĩōńś ōƒ ţĥē ũśēŕ ŵĩĺĺ ƀē ţēŕmĩńàţēď. Remove the user from the current session. + Ŕēmōvē ţĥē ũśēŕ ƒŕōm ţĥē ćũŕŕēńţ śēśśĩōń. Write any data from the flow's context's 'prompt_data' to the currently pending user. If no user is pending, a new user is created, and data is written to them. + Ŵŕĩţē àńŷ ďàţà ƒŕōm ţĥē ƒĺōŵ'ś ćōńţēxţ'ś 'ƥŕōmƥţ_ďàţà' ţō ţĥē ćũŕŕēńţĺŷ ƥēńďĩńĝ ũśēŕ. Ĩƒ ńō ũśēŕ + ĩś ƥēńďĩńĝ, à ńēŵ ũśēŕ ĩś ćŕēàţēď, àńď ďàţà ĩś ŵŕĩţţēń ţō ţĥēm. Never create users + Ńēvēŕ ćŕēàţē ũśēŕś When no user is present in the flow context, the stage will fail. + Ŵĥēń ńō ũśēŕ ĩś ƥŕēśēńţ ĩń ţĥē ƒĺōŵ ćōńţēxţ, ţĥē śţàĝē ŵĩĺĺ ƒàĩĺ. Create users when required + Ćŕēàţē ũśēŕś ŵĥēń ŕēǫũĩŕēď When no user is present in the the flow context, a new user is created. + Ŵĥēń ńō ũśēŕ ĩś ƥŕēśēńţ ĩń ţĥē ţĥē ƒĺōŵ ćōńţēxţ, à ńēŵ ũśēŕ ĩś ćŕēàţēď. Always create new users + Àĺŵàŷś ćŕēàţē ńēŵ ũśēŕś Create a new user even if a user is in the flow context. + Ćŕēàţē à ńēŵ ũśēŕ ēvēń ĩƒ à ũśēŕ ĩś ĩń ţĥē ƒĺōŵ ćōńţēxţ. Create users as inactive + Ćŕēàţē ũśēŕś àś ĩńàćţĩvē Mark newly created users as inactive. + Màŕķ ńēŵĺŷ ćŕēàţēď ũśēŕś àś ĩńàćţĩvē. User path template + Ũśēŕ ƥàţĥ ţēmƥĺàţē Path new users will be created under. If left blank, the default path will be used. + Ƥàţĥ ńēŵ ũśēŕś ŵĩĺĺ ƀē ćŕēàţēď ũńďēŕ. Ĩƒ ĺēƒţ ƀĺàńķ, ţĥē ďēƒàũĺţ ƥàţĥ ŵĩĺĺ ƀē ũśēď. Newly created users are added to this group, if a group is selected. + Ńēŵĺŷ ćŕēàţēď ũśēŕś àŕē àďďēď ţō ţĥĩś ĝŕōũƥ, ĩƒ à ĝŕōũƥ ĩś śēĺēćţēď. New stage + Ńēŵ śţàĝē Create a new stage. + Ćŕēàţē à ńēŵ śţàĝē. Successfully imported device. + Śũććēśśƒũĺĺŷ ĩmƥōŕţēď ďēvĩćē. The user in authentik this device will be assigned to. + Ţĥē ũśēŕ ĩń àũţĥēńţĩķ ţĥĩś ďēvĩćē ŵĩĺĺ ƀē àśśĩĝńēď ţō. Duo User ID + Ďũō Ũśēŕ ĨĎ The user ID in Duo, can be found in the URL after clicking on a user. + Ţĥē ũśēŕ ĨĎ ĩń Ďũō, ćàń ƀē ƒōũńď ĩń ţĥē ŨŔĹ àƒţēŕ ćĺĩćķĩńĝ ōń à ũśēŕ. Automatic import + Àũţōmàţĩć ĩmƥōŕţ Successfully imported devices. + Śũććēśśƒũĺĺŷ ĩmƥōŕţēď ďēvĩćēś. Start automatic import + Śţàŕţ àũţōmàţĩć ĩmƥōŕţ Or manually import + Ōŕ màńũàĺĺŷ ĩmƥōŕţ Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow. + Śţàĝēś àŕē śĩńĝĺē śţēƥś ōƒ à Ƒĺōŵ ţĥàţ à ũśēŕ ĩś ĝũĩďēď ţĥŕōũĝĥ. À śţàĝē ćàń ōńĺŷ ƀē ēxēćũţēď ƒŕōm ŵĩţĥĩń à ƒĺōŵ. Flows + Ƒĺōŵś Stage(s) + Śţàĝē(ś) Import + Ĩmƥōŕţ Import Duo device + Ĩmƥōŕţ Ďũō ďēvĩćē Successfully updated flow. + Śũććēśśƒũĺĺŷ ũƥďàţēď ƒĺōŵ. Successfully created flow. + Śũććēśśƒũĺĺŷ ćŕēàţēď ƒĺōŵ. Shown as the Title in Flow pages. + Śĥōŵń àś ţĥē Ţĩţĺē ĩń Ƒĺōŵ ƥàĝēś. Visible in the URL. + Vĩśĩƀĺē ĩń ţĥē ŨŔĹ. Designation + Ďēśĩĝńàţĩōń Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik. + Ďēćĩďēś ŵĥàţ ţĥĩś Ƒĺōŵ ĩś ũśēď ƒōŕ. Ƒōŕ ēxàmƥĺē, ţĥē Àũţĥēńţĩćàţĩōń ƒĺōŵ ĩś ŕēďĩŕēćţ ţō ŵĥēń àń ũń-àũţĥēńţĩćàţēď ũśēŕ vĩśĩţś àũţĥēńţĩķ. No requirement + Ńō ŕēǫũĩŕēmēńţ Require authentication + Ŕēǫũĩŕē àũţĥēńţĩćàţĩōń Require no authentication. + Ŕēǫũĩŕē ńō àũţĥēńţĩćàţĩōń. Require superuser. + Ŕēǫũĩŕē śũƥēŕũśēŕ. Required authentication level for this flow. + Ŕēǫũĩŕēď àũţĥēńţĩćàţĩōń ĺēvēĺ ƒōŕ ţĥĩś ƒĺōŵ. Behavior settings + ßēĥàvĩōŕ śēţţĩńĝś Compatibility mode + Ćōmƥàţĩƀĩĺĩţŷ mōďē Increases compatibility with password managers and mobile devices. + Ĩńćŕēàśēś ćōmƥàţĩƀĩĺĩţŷ ŵĩţĥ ƥàśśŵōŕď màńàĝēŕś àńď mōƀĩĺē ďēvĩćēś. Denied action + Ďēńĩēď àćţĩōń Will follow the ?next parameter if set, otherwise show a message + Ŵĩĺĺ ƒōĺĺōŵ ţĥē ?ńēxţ ƥàŕàmēţēŕ ĩƒ śēţ, ōţĥēŕŵĩśē śĥōŵ à mēśśàĝē Will either follow the ?next parameter or redirect to the default interface + Ŵĩĺĺ ēĩţĥēŕ ƒōĺĺōŵ ţĥē ?ńēxţ ƥàŕàmēţēŕ ōŕ ŕēďĩŕēćţ ţō ţĥē ďēƒàũĺţ ĩńţēŕƒàćē Will notify the user the flow isn't applicable + Ŵĩĺĺ ńōţĩƒŷ ţĥē ũśēŕ ţĥē ƒĺōŵ ĩśń'ţ àƥƥĺĩćàƀĺē Decides the response when a policy denies access to this flow for a user. + Ďēćĩďēś ţĥē ŕēśƥōńśē ŵĥēń à ƥōĺĩćŷ ďēńĩēś àććēśś ţō ţĥĩś ƒĺōŵ ƒōŕ à ũśēŕ. Appearance settings + Àƥƥēàŕàńćē śēţţĩńĝś Layout + Ĺàŷōũţ Background + ßàćķĝŕōũńď Background shown during execution. + ßàćķĝŕōũńď śĥōŵń ďũŕĩńĝ ēxēćũţĩōń. Clear background + Ćĺēàŕ ƀàćķĝŕōũńď Delete currently set background image. + Ďēĺēţē ćũŕŕēńţĺŷ śēţ ƀàćķĝŕōũńď ĩmàĝē. Successfully imported flow. + Śũććēśśƒũĺĺŷ ĩmƥōŕţēď ƒĺōŵ. .yaml files, which can be found on goauthentik.io and can be exported by authentik. + .ŷàmĺ ƒĩĺēś, ŵĥĩćĥ ćàń ƀē ƒōũńď ōń ĝōàũţĥēńţĩķ.ĩō àńď ćàń ƀē ēxƥōŕţēď ƀŷ àũţĥēńţĩķ. Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them. + Ƒĺōŵś ďēśćŕĩƀē à ćĥàĩń ōƒ Śţàĝēś ţō àũţĥēńţĩćàţē, ēńŕōĺĺ ōŕ ŕēćōvēŕ à ũśēŕ. Śţàĝēś àŕē ćĥōśēń ƀàśēď ōń ƥōĺĩćĩēś àƥƥĺĩēď ţō ţĥēm. Flow(s) + Ƒĺōŵ(ś) Update Flow + Ũƥďàţē Ƒĺōŵ Create Flow + Ćŕēàţē Ƒĺōŵ Import Flow + Ĩmƥōŕţ Ƒĺōŵ Successfully cleared flow cache + Śũććēśśƒũĺĺŷ ćĺēàŕēď ƒĺōŵ ćàćĥē Failed to delete flow cache + Ƒàĩĺēď ţō ďēĺēţē ƒĺōŵ ćàćĥē Clear Flow cache + Ćĺēàŕ Ƒĺōŵ ćàćĥē Are you sure you want to clear the flow cache? This will cause all flows to be re-evaluated on their next usage. + Àŕē ŷōũ śũŕē ŷōũ ŵàńţ ţō ćĺēàŕ ţĥē ƒĺōŵ ćàćĥē? + Ţĥĩś ŵĩĺĺ ćàũśē àĺĺ ƒĺōŵś ţō ƀē ŕē-ēvàĺũàţēď ōń ţĥēĩŕ ńēxţ ũśàĝē. Stage binding(s) + Śţàĝē ƀĩńďĩńĝ(ś) Stage type + Śţàĝē ţŷƥē Edit Stage + Ēďĩţ Śţàĝē Update Stage binding + Ũƥďàţē Śţàĝē ƀĩńďĩńĝ These bindings control if this stage will be applied to the flow. + Ţĥēśē ƀĩńďĩńĝś ćōńţŕōĺ ĩƒ ţĥĩś śţàĝē ŵĩĺĺ ƀē àƥƥĺĩēď ţō ţĥē ƒĺōŵ. No Stages bound + Ńō Śţàĝēś ƀōũńď No stages are currently bound to this flow. + Ńō śţàĝēś àŕē ćũŕŕēńţĺŷ ƀōũńď ţō ţĥĩś ƒĺōŵ. Create Stage binding + Ćŕēàţē Śţàĝē ƀĩńďĩńĝ Bind stage + ßĩńď śţàĝē Bind existing stage + ßĩńď ēxĩśţĩńĝ śţàĝē Flow Overview + Ƒĺōŵ Ōvēŕvĩēŵ Related actions + Ŕēĺàţēď àćţĩōńś Execute flow + Ēxēćũţē ƒĺōŵ Normal + Ńōŕmàĺ with current user + ŵĩţĥ ćũŕŕēńţ ũśēŕ with inspector + ŵĩţĥ ĩńśƥēćţōŕ Export flow + Ēxƥōŕţ ƒĺōŵ Export + Ēxƥōŕţ Stage Bindings + Śţàĝē ßĩńďĩńĝś These bindings control which users can access this flow. + Ţĥēśē ƀĩńďĩńĝś ćōńţŕōĺ ŵĥĩćĥ ũśēŕś ćàń àććēśś ţĥĩś ƒĺōŵ. Event Log + Ēvēńţ Ĺōĝ Event + Ēvēńţ Event info + Ēvēńţ ĩńƒō Created + Ćŕēàţēď Successfully updated transport. + Śũććēśśƒũĺĺŷ ũƥďàţēď ţŕàńśƥōŕţ. Successfully created transport. + Śũććēśśƒũĺĺŷ ćŕēàţēď ţŕàńśƥōŕţ. Local (notifications will be created within authentik) + Ĺōćàĺ (ńōţĩƒĩćàţĩōńś ŵĩĺĺ ƀē ćŕēàţēď ŵĩţĥĩń àũţĥēńţĩķ) Webhook (generic) + Ŵēƀĥōōķ (ĝēńēŕĩć) Webhook (Slack/Discord) + Ŵēƀĥōōķ (Śĺàćķ/Ďĩśćōŕď) Webhook URL + Ŵēƀĥōōķ ŨŔĹ Webhook Mapping + Ŵēƀĥōōķ Màƥƥĩńĝ Send once + Śēńď ōńćē Only send notification once, for example when sending a webhook into a chat channel. + Ōńĺŷ śēńď ńōţĩƒĩćàţĩōń ōńćē, ƒōŕ ēxàmƥĺē ŵĥēń śēńďĩńĝ à ŵēƀĥōōķ ĩńţō à ćĥàţ ćĥàńńēĺ. Notification Transports + Ńōţĩƒĩćàţĩōń Ţŕàńśƥōŕţś Define how notifications are sent to users, like Email or Webhook. + Ďēƒĩńē ĥōŵ ńōţĩƒĩćàţĩōńś àŕē śēńţ ţō ũśēŕś, ĺĩķē Ēmàĩĺ ōŕ Ŵēƀĥōōķ. Notification transport(s) + Ńōţĩƒĩćàţĩōń ţŕàńśƥōŕţ(ś) Update Notification Transport + Ũƥďàţē Ńōţĩƒĩćàţĩōń Ţŕàńśƥōŕţ Create Notification Transport + Ćŕēàţē Ńōţĩƒĩćàţĩōń Ţŕàńśƥōŕţ Successfully updated rule. + Śũććēśśƒũĺĺŷ ũƥďàţēď ŕũĺē. Successfully created rule. + Śũććēśśƒũĺĺŷ ćŕēàţēď ŕũĺē. Select the group of users which the alerts are sent to. If no group is selected the rule is disabled. + Śēĺēćţ ţĥē ĝŕōũƥ ōƒ ũśēŕś ŵĥĩćĥ ţĥē àĺēŕţś àŕē śēńţ ţō. Ĩƒ ńō ĝŕōũƥ ĩś śēĺēćţēď ţĥē ŕũĺē ĩś ďĩśàƀĺēď. Transports + Ţŕàńśƥōŕţś Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI. + Śēĺēćţ ŵĥĩćĥ ţŕàńśƥōŕţś śĥōũĺď ƀē ũśēď ţō ńōţĩƒŷ ţĥē ũśēŕ. Ĩƒ ńōńē àŕē śēĺēćţēď, ţĥē ńōţĩƒĩćàţĩōń ŵĩĺĺ ōńĺŷ ƀē śĥōŵń ĩń ţĥē àũţĥēńţĩķ ŨĨ. Severity + Śēvēŕĩţŷ Notification Rules + Ńōţĩƒĩćàţĩōń Ŕũĺēś Send notifications whenever a specific Event is created and matched by policies. + Śēńď ńōţĩƒĩćàţĩōńś ŵĥēńēvēŕ à śƥēćĩƒĩć Ēvēńţ ĩś ćŕēàţēď àńď màţćĥēď ƀŷ ƥōĺĩćĩēś. Sent to group + Śēńţ ţō ĝŕōũƥ Notification rule(s) + Ńōţĩƒĩćàţĩōń ŕũĺē(ś) None (rule disabled) + Ńōńē (ŕũĺē ďĩśàƀĺēď) Update Notification Rule + Ũƥďàţē Ńōţĩƒĩćàţĩōń Ŕũĺē Create Notification Rule + Ćŕēàţē Ńōţĩƒĩćàţĩōń Ŕũĺē These bindings control upon which events this rule triggers. Bindings to groups/users are checked against the user of the event. + Ţĥēśē ƀĩńďĩńĝś ćōńţŕōĺ ũƥōń ŵĥĩćĥ ēvēńţś ţĥĩś ŕũĺē ţŕĩĝĝēŕś. +ßĩńďĩńĝś ţō ĝŕōũƥś/ũśēŕś àŕē ćĥēćķēď àĝàĩńśţ ţĥē ũśēŕ ōƒ ţĥē ēvēńţ. Outpost Deployment Info + Ōũţƥōśţ Ďēƥĺōŷmēńţ Ĩńƒō View deployment documentation + Vĩēŵ ďēƥĺōŷmēńţ ďōćũmēńţàţĩōń Click to copy token + Ćĺĩćķ ţō ćōƥŷ ţōķēń If your authentik Instance is using a self-signed certificate, set this value. + Ĩƒ ŷōũŕ àũţĥēńţĩķ Ĩńśţàńćē ĩś ũśĩńĝ à śēĺƒ-śĩĝńēď ćēŕţĩƒĩćàţē, śēţ ţĥĩś vàĺũē. If your authentik_host setting does not match the URL you want to login with, add this setting. + Ĩƒ ŷōũŕ àũţĥēńţĩķ_ĥōśţ śēţţĩńĝ ďōēś ńōţ màţćĥ ţĥē ŨŔĹ ŷōũ ŵàńţ ţō ĺōĝĩń ŵĩţĥ, àďď ţĥĩś śēţţĩńĝ. Successfully updated outpost. + Śũććēśśƒũĺĺŷ ũƥďàţēď ōũţƥōśţ. Successfully created outpost. + Śũććēśśƒũĺĺŷ ćŕēàţēď ōũţƥōśţ. Radius + Ŕàďĩũś Integration + Ĩńţēĝŕàţĩōń Selecting an integration enables the management of the outpost by authentik. + Śēĺēćţĩńĝ àń ĩńţēĝŕàţĩōń ēńàƀĺēś ţĥē màńàĝēmēńţ ōƒ ţĥē ōũţƥōśţ ƀŷ àũţĥēńţĩķ. You can only select providers that match the type of the outpost. + Ŷōũ ćàń ōńĺŷ śēĺēćţ ƥŕōvĩďēŕś ţĥàţ màţćĥ ţĥē ţŷƥē ōƒ ţĥē ōũţƥōśţ. Configuration + Ćōńƒĩĝũŕàţĩōń See more here: + Śēē mōŕē ĥēŕē: Documentation + Ďōćũmēńţàţĩōń Last seen + Ĺàśţ śēēń , should be + , śĥōũĺď ƀē Hostname + Ĥōśţńàmē Not available + Ńōţ àvàĩĺàƀĺē Last seen: + Ĺàśţ śēēń: Unknown type + Ũńķńōŵń ţŷƥē Outposts + Ōũţƥōśţś Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies. + Ōũţƥōśţś àŕē ďēƥĺōŷmēńţś ōƒ àũţĥēńţĩķ ćōmƥōńēńţś ţō śũƥƥōŕţ ďĩƒƒēŕēńţ ēńvĩŕōńmēńţś àńď ƥŕōţōćōĺś, ĺĩķē ŕēvēŕśē ƥŕōxĩēś. Health and Version + Ĥēàĺţĥ àńď Vēŕśĩōń Warning: authentik Domain is not configured, authentication will not work. + Ŵàŕńĩńĝ: àũţĥēńţĩķ Ďōmàĩń ĩś ńōţ ćōńƒĩĝũŕēď, àũţĥēńţĩćàţĩōń ŵĩĺĺ ńōţ ŵōŕķ. Logging in via . + Ĺōĝĝĩńĝ ĩń vĩà . No integration active + Ńō ĩńţēĝŕàţĩōń àćţĩvē Update Outpost + Ũƥďàţē Ōũţƥōśţ View Deployment Info + Vĩēŵ Ďēƥĺōŷmēńţ Ĩńƒō Detailed health (one instance per column, data is cached so may be out of date) + Ďēţàĩĺēď ĥēàĺţĥ (ōńē ĩńśţàńćē ƥēŕ ćōĺũmń, ďàţà ĩś ćàćĥēď śō màŷ ƀē ōũţ ōƒ ďàţē) Outpost(s) + Ōũţƥōśţ(ś) Create Outpost + Ćŕēàţē Ōũţƥōśţ Successfully updated integration. + Śũććēśśƒũĺĺŷ ũƥďàţēď ĩńţēĝŕàţĩōń. Successfully created integration. + Śũććēśśƒũĺĺŷ ćŕēàţēď ĩńţēĝŕàţĩōń. Local + Ĺōćàĺ If enabled, use the local connection. Required Docker socket/Kubernetes Integration. + Ĩƒ ēńàƀĺēď, ũśē ţĥē ĺōćàĺ ćōńńēćţĩōń. Ŕēǫũĩŕēď Ďōćķēŕ śōćķēţ/Ķũƀēŕńēţēś Ĩńţēĝŕàţĩōń. Docker URL + Ďōćķēŕ ŨŔĹ Can be in the format of 'unix://' when connecting to a local docker daemon, using 'ssh://' to connect via SSH, or 'https://:2376' when connecting to a remote system. + Ćàń ƀē ĩń ţĥē ƒōŕmàţ ōƒ 'ũńĩx://' ŵĥēń ćōńńēćţĩńĝ ţō à ĺōćàĺ ďōćķēŕ ďàēmōń, ũśĩńĝ 'śśĥ://' ţō ćōńńēćţ vĩà ŚŚĤ, ōŕ 'ĥţţƥś://:2376' ŵĥēń ćōńńēćţĩńĝ ţō à ŕēmōţē śŷśţēm. CA which the endpoint's Certificate is verified against. Can be left empty for no validation. + ĆÀ ŵĥĩćĥ ţĥē ēńďƥōĩńţ'ś Ćēŕţĩƒĩćàţē ĩś vēŕĩƒĩēď àĝàĩńśţ. Ćàń ƀē ĺēƒţ ēmƥţŷ ƒōŕ ńō vàĺĩďàţĩōń. TLS Authentication Certificate/SSH Keypair + ŢĹŚ Àũţĥēńţĩćàţĩōń Ćēŕţĩƒĩćàţē/ŚŚĤ Ķēŷƥàĩŕ Certificate/Key used for authentication. Can be left empty for no authentication. + Ćēŕţĩƒĩćàţē/Ķēŷ ũśēď ƒōŕ àũţĥēńţĩćàţĩōń. Ćàń ƀē ĺēƒţ ēmƥţŷ ƒōŕ ńō àũţĥēńţĩćàţĩōń. When connecting via SSH, this keypair is used for authentication. + Ŵĥēń ćōńńēćţĩńĝ vĩà ŚŚĤ, ţĥĩś ķēŷƥàĩŕ ĩś ũśēď ƒōŕ àũţĥēńţĩćàţĩōń. Kubeconfig + Ķũƀēćōńƒĩĝ Verify Kubernetes API SSL Certificate + Vēŕĩƒŷ Ķũƀēŕńēţēś ÀƤĨ ŚŚĹ Ćēŕţĩƒĩćàţē New outpost integration + Ńēŵ ōũţƥōśţ ĩńţēĝŕàţĩōń Create a new outpost integration. + Ćŕēàţē à ńēŵ ōũţƥōśţ ĩńţēĝŕàţĩōń. State + Śţàţē Unhealthy + Ũńĥēàĺţĥŷ Outpost integration(s) + Ōũţƥōśţ ĩńţēĝŕàţĩōń(ś) Successfully generated certificate-key pair. + Śũććēśśƒũĺĺŷ ĝēńēŕàţēď ćēŕţĩƒĩćàţē-ķēŷ ƥàĩŕ. Common Name + Ćōmmōń Ńàmē Subject-alt name + ŚũƀĴēćţ-àĺţ ńàmē Optional, comma-separated SubjectAlt Names. + Ōƥţĩōńàĺ, ćōmmà-śēƥàŕàţēď ŚũƀĴēćţÀĺţ Ńàmēś. Validity days + Vàĺĩďĩţŷ ďàŷś Successfully updated certificate-key pair. + Śũććēśśƒũĺĺŷ ũƥďàţēď ćēŕţĩƒĩćàţē-ķēŷ ƥàĩŕ. Successfully created certificate-key pair. + Śũććēśśƒũĺĺŷ ćŕēàţēď ćēŕţĩƒĩćàţē-ķēŷ ƥàĩŕ. PEM-encoded Certificate data. + ƤĒM-ēńćōďēď Ćēŕţĩƒĩćàţē ďàţà. Optional Private Key. If this is set, you can use this keypair for encryption. + Ōƥţĩōńàĺ Ƥŕĩvàţē Ķēŷ. Ĩƒ ţĥĩś ĩś śēţ, ŷōũ ćàń ũśē ţĥĩś ķēŷƥàĩŕ ƒōŕ ēńćŕŷƥţĩōń. Certificate-Key Pairs + Ćēŕţĩƒĩćàţē-Ķēŷ Ƥàĩŕś Import certificates of external providers or create certificates to sign requests with. + Ĩmƥōŕţ ćēŕţĩƒĩćàţēś ōƒ ēxţēŕńàĺ ƥŕōvĩďēŕś ōŕ ćŕēàţē ćēŕţĩƒĩćàţēś ţō śĩĝń ŕēǫũēśţś ŵĩţĥ. Private key available? + Ƥŕĩvàţē ķēŷ àvàĩĺàƀĺē? Certificate-Key Pair(s) + Ćēŕţĩƒĩćàţē-Ķēŷ Ƥàĩŕ(ś) Managed by authentik + Màńàĝēď ƀŷ àũţĥēńţĩķ Managed by authentik (Discovered) + Màńàĝēď ƀŷ àũţĥēńţĩķ (Ďĩśćōvēŕēď) Yes () + Ŷēś () No + Ńō Update Certificate-Key Pair + Ũƥďàţē Ćēŕţĩƒĩćàţē-Ķēŷ Ƥàĩŕ Certificate Fingerprint (SHA1) + Ćēŕţĩƒĩćàţē Ƒĩńĝēŕƥŕĩńţ (ŚĤÀ1) Certificate Fingerprint (SHA256) + Ćēŕţĩƒĩćàţē Ƒĩńĝēŕƥŕĩńţ (ŚĤÀ256) Certificate Subject + Ćēŕţĩƒĩćàţē ŚũƀĴēćţ Download Certificate + Ďōŵńĺōàď Ćēŕţĩƒĩćàţē Download Private key + Ďōŵńĺōàď Ƥŕĩvàţē ķēŷ Create Certificate-Key Pair + Ćŕēàţē Ćēŕţĩƒĩćàţē-Ķēŷ Ƥàĩŕ Generate + Ĝēńēŕàţē Generate Certificate-Key Pair + Ĝēńēŕàţē Ćēŕţĩƒĩćàţē-Ķēŷ Ƥàĩŕ Successfully updated instance. + Śũććēśśƒũĺĺŷ ũƥďàţēď ĩńśţàńćē. Successfully created instance. + Śũććēśśƒũĺĺŷ ćŕēàţēď ĩńśţàńćē. Disabled blueprints are never applied. + Ďĩśàƀĺēď ƀĺũēƥŕĩńţś àŕē ńēvēŕ àƥƥĺĩēď. Local path + Ĺōćàĺ ƥàţĥ OCI Registry + ŌĆĨ Ŕēĝĩśţŕŷ Internal + Ĩńţēŕńàĺ OCI URL, in the format of oci://registry.domain.tld/path/to/manifest. + ŌĆĨ ŨŔĹ, ĩń ţĥē ƒōŕmàţ ōƒ ōćĩ://ŕēĝĩśţŕŷ.ďōmàĩń.ţĺď/ƥàţĥ/ţō/màńĩƒēśţ. See more about OCI support here: + Śēē mōŕē àƀōũţ ŌĆĨ śũƥƥōŕţ ĥēŕē: Blueprint + ßĺũēƥŕĩńţ Configure the blueprint context, used for templating. + Ćōńƒĩĝũŕē ţĥē ƀĺũēƥŕĩńţ ćōńţēxţ, ũśēď ƒōŕ ţēmƥĺàţĩńĝ. Orphaned + Ōŕƥĥàńēď Blueprints + ßĺũēƥŕĩńţś Automate and template configuration within authentik. + Àũţōmàţē àńď ţēmƥĺàţē ćōńƒĩĝũŕàţĩōń ŵĩţĥĩń àũţĥēńţĩķ. Last applied + Ĺàśţ àƥƥĺĩēď Blueprint(s) + ßĺũēƥŕĩńţ(ś) Update Blueprint + Ũƥďàţē ßĺũēƥŕĩńţ Create Blueprint Instance + Ćŕēàţē ßĺũēƥŕĩńţ Ĩńśţàńćē API Requests + ÀƤĨ Ŕēǫũēśţś Open API Browser + Ōƥēń ÀƤĨ ßŕōŵśēŕ Notifications + Ńōţĩƒĩćàţĩōńś unread + ũńŕēàď Successfully cleared notifications + Śũććēśśƒũĺĺŷ ćĺēàŕēď ńōţĩƒĩćàţĩōńś Clear all + Ćĺēàŕ àĺĺ A newer version of the frontend is available. + À ńēŵēŕ vēŕśĩōń ōƒ ţĥē ƒŕōńţēńď ĩś àvàĩĺàƀĺē. You're currently impersonating . Click to stop. + Ŷōũ'ŕē ćũŕŕēńţĺŷ ĩmƥēŕśōńàţĩńĝ . Ćĺĩćķ ţō śţōƥ. User interface + Ũśēŕ ĩńţēŕƒàćē Dashboards + Ďàśĥƀōàŕďś Events + Ēvēńţś Logs + Ĺōĝś Customisation + Ćũśţōmĩśàţĩōń Directory + Ďĩŕēćţōŕŷ System + Śŷśţēm Certificates + Ćēŕţĩƒĩćàţēś Outpost Integrations + Ōũţƥōśţ Ĩńţēĝŕàţĩōńś API request failed + ÀƤĨ ŕēǫũēśţ ƒàĩĺēď User's avatar + Ũśēŕ'ś àvàţàŕ Something went wrong! Please try again later. + Śōmēţĥĩńĝ ŵēńţ ŵŕōńĝ! Ƥĺēàśē ţŕŷ àĝàĩń ĺàţēŕ. Request ID + Ŕēǫũēśţ ĨĎ You may close this page now. + Ŷōũ màŷ ćĺōśē ţĥĩś ƥàĝē ńōŵ. You're about to be redirect to the following URL. + Ŷōũ'ŕē àƀōũţ ţō ƀē ŕēďĩŕēćţ ţō ţĥē ƒōĺĺōŵĩńĝ ŨŔĹ. Follow redirect + Ƒōĺĺōŵ ŕēďĩŕēćţ Request has been denied. + Ŕēǫũēśţ ĥàś ƀēēń ďēńĩēď. Not you? + Ńōţ ŷōũ? Need an account? + Ńēēď àń àććōũńţ? Sign up. + Śĩĝń ũƥ. Forgot username or password? + Ƒōŕĝōţ ũśēŕńàmē ōŕ ƥàśśŵōŕď? Select one of the sources below to login. + Śēĺēćţ ōńē ōƒ ţĥē śōũŕćēś ƀēĺōŵ ţō ĺōĝĩń. Or + Ōŕ Use a security key + Ũśē à śēćũŕĩţŷ ķēŷ Login to continue to . + Ĺōĝĩń ţō ćōńţĩńũē ţō . Please enter your password + Ƥĺēàśē ēńţēŕ ŷōũŕ ƥàśśŵōŕď Forgot password? + Ƒōŕĝōţ ƥàśśŵōŕď? Application requires following permissions: + Àƥƥĺĩćàţĩōń ŕēǫũĩŕēś ƒōĺĺōŵĩńĝ ƥēŕmĩśśĩōńś: Application already has access to the following permissions: + Àƥƥĺĩćàţĩōń àĺŕēàďŷ ĥàś àććēśś ţō ţĥē ƒōĺĺōŵĩńĝ ƥēŕmĩśśĩōńś: Application requires following new permissions: + Àƥƥĺĩćàţĩōń ŕēǫũĩŕēś ƒōĺĺōŵĩńĝ ńēŵ ƥēŕmĩśśĩōńś: Check your Inbox for a verification email. + Ćĥēćķ ŷōũŕ Ĩńƀōx ƒōŕ à vēŕĩƒĩćàţĩōń ēmàĩĺ. Send Email again. + Śēńď Ēmàĩĺ àĝàĩń. Successfully copied TOTP Config. + Śũććēśśƒũĺĺŷ ćōƥĩēď ŢŌŢƤ Ćōńƒĩĝ. Copy + Ćōƥŷ Code + Ćōďē Please enter your TOTP Code + Ƥĺēàśē ēńţēŕ ŷōũŕ ŢŌŢƤ Ćōďē Duo activation QR code + Ďũō àćţĩvàţĩōń ǪŔ ćōďē Alternatively, if your current device has Duo installed, click on this link: + Àĺţēŕńàţĩvēĺŷ, ĩƒ ŷōũŕ ćũŕŕēńţ ďēvĩćē ĥàś Ďũō ĩńśţàĺĺēď, ćĺĩćķ ōń ţĥĩś ĺĩńķ: Duo activation + Ďũō àćţĩvàţĩōń Check status + Ćĥēćķ śţàţũś Make sure to keep these tokens in a safe place. + Màķē śũŕē ţō ķēēƥ ţĥēśē ţōķēńś ĩń à śàƒē ƥĺàćē. Phone number + Ƥĥōńē ńũmƀēŕ Please enter your Phone number. + Ƥĺēàśē ēńţēŕ ŷōũŕ Ƥĥōńē ńũmƀēŕ. Please enter the code you received via SMS + Ƥĺēàśē ēńţēŕ ţĥē ćōďē ŷōũ ŕēćēĩvēď vĩà ŚMŚ A code has been sent to you via SMS. + À ćōďē ĥàś ƀēēń śēńţ ţō ŷōũ vĩà ŚMŚ. Open your two-factor authenticator app to view your authentication code. + Ōƥēń ŷōũŕ ţŵō-ƒàćţōŕ àũţĥēńţĩćàţōŕ àƥƥ ţō vĩēŵ ŷōũŕ àũţĥēńţĩćàţĩōń ćōďē. Static token + Śţàţĩć ţōķēń Authentication code + Àũţĥēńţĩćàţĩōń ćōďē Please enter your code + Ƥĺēàśē ēńţēŕ ŷōũŕ ćōďē Return to device picker + Ŕēţũŕń ţō ďēvĩćē ƥĩćķēŕ Sending Duo push notification + Śēńďĩńĝ Ďũō ƥũśĥ ńōţĩƒĩćàţĩōń Assertions is empty + Àśśēŕţĩōńś ĩś ēmƥţŷ Error when creating credential: + Ēŕŕōŕ ŵĥēń ćŕēàţĩńĝ ćŕēďēńţĩàĺ: Error when validating assertion on server: + Ēŕŕōŕ ŵĥēń vàĺĩďàţĩńĝ àśśēŕţĩōń ōń śēŕvēŕ: Retry authentication + Ŕēţŕŷ àũţĥēńţĩćàţĩōń Duo push-notifications + Ďũō ƥũśĥ-ńōţĩƒĩćàţĩōńś Receive a push notification on your device. + Ŕēćēĩvē à ƥũśĥ ńōţĩƒĩćàţĩōń ōń ŷōũŕ ďēvĩćē. Authenticator + Àũţĥēńţĩćàţōŕ Use a security key to prove your identity. + Ũśē à śēćũŕĩţŷ ķēŷ ţō ƥŕōvē ŷōũŕ ĩďēńţĩţŷ. Traditional authenticator + Ţŕàďĩţĩōńàĺ àũţĥēńţĩćàţōŕ Use a code-based authenticator. + Ũśē à ćōďē-ƀàśēď àũţĥēńţĩćàţōŕ. Recovery keys + Ŕēćōvēŕŷ ķēŷś In case you can't access any other method. + Ĩń ćàśē ŷōũ ćàń'ţ àććēśś àńŷ ōţĥēŕ mēţĥōď. SMS + ŚMŚ Tokens sent via SMS. + Ţōķēńś śēńţ vĩà ŚMŚ. Select an authentication method. + Śēĺēćţ àń àũţĥēńţĩćàţĩōń mēţĥōď. Stay signed in? + Śţàŷ śĩĝńēď ĩń? Select Yes to reduce the number of times you're asked to sign in. + Śēĺēćţ Ŷēś ţō ŕēďũćē ţĥē ńũmƀēŕ ōƒ ţĩmēś ŷōũ'ŕē àśķēď ţō śĩĝń ĩń. Authenticating with Plex... + Àũţĥēńţĩćàţĩńĝ ŵĩţĥ Ƥĺēx... Waiting for authentication... + Ŵàĩţĩńĝ ƒōŕ àũţĥēńţĩćàţĩōń... If no Plex popup opens, click the button below. + Ĩƒ ńō Ƥĺēx ƥōƥũƥ ōƥēńś, ćĺĩćķ ţĥē ƀũţţōń ƀēĺōŵ. Open login + Ōƥēń ĺōĝĩń Authenticating with Apple... + Àũţĥēńţĩćàţĩńĝ ŵĩţĥ Àƥƥĺē... Retry + Ŕēţŕŷ Enter the code shown on your device. + Ēńţēŕ ţĥē ćōďē śĥōŵń ōń ŷōũŕ ďēvĩćē. Please enter your Code + Ƥĺēàśē ēńţēŕ ŷōũŕ Ćōďē You've successfully authenticated your device. + Ŷōũ'vē śũććēśśƒũĺĺŷ àũţĥēńţĩćàţēď ŷōũŕ ďēvĩćē. Flow inspector + Ƒĺōŵ ĩńśƥēćţōŕ Next stage + Ńēxţ śţàĝē Stage name + Śţàĝē ńàmē Stage kind + Śţàĝē ķĩńď Stage object + Śţàĝē ōƀĴēćţ This flow is completed. + Ţĥĩś ƒĺōŵ ĩś ćōmƥĺēţēď. Plan history + Ƥĺàń ĥĩśţōŕŷ Current plan context + Ćũŕŕēńţ ƥĺàń ćōńţēxţ Session ID + Śēśśĩōń ĨĎ Powered by authentik + Ƥōŵēŕēď ƀŷ àũţĥēńţĩķ Background image + ßàćķĝŕōũńď ĩmàĝē Error creating credential: + Ēŕŕōŕ ćŕēàţĩńĝ ćŕēďēńţĩàĺ: Server validation of credential failed: + Śēŕvēŕ vàĺĩďàţĩōń ōƒ ćŕēďēńţĩàĺ ƒàĩĺēď: Register device + Ŕēĝĩśţēŕ ďēvĩćē Refer to documentation + Ŕēƒēŕ ţō ďōćũmēńţàţĩōń No Applications available. + Ńō Àƥƥĺĩćàţĩōńś àvàĩĺàƀĺē. Either no applications are defined, or you don’t have access to any. + Ēĩţĥēŕ ńō àƥƥĺĩćàţĩōńś àŕē ďēƒĩńēď, ōŕ ŷōũ ďōń’ţ ĥàvē àććēśś ţō àńŷ. My Applications + Mŷ Àƥƥĺĩćàţĩōńś My applications + Mŷ àƥƥĺĩćàţĩōńś Change your password + Ćĥàńĝē ŷōũŕ ƥàśśŵōŕď Change password + Ćĥàńĝē ƥàśśŵōŕď + Save + Śàvē Delete account + Ďēĺēţē àććōũńţ Successfully updated details + Śũććēśśƒũĺĺŷ ũƥďàţēď ďēţàĩĺś Open settings + Ōƥēń śēţţĩńĝś No settings flow configured. + Ńō śēţţĩńĝś ƒĺōŵ ćōńƒĩĝũŕēď. Update details + Ũƥďàţē ďēţàĩĺś Successfully disconnected source + Śũććēśśƒũĺĺŷ ďĩśćōńńēćţēď śōũŕćē Failed to disconnected source: + Ƒàĩĺēď ţō ďĩśćōńńēćţēď śōũŕćē: Disconnect + Ďĩśćōńńēćţ Connect + Ćōńńēćţ Error: unsupported source settings: + Ēŕŕōŕ: ũńśũƥƥōŕţēď śōũŕćē śēţţĩńĝś: Connect your user account to the services listed below, to allow you to login using the service instead of traditional credentials. + Ćōńńēćţ ŷōũŕ ũśēŕ àććōũńţ ţō ţĥē śēŕvĩćēś ĺĩśţēď ƀēĺōŵ, ţō àĺĺōŵ ŷōũ ţō ĺōĝĩń ũśĩńĝ ţĥē śēŕvĩćē ĩńśţēàď ōƒ ţŕàďĩţĩōńàĺ ćŕēďēńţĩàĺś. No services available. + Ńō śēŕvĩćēś àvàĩĺàƀĺē. Create App password + Ćŕēàţē Àƥƥ ƥàśśŵōŕď User details + Ũśēŕ ďēţàĩĺś Consent + Ćōńśēńţ MFA Devices + MƑÀ Ďēvĩćēś Connected services + Ćōńńēćţēď śēŕvĩćēś Tokens and App passwords + Ţōķēńś àńď Àƥƥ ƥàśśŵōŕďś Unread notifications + Ũńŕēàď ńōţĩƒĩćàţĩōńś Admin interface + Àďmĩń ĩńţēŕƒàćē Stop impersonation + Śţōƥ ĩmƥēŕśōńàţĩōń Avatar image + Àvàţàŕ ĩmàĝē Failed + Ƒàĩĺēď Unsynced / N/A + Ũńśŷńćēď / Ń/À Outdated outposts + Ōũţďàţēď ōũţƥōśţś Unhealthy outposts + Ũńĥēàĺţĥŷ ōũţƥōśţś Next + Ńēxţ Inactive + Ĩńàćţĩvē Regular user + Ŕēĝũĺàŕ ũśēŕ Activate + Àćţĩvàţē Use Server URI for SNI verification + Ũśē Śēŕvēŕ ŨŔĨ ƒōŕ ŚŃĨ vēŕĩƒĩćàţĩōń Required for servers using TLS 1.3+ + Ŕēǫũĩŕēď ƒōŕ śēŕvēŕś ũśĩńĝ ŢĹŚ 1.3+ Client certificate keypair to authenticate against the LDAP Server's Certificate. + Ćĺĩēńţ ćēŕţĩƒĩćàţē ķēŷƥàĩŕ ţō àũţĥēńţĩćàţē àĝàĩńśţ ţĥē ĹĎÀƤ Śēŕvēŕ'ś Ćēŕţĩƒĩćàţē. The certificate for the above configured Base DN. As a fallback, the provider uses a self-signed certificate. + Ţĥē ćēŕţĩƒĩćàţē ƒōŕ ţĥē àƀōvē ćōńƒĩĝũŕēď ßàśē ĎŃ. Àś à ƒàĺĺƀàćķ, ţĥē ƥŕōvĩďēŕ ũśēś à śēĺƒ-śĩĝńēď ćēŕţĩƒĩćàţē. TLS Server name + ŢĹŚ Śēŕvēŕ ńàmē DNS name for which the above configured certificate should be used. The certificate cannot be detected based on the base DN, as the SSL/TLS negotiation happens before such data is exchanged. + ĎŃŚ ńàmē ƒōŕ ŵĥĩćĥ ţĥē àƀōvē ćōńƒĩĝũŕēď ćēŕţĩƒĩćàţē śĥōũĺď ƀē ũśēď. Ţĥē ćēŕţĩƒĩćàţē ćàńńōţ ƀē ďēţēćţēď ƀàśēď ōń ţĥē ƀàśē ĎŃ, àś ţĥē ŚŚĹ/ŢĹŚ ńēĝōţĩàţĩōń ĥàƥƥēńś ƀēƒōŕē śũćĥ ďàţà ĩś ēxćĥàńĝēď. TLS Client authentication certificate + ŢĹŚ Ćĺĩēńţ àũţĥēńţĩćàţĩōń ćēŕţĩƒĩćàţē Model + Mōďēĺ Match events created by selected model. When left empty, all models are matched. + Màţćĥ ēvēńţś ćŕēàţēď ƀŷ śēĺēćţēď mōďēĺ. Ŵĥēń ĺēƒţ ēmƥţŷ, àĺĺ mōďēĺś àŕē màţćĥēď. Code-based MFA Support + Ćōďē-ƀàśēď MƑÀ Śũƥƥōŕţ When enabled, code-based multi-factor authentication can be used by appending a semicolon and the TOTP code to the password. This should only be enabled if all users that will bind to this provider have a TOTP device configured, as otherwise a password may incorrectly be rejected if it contains a semicolon. + Ŵĥēń ēńàƀĺēď, ćōďē-ƀàśēď mũĺţĩ-ƒàćţōŕ àũţĥēńţĩćàţĩōń ćàń ƀē ũśēď ƀŷ àƥƥēńďĩńĝ à śēmĩćōĺōń àńď ţĥē ŢŌŢƤ ćōďē ţō ţĥē ƥàśśŵōŕď. Ţĥĩś śĥōũĺď ōńĺŷ ƀē ēńàƀĺēď ĩƒ àĺĺ ũśēŕś ţĥàţ ŵĩĺĺ ƀĩńď ţō ţĥĩś ƥŕōvĩďēŕ ĥàvē à ŢŌŢƤ ďēvĩćē ćōńƒĩĝũŕēď, àś ōţĥēŕŵĩśē à ƥàśśŵōŕď màŷ ĩńćōŕŕēćţĺŷ ƀē ŕēĴēćţēď ĩƒ ĩţ ćōńţàĩńś à śēmĩćōĺōń. User type + Ũśēŕ ţŷƥē Successfully updated license. + Śũććēśśƒũĺĺŷ ũƥďàţēď ĺĩćēńśē. Successfully created license. + Śũććēśśƒũĺĺŷ ćŕēàţēď ĺĩćēńśē. Install ID + Ĩńśţàĺĺ ĨĎ License key + Ĺĩćēńśē ķēŷ Licenses + Ĺĩćēńśēś License(s) + Ĺĩćēńśē(ś) Enterprise is in preview. + Ēńţēŕƥŕĩśē ĩś ĩń ƥŕēvĩēŵ. Cumulative license expiry + Ćũmũĺàţĩvē ĺĩćēńśē ēxƥĩŕŷ Update License + Ũƥďàţē Ĺĩćēńśē Warning: The current user count has exceeded the configured licenses. + Ŵàŕńĩńĝ: Ţĥē ćũŕŕēńţ ũśēŕ ćōũńţ ĥàś ēxćēēďēď ţĥē ćōńƒĩĝũŕēď ĺĩćēńśēś. Click here for more info. + Ćĺĩćķ ĥēŕē ƒōŕ mōŕē ĩńƒō. Enterprise + Ēńţēŕƥŕĩśē Manage enterprise licenses + Màńàĝē ēńţēŕƥŕĩśē ĺĩćēńśēś No licenses found. + Ńō ĺĩćēńśēś ƒōũńď. Send us feedback! + Śēńď ũś ƒēēďƀàćķ! Get a license + Ĝēţ à ĺĩćēńśē Go to Customer Portal + Ĝō ţō Ćũśţōmēŕ Ƥōŕţàĺ Forecast internal users + Ƒōŕēćàśţ ĩńţēŕńàĺ ũśēŕś Estimated user count one year from now based on current internal users and forecasted internal users. + Ēśţĩmàţēď ũśēŕ ćōũńţ ōńē ŷēàŕ ƒŕōm ńōŵ ƀàśēď ōń ćũŕŕēńţ ĩńţēŕńàĺ ũśēŕś àńď ƒōŕēćàśţēď ĩńţēŕńàĺ ũśēŕś. Forecast external users + Ƒōŕēćàśţ ēxţēŕńàĺ ũśēŕś Estimated user count one year from now based on current external users and forecasted external users. + Ēśţĩmàţēď ũśēŕ ćōũńţ ōńē ŷēàŕ ƒŕōm ńōŵ ƀàśēď ōń ćũŕŕēńţ ēxţēŕńàĺ ũśēŕś àńď ƒōŕēćàśţēď ēxţēŕńàĺ ũśēŕś. Install + Ĩńśţàĺĺ Install License + Ĩńśţàĺĺ Ĺĩćēńśē Internal users might be users such as company employees, which will get access to the full Enterprise feature set. + Ĩńţēŕńàĺ ũśēŕś mĩĝĥţ ƀē ũśēŕś śũćĥ àś ćōmƥàńŷ ēmƥĺōŷēēś, ŵĥĩćĥ ŵĩĺĺ ĝēţ àććēśś ţō ţĥē ƒũĺĺ Ēńţēŕƥŕĩśē ƒēàţũŕē śēţ. External users might be external consultants or B2C customers. These users don't get access to enterprise features. + Ēxţēŕńàĺ ũśēŕś mĩĝĥţ ƀē ēxţēŕńàĺ ćōńśũĺţàńţś ōŕ ß2Ć ćũśţōmēŕś. Ţĥēśē ũśēŕś ďōń'ţ ĝēţ àććēśś ţō ēńţēŕƥŕĩśē ƒēàţũŕēś. Service accounts should be used for machine-to-machine authentication or other automations. + Śēŕvĩćē àććōũńţś śĥōũĺď ƀē ũśēď ƒōŕ màćĥĩńē-ţō-màćĥĩńē àũţĥēńţĩćàţĩōń ōŕ ōţĥēŕ àũţōmàţĩōńś. Less details + Ĺēśś ďēţàĩĺś More details + Mōŕē ďēţàĩĺś Remove item + Ŕēmōvē ĩţēm Open API drawer + Ōƥēń ÀƤĨ ďŕàŵēŕ Open Notification drawer + Ōƥēń Ńōţĩƒĩćàţĩōń ďŕàŵēŕ Restart task + Ŕēśţàŕţ ţàśķ Add provider + Àďď ƥŕōvĩďēŕ Open + Ōƥēń Copy token + Ćōƥŷ ţōķēń Add users + Àďď ũśēŕś Add group + Àďď ĝŕōũƥ Import devices + Ĩmƥōŕţ ďēvĩćēś Execute + Ēxēćũţē Show details + Śĥōŵ ďēţàĩĺś Apply + Àƥƥĺŷ Settings + Śēţţĩńĝś Sign out + Śĩĝń ōũţ The number of tokens generated whenever this stage is used. Every token generated per stage execution will be attached to a single static device. + Ţĥē ńũmƀēŕ ōƒ ţōķēńś ĝēńēŕàţēď ŵĥēńēvēŕ ţĥĩś śţàĝē ĩś ũśēď. Ēvēŕŷ ţōķēń ĝēńēŕàţēď ƥēŕ śţàĝē ēxēćũţĩōń ŵĩĺĺ ƀē àţţàćĥēď ţō à śĩńĝĺē śţàţĩć ďēvĩćē. Token length + Ţōķēń ĺēńĝţĥ The length of the individual generated tokens. Can be increased to improve security. + Ţĥē ĺēńĝţĥ ōƒ ţĥē ĩńďĩvĩďũàĺ ĝēńēŕàţēď ţōķēńś. Ćàń ƀē ĩńćŕēàśēď ţō ĩmƥŕōvē śēćũŕĩţŷ. Internal: + Ĩńţēŕńàĺ: External: + Ēxţēŕńàĺ: Statically deny the flow. To use this stage effectively, disable *Evaluate when flow is planned* on the respective binding. + Śţàţĩćàĺĺŷ ďēńŷ ţĥē ƒĺōŵ. Ţō ũśē ţĥĩś śţàĝē ēƒƒēćţĩvēĺŷ, ďĩśàƀĺē *Ēvàĺũàţē ŵĥēń ƒĺōŵ ĩś ƥĺàńńēď* ōń ţĥē ŕēśƥēćţĩvē ƀĩńďĩńĝ. Create and bind Policy + Ćŕēàţē àńď ƀĩńď Ƥōĺĩćŷ Federation and Social login + Ƒēďēŕàţĩōń àńď Śōćĩàĺ ĺōĝĩń Create and bind Stage + Ćŕēàţē àńď ƀĩńď Śţàĝē Flows and Stages + Ƒĺōŵś àńď Śţàĝēś New version available + Ńēŵ vēŕśĩōń àvàĩĺàƀĺē Failure result + Ƒàĩĺũŕē ŕēśũĺţ Pass + Ƥàśś Don't pass + Ďōń'ţ ƥàśś Result used when policy execution fails. + Ŕēśũĺţ ũśēď ŵĥēń ƥōĺĩćŷ ēxēćũţĩōń ƒàĩĺś. Required: User verification must occur. + Ŕēǫũĩŕēď: Ũśēŕ vēŕĩƒĩćàţĩōń mũśţ ōććũŕ. Preferred: User verification is preferred if available, but not required. + Ƥŕēƒēŕŕēď: Ũśēŕ vēŕĩƒĩćàţĩōń ĩś ƥŕēƒēŕŕēď ĩƒ àvàĩĺàƀĺē, ƀũţ ńōţ ŕēǫũĩŕēď. Discouraged: User verification should not occur. + Ďĩśćōũŕàĝēď: Ũśēŕ vēŕĩƒĩćàţĩōń śĥōũĺď ńōţ ōććũŕ. Required: The authenticator MUST create a dedicated credential. If it cannot, the RP is prepared for an error to occur + Ŕēǫũĩŕēď: Ţĥē àũţĥēńţĩćàţōŕ MŨŚŢ ćŕēàţē à ďēďĩćàţēď ćŕēďēńţĩàĺ. Ĩƒ ĩţ ćàńńōţ, ţĥē ŔƤ ĩś ƥŕēƥàŕēď ƒōŕ àń ēŕŕōŕ ţō ōććũŕ Preferred: The authenticator can create and store a dedicated credential, but if it doesn't that's alright too + Ƥŕēƒēŕŕēď: Ţĥē àũţĥēńţĩćàţōŕ ćàń ćŕēàţē àńď śţōŕē à ďēďĩćàţēď ćŕēďēńţĩàĺ, ƀũţ ĩƒ ĩţ ďōēśń'ţ ţĥàţ'ś àĺŕĩĝĥţ ţōō Discouraged: The authenticator should not create a dedicated credential + Ďĩśćōũŕàĝēď: Ţĥē àũţĥēńţĩćàţōŕ śĥōũĺď ńōţ ćŕēàţē à ďēďĩćàţēď ćŕēďēńţĩàĺ Lock the user out of this system + Ĺōćķ ţĥē ũśēŕ ōũţ ōƒ ţĥĩś śŷśţēm Allow the user to log in and use this system + Àĺĺōŵ ţĥē ũśēŕ ţō ĺōĝ ĩń àńď ũśē ţĥĩś śŷśţēm Temporarily assume the identity of this user + Ţēmƥōŕàŕĩĺŷ àśśũmē ţĥē ĩďēńţĩţŷ ōƒ ţĥĩś ũśēŕ Enter a new password for this user + Ēńţēŕ à ńēŵ ƥàśśŵōŕď ƒōŕ ţĥĩś ũśēŕ Create a link for this user to reset their password + Ćŕēàţē à ĺĩńķ ƒōŕ ţĥĩś ũśēŕ ţō ŕēśēţ ţĥēĩŕ ƥàśśŵōŕď WebAuthn requires this page to be accessed via HTTPS. + ŴēƀÀũţĥń ŕēǫũĩŕēś ţĥĩś ƥàĝē ţō ƀē àććēśśēď vĩà ĤŢŢƤŚ. WebAuthn not supported by browser. + ŴēƀÀũţĥń ńōţ śũƥƥōŕţēď ƀŷ ƀŕōŵśēŕ. Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you). + Ũśē ţĥĩś ƥŕōvĩďēŕ ŵĩţĥ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ. Ēàćĥ àƥƥĺĩćàţĩōń/ďōmàĩń ńēēďś ĩţś ōŵń ƥŕōvĩďēŕ. Àďďĩţĩōńàĺĺŷ, ōń ēàćĥ ďōmàĩń, /ōũţƥōśţ.ĝōàũţĥēńţĩķ.ĩō mũśţ ƀē ŕōũţēď ţō ţĥē ōũţƥōśţ (ŵĥēń ũśĩńĝ à màńàĝēď ōũţƥōśţ, ţĥĩś ĩś ďōńē ƒōŕ ŷōũ). Default relay state + Ďēƒàũĺţ ŕēĺàŷ śţàţē When using IDP-initiated logins, the relay state will be set to this value. + Ŵĥēń ũśĩńĝ ĨĎƤ-ĩńĩţĩàţēď ĺōĝĩńś, ţĥē ŕēĺàŷ śţàţē ŵĩĺĺ ƀē śēţ ţō ţĥĩś vàĺũē. Flow Info + Ƒĺōŵ Ĩńƒō Stage used to configure a WebAuthn authenticator (i.e. Yubikey, FaceID/Windows Hello). + Śţàĝē ũśēď ţō ćōńƒĩĝũŕē à ŴēƀÀũţĥń àũţĥēńţĩćàţōŕ (ĩ.ē. Ŷũƀĩķēŷ, ƑàćēĨĎ/Ŵĩńďōŵś Ĥēĺĺō). Custom attributes + Ćũśţōm àţţŕĩƀũţēś Don't show this message again. + Ďōń'ţ śĥōŵ ţĥĩś mēśśàĝē àĝàĩń. + + + Pseudolocale (for testing) + Ƥśēũďōĺōćàĺē (ƒōŕ ţēśţĩńĝ) Failed to fetch From 514f4666875db7335a120b5047622bfa9f551e45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:14:58 +0200 Subject: [PATCH 18/23] web: bump the eslint group in /web with 2 updates (#7193) Bumps the eslint group in /web with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 6.7.5 to 6.8.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/v6.8.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 6.7.5 to 6.8.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/v6.8.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 88 +++++++++++++++++++++---------------------- web/package.json | 4 +- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 645eccde8..c816c65e9 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -70,8 +70,8 @@ "@types/chart.js": "^2.9.38", "@types/codemirror": "5.60.10", "@types/grecaptcha": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^6.7.5", - "@typescript-eslint/parser": "^6.7.5", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-tsconfig-paths": "^1.0.3", "cross-env": "^7.0.3", @@ -10492,16 +10492,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.5.tgz", - "integrity": "sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/type-utils": "6.7.5", - "@typescript-eslint/utils": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -10560,15 +10560,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.5.tgz", - "integrity": "sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4" }, "engines": { @@ -10588,13 +10588,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz", - "integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10605,13 +10605,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.5.tgz", - "integrity": "sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/utils": "6.7.5", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -10632,9 +10632,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz", - "integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10645,13 +10645,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz", - "integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10705,17 +10705,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.5.tgz", - "integrity": "sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", "semver": "^7.5.4" }, "engines": { @@ -10763,12 +10763,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz", - "integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", + "@typescript-eslint/types": "6.8.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/web/package.json b/web/package.json index fcc5770bc..ccc22f5e8 100644 --- a/web/package.json +++ b/web/package.json @@ -91,8 +91,8 @@ "@types/chart.js": "^2.9.38", "@types/codemirror": "5.60.10", "@types/grecaptcha": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^6.7.5", - "@typescript-eslint/parser": "^6.7.5", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-tsconfig-paths": "^1.0.3", "cross-env": "^7.0.3", From 1cd5ced2ab157ad0be06f29b7a0b500a3f3af451 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:15:10 +0200 Subject: [PATCH 19/23] core: bump ruff from 0.0.292 to 0.1.0 (#7194) Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.292 to 0.1.0. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/v0.0.292...v0.1.0) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index d509b16d1..cd5cb4769 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3425,28 +3425,28 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.0.292" +version = "0.1.0" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.292-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96"}, - {file = "ruff-0.0.292-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8"}, - {file = "ruff-0.0.292-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0"}, - {file = "ruff-0.0.292-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016"}, - {file = "ruff-0.0.292-py3-none-win32.whl", hash = "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003"}, - {file = "ruff-0.0.292-py3-none-win_amd64.whl", hash = "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c"}, - {file = "ruff-0.0.292-py3-none-win_arm64.whl", hash = "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68"}, - {file = "ruff-0.0.292.tar.gz", hash = "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac"}, + {file = "ruff-0.1.0-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:87114e254dee35e069e1b922d85d4b21a5b61aec759849f393e1dbb308a00439"}, + {file = "ruff-0.1.0-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:764f36d2982cc4a703e69fb73a280b7c539fd74b50c9ee531a4e3fe88152f521"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65f4b7fb539e5cf0f71e9bd74f8ddab74cabdd673c6fb7f17a4dcfd29f126255"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:299fff467a0f163baa282266b310589b21400de0a42d8f68553422fa6bf7ee01"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d412678bf205787263bb702c984012a4f97e460944c072fd7cfa2bd084857c4"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a5391b49b1669b540924640587d8d24128e45be17d1a916b1801d6645e831581"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee8cd57f454cdd77bbcf1e11ff4e0046fb6547cac1922cc6e3583ce4b9c326d1"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa7aeed7bc23861a2b38319b636737bf11cfa55d2109620b49cf995663d3e888"}, + {file = "ruff-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04cd4298b43b16824d9a37800e4c145ba75c29c43ce0d74cad1d66d7ae0a4c5"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7186ccf54707801d91e6314a016d1c7895e21d2e4cd614500d55870ed983aa9f"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d88adfd93849bc62449518228581d132e2023e30ebd2da097f73059900d8dce3"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ad2ccdb3bad5a61013c76a9c1240fdfadf2c7103a2aeebd7bcbbed61f363138f"}, + {file = "ruff-0.1.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b77f6cfa72c6eb19b5cac967cc49762ae14d036db033f7d97a72912770fd8e1c"}, + {file = "ruff-0.1.0-py3-none-win32.whl", hash = "sha256:480bd704e8af1afe3fd444cc52e3c900b936e6ca0baf4fb0281124330b6ceba2"}, + {file = "ruff-0.1.0-py3-none-win_amd64.whl", hash = "sha256:a76ba81860f7ee1f2d5651983f87beb835def94425022dc5f0803108f1b8bfa2"}, + {file = "ruff-0.1.0-py3-none-win_arm64.whl", hash = "sha256:45abdbdab22509a2c6052ecf7050b3f5c7d6b7898dc07e82869401b531d46da4"}, + {file = "ruff-0.1.0.tar.gz", hash = "sha256:ad6b13824714b19c5f8225871cf532afb994470eecb74631cd3500fe817e6b3f"}, ] [[package]] From b5f986c7d0d025da84c206c72b085f2e57fa0a28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:15:20 +0200 Subject: [PATCH 20/23] web: bump the eslint group in /tests/wdio with 2 updates (#7192) Bumps the eslint group in /tests/wdio with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 6.7.5 to 6.8.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/v6.8.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 6.7.5 to 6.8.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/v6.8.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/wdio/package-lock.json | 88 ++++++++++++++++++------------------ tests/wdio/package.json | 4 +- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/tests/wdio/package-lock.json b/tests/wdio/package-lock.json index 2f0ff41a9..7d7867aca 100644 --- a/tests/wdio/package-lock.json +++ b/tests/wdio/package-lock.json @@ -7,8 +7,8 @@ "name": "@goauthentik/web-tests", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.2.0", - "@typescript-eslint/eslint-plugin": "^6.7.5", - "@typescript-eslint/parser": "^6.7.5", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "@wdio/cli": "^8.18.2", "@wdio/local-runner": "^8.18.2", "@wdio/mocha-framework": "^8.18.2", @@ -878,16 +878,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.5.tgz", - "integrity": "sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/type-utils": "6.7.5", - "@typescript-eslint/utils": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -913,15 +913,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.5.tgz", - "integrity": "sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4" }, "engines": { @@ -941,13 +941,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz", - "integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -958,13 +958,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.5.tgz", - "integrity": "sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/utils": "6.7.5", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -985,9 +985,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz", - "integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -998,13 +998,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz", - "integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1025,17 +1025,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.5.tgz", - "integrity": "sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", "semver": "^7.5.4" }, "engines": { @@ -1050,12 +1050,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz", - "integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", + "@typescript-eslint/types": "6.8.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/tests/wdio/package.json b/tests/wdio/package.json index bf0f4758c..9bdc725e6 100644 --- a/tests/wdio/package.json +++ b/tests/wdio/package.json @@ -4,8 +4,8 @@ "type": "module", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.2.0", - "@typescript-eslint/eslint-plugin": "^6.7.5", - "@typescript-eslint/parser": "^6.7.5", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "@wdio/cli": "^8.18.2", "@wdio/local-runner": "^8.18.2", "@wdio/mocha-framework": "^8.18.2", From 5b9a16826ef78a1a4c62a0261939df1a15dce40f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 12:45:25 +0200 Subject: [PATCH 21/23] web: bump the esbuild group in /web with 2 updates (#7195) Bumps the esbuild group in /web with 2 updates: [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) and [@esbuild/linux-arm64](https://github.com/evanw/esbuild). Updates `@esbuild/darwin-arm64` from 0.19.4 to 0.19.5 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.19.4...v0.19.5) Updates `@esbuild/linux-arm64` from 0.19.4 to 0.19.5 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.19.4...v0.19.5) --- updated-dependencies: - dependency-name: "@esbuild/darwin-arm64" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: esbuild - dependency-name: "@esbuild/linux-arm64" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: esbuild ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package-lock.json | 17 +++++++++-------- web/package.json | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index c816c65e9..53a9ef724 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -15,6 +15,7 @@ "@codemirror/lang-xml": "^6.0.2", "@codemirror/legacy-modes": "^6.3.3", "@codemirror/theme-one-dark": "^6.1.2", + "@esbuild/linux-arm64": "^0.19.5", "@formatjs/intl-listformat": "^7.5.0", "@fortawesome/fontawesome-free": "^6.4.2", "@goauthentik/api": "^2023.8.3-1697470337", @@ -101,9 +102,9 @@ "vite-tsconfig-paths": "^4.2.1" }, "optionalDependencies": { - "@esbuild/darwin-arm64": "^0.19.4", + "@esbuild/darwin-arm64": "^0.19.5", "@esbuild/linux-amd64": "^0.18.11", - "@esbuild/linux-arm64": "^0.19.4" + "@esbuild/linux-arm64": "^0.19.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2403,9 +2404,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.4.tgz", - "integrity": "sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", "cpu": [ "arm64" ], @@ -2482,9 +2483,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.4.tgz", - "integrity": "sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", "cpu": [ "arm64" ], diff --git a/web/package.json b/web/package.json index ccc22f5e8..6735e2268 100644 --- a/web/package.json +++ b/web/package.json @@ -122,8 +122,8 @@ "vite-tsconfig-paths": "^4.2.1" }, "optionalDependencies": { - "@esbuild/darwin-arm64": "^0.19.4", + "@esbuild/darwin-arm64": "^0.19.5", "@esbuild/linux-amd64": "^0.18.11", - "@esbuild/linux-arm64": "^0.19.4" + "@esbuild/linux-arm64": "^0.19.5" } } From 3db897d64bb8fc38f899d0a6c0c06b7b6574efd5 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:53:32 +0200 Subject: [PATCH 22/23] translate: Updates for file web/xliff/en.xlf in fr (#7189) * Translate web/xliff/en.xlf in fr 100% translated source file: 'web/xliff/en.xlf' on 'fr'. * Translate web/xliff/en.xlf in fr 100% translated source file: 'web/xliff/en.xlf' on 'fr'. --------- Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com> --- web/xliff/fr.xlf | 104 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf index 178e43b15..d4f94ea4c 100644 --- a/web/xliff/fr.xlf +++ b/web/xliff/fr.xlf @@ -1,4 +1,4 @@ - + @@ -613,9 +613,9 @@ Il y a jour(s) - The URL "" was not found. - L'URL " - " n'a pas été trouvée. + The URL "" was not found. + L'URL " + " n'a pas été trouvée. @@ -1067,8 +1067,8 @@ Il y a jour(s) - To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. - Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir. + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + Pour permettre n'importe quelle URI de redirection, définissez cette valeur sur ".*". Soyez conscient des possibles implications de sécurité que cela peut avoir. @@ -1640,7 +1640,7 @@ Il y a jour(s) Token to authenticate with. Currently only bearer authentication is supported. - Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge. + Jeton d'authentification à utiliser. Actuellement, seule l'authentification "bearer authentication" est prise en charge. @@ -1808,8 +1808,8 @@ Il y a jour(s) - Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". - Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test". + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + Entrez une URL complète, un chemin relatif ou utilisez 'fa://fa-test' pour utiliser l'icône Font Awesome "fa-test". @@ -3142,7 +3142,7 @@ doesn't pass when either or both of the selected options are equal or above the To use SSL instead, use 'ldaps://' and disable this option. - Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option. + Pour utiliser SSL à la base, utilisez "ldaps://" et désactviez cette option. @@ -3231,8 +3231,8 @@ doesn't pass when either or both of the selected options are equal or above the - Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' - Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...' + Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + Champ qui contient les membres d'un groupe. Si vous utilisez le champ "memberUid", la valeur est censée contenir un nom distinctif relatif, par exemple 'memberUid=un-utilisateur' au lieu de 'memberUid=cn=un-utilisateur,ou=groups,...' @@ -3527,7 +3527,7 @@ doesn't pass when either or both of the selected options are equal or above the Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. - Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement. + Moment où les utilisateurs temporaires doivent être supprimés. Cela ne s'applique que si votre IDP utilise le format NameID "transient" et que l'utilisateur ne se déconnecte pas manuellement. @@ -3695,7 +3695,7 @@ doesn't pass when either or both of the selected options are equal or above the Optionally set the 'FriendlyName' value of the Assertion attribute. - Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel) + Indiquer la valeur "FriendlyName" de l'attribut d'assertion (optionnel) @@ -4024,8 +4024,8 @@ doesn't pass when either or both of the selected options are equal or above the - When using an external logging solution for archiving, this can be set to "minutes=5". - En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à "minutes=5". + When using an external logging solution for archiving, this can be set to "minutes=5". + En cas d'utilisation d'une solution de journalisation externe pour l'archivage, cette valeur peut être fixée à "minutes=5". @@ -4034,8 +4034,8 @@ doesn't pass when either or both of the selected options are equal or above the - Format: "weeks=3;days=2;hours=3,seconds=2". - Format : "weeks=3;days=2;hours=3,seconds=2". + Format: "weeks=3;days=2;hours=3,seconds=2". + Format : "weeks=3;days=2;hours=3,seconds=2". @@ -4231,10 +4231,10 @@ doesn't pass when either or both of the selected options are equal or above the - Are you sure you want to update ""? + Are you sure you want to update ""? Êtes-vous sûr de vouloir mettre à jour - " - " ? + " + " ? @@ -5330,8 +5330,8 @@ doesn't pass when either or both of the selected options are equal or above the - A "roaming" authenticator, like a YubiKey - Un authentificateur "itinérant", comme une YubiKey + A "roaming" authenticator, like a YubiKey + Un authentificateur "itinérant", comme une YubiKey @@ -5656,7 +5656,7 @@ doesn't pass when either or both of the selected options are equal or above the Show arbitrary input fields to the user, for example during enrollment. Data is saved in the flow context under the 'prompt_data' variable. - Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data". + Afficher des champs de saisie arbitraires à l'utilisateur, par exemple pendant l'inscription. Les données sont enregistrées dans le contexte du flux sous la variable "prompt_data". @@ -5665,10 +5665,10 @@ doesn't pass when either or both of the selected options are equal or above the - ("", of type ) + ("", of type ) - (" - ", de type + (" + ", de type ) @@ -5717,8 +5717,8 @@ doesn't pass when either or both of the selected options are equal or above the - If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. - Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici. + If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + Si défini à une durée supérieure à 0, l'utilisateur aura la possibilité de choisir de "rester connecté", ce qui prolongera sa session jusqu'à la durée spécifiée ici. @@ -6502,7 +6502,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti Can be in the format of 'unix://' when connecting to a local docker daemon, using 'ssh://' to connect via SSH, or 'https://:2376' when connecting to a remote system. - Peut être au format "unix://" pour une connexion à un service docker local, "ssh://" pour une connexion via SSH, ou "https://:2376" pour une connexion à un système distant. + Peut être au format "unix://" pour une connexion à un service docker local, "ssh://" pour une connexion via SSH, ou "https://:2376" pour une connexion à un système distant. @@ -7809,130 +7809,172 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you). + Utilisez ce fournisseur avec l'option "auth_request" de Nginx ou "forwardAuth" de Traefik. Chaque application/domaine a besoin de son propre fournisseur. De plus, sur chaque domaine, "/outpost.goauthentik.io" doit être routé vers le poste avancé (lorsque vous utilisez un poste avancé géré, cela est fait pour vous). Default relay state + Relay state par défaut When using IDP-initiated logins, the relay state will be set to this value. + Lors de l'utilisation de connexions initiées par l'IdP, le relay state sera défini à cette valeur. Flow Info + Informations du flux Stage used to configure a WebAuthn authenticator (i.e. Yubikey, FaceID/Windows Hello). + Étape de configuration d'un authentificateur WebAuthn (Yubikey, FaceID/Windows Hello). Custom attributes + Attributs personnalisés Don't show this message again. + Ne plus montrer ce message. Failed to fetch + Erreur de récupération Failed to fetch data. + Erreur de récupération des données. Successfully assigned permission. + Les permissions ont été assignées avec succès. Role + Rôle Assign + Assigner Assign permission to role + Assigner une permission à un rôle Assign to new role + Assigner à un nouveau rôle Directly assigned + Assigné directement Assign permission to user + Assigner une permission à un utilisateur Assign to new user + Assigner à un nouvel utilisateur User Object Permissions + Permissions de l'objet utilisateur Role Object Permissions + Permission de l'objet rôle Roles + Rôles Select roles to grant this groups' users' permissions from the selected roles. + Sélectionner les roles depuis lesquels assigner les permissions des utilisateurs de ce groupe depuis les rôles sélectionnés. Update Permissions + Mettre à jour les permissions Editing is disabled for managed tokens + L'édition est désactivée pour les jetons gérés Select permissions to grant + Sélectionner les permissions à attribuer Permissions to add + Permissions à ajouter Select permissions + Sélectionner les permissions Assign permission + Assigner les permissions Permission(s) + Permission(s) Permission + Permission User doesn't have view permission so description cannot be retrieved. + L'utilisateur n'a pas les permissions de lecture, la description ne peut donc pas être récupérée. Assigned permissions + Permissions assignées Assigned global permissions + Permissions globales assignées Assigned object permissions + Permissions d'objet assignées Successfully updated role. + Rôle mis à jour avec succès. Successfully created role. + Rôle créé avec succès. Manage roles which grant permissions to objects within authentik. + Gérer les rôles qui attribuent des permissions sur les objets au sein d'authentik. Role(s) + Role(s) Update Role + Mettre à jour le rôle Create Role + Créer un rôle Role doesn't have view permission so description cannot be retrieved. + Le rôle n'a pas les permissions de lecture, la description ne peut donc pas être récupérée. Role + Rôle Role Info + Informations du rôle - + \ No newline at end of file From 914e0d2b013f7c25d9c788f58203edf18d46d70f Mon Sep 17 00:00:00 2001 From: Marc 'risson' Schmitt Date: Tue, 17 Oct 2023 18:04:57 +0200 Subject: [PATCH 23/23] ci: test with postgres 16 Signed-off-by: Marc 'risson' Schmitt --- .github/workflows/ci-main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index d24a2a88d..097321888 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -90,6 +90,7 @@ jobs: psql: - 12-alpine - 15-alpine + - 16-alpine steps: - uses: actions/checkout@v4 - name: Setup authentik env