Compare commits
522 Commits
version/20
...
version-20
Author | SHA1 | Date | |
---|---|---|---|
4b33971155 | |||
9e71287c25 | |||
9784c6c828 | |||
732b6a3556 | |||
dc1e17ba0c | |||
f05d5973af | |||
deb48487f3 | |||
78f3abc64f | |||
e45bc3834a | |||
0d9db1b6f2 | |||
ce555aa5e9 | |||
07ca82e599 | |||
a9339589bb | |||
c8ed650f1c | |||
cd78d8d3fa | |||
7fdc935fb9 | |||
c8069325b3 | |||
9d08e02fe1 | |||
a11ea598a2 | |||
2713b05e8c | |||
fef5a5ca52 | |||
9d339d8b11 | |||
4e86aa3f59 | |||
221e4b665c | |||
e67f235a9f | |||
741ebbacca | |||
b63b789f77 | |||
a63702ef90 | |||
a4a4550753 | |||
fd864655f6 | |||
c1da09507a | |||
ed2ea220bf | |||
7738cbe751 | |||
bf16ea3607 | |||
d6f44e069c | |||
899cf392f4 | |||
d99451b45c | |||
5b31f8edf6 | |||
00235e039b | |||
2dfaef4220 | |||
13fceacfe4 | |||
f8dc32b387 | |||
828f2f8b92 | |||
734399755d | |||
d8f106b976 | |||
9a524dd671 | |||
0775296003 | |||
390534c14e | |||
2a644f64ad | |||
e0298141cf | |||
df7119bb22 | |||
1d5bba831e | |||
0b4be70c00 | |||
786737650b | |||
54c80a2e1f | |||
b376211a0e | |||
1990a3063e | |||
5abf22ad8a | |||
b7b87d87fc | |||
20184424ab | |||
d5de12b69e | |||
d1a3350085 | |||
e0b84c71a7 | |||
3bc1d6a690 | |||
786c74ef2c | |||
3e9b5f5449 | |||
5d071488d3 | |||
90d234a458 | |||
0032bb6aee | |||
6e6755d805 | |||
132b990f10 | |||
34a3d81eff | |||
43a4217497 | |||
e0ec5826ca | |||
5413a01360 | |||
d9c3a29404 | |||
bcce91476c | |||
56f0f454d0 | |||
25e63edf77 | |||
d150851ff5 | |||
2e2840c71e | |||
ff276fcc58 | |||
2852fa3c5e | |||
1c6d498621 | |||
3f0e4bb654 | |||
a59d78a7c7 | |||
0a24202f1e | |||
cbc86d674d | |||
082628771b | |||
93b50e7d6e | |||
c6de4e47d7 | |||
0e9e378bdf | |||
de4b3d6290 | |||
56f75aecc7 | |||
0fe009d37c | |||
49db283e71 | |||
7058366623 | |||
ced45513b8 | |||
15e15c9635 | |||
d53c82eee2 | |||
e1e0b0cf7d | |||
33e013a59f | |||
96a74776f8 | |||
bb63d08682 | |||
32655567da | |||
ff5f5f65e8 | |||
1f97aa09fa | |||
32e5ebb8a3 | |||
597e00dd86 | |||
dd31191845 | |||
e9d95b1311 | |||
3319547a0e | |||
1a00730cdd | |||
466723573c | |||
ea784d47f4 | |||
77d5ba2862 | |||
f4580a1097 | |||
9e3d1f0baa | |||
c002c4b610 | |||
dde5e910cf | |||
5218332bce | |||
28cd08bbba | |||
3cb0575a1e | |||
dc1c1b9569 | |||
662d117b66 | |||
b2449757f9 | |||
a0753bfc88 | |||
e2a771bdaa | |||
23de9df2a5 | |||
5c739ebed2 | |||
d3f8d7120f | |||
21fd251edf | |||
28cededb90 | |||
d420719649 | |||
0018fbacd3 | |||
8c41d2f4cb | |||
3941590d0c | |||
dc4a7c35da | |||
e8c9b70ae8 | |||
74d240dfd4 | |||
7d296b2119 | |||
373793ce9a | |||
5c0ec7554b | |||
792fa45dca | |||
743aaea15e | |||
de03ed0aec | |||
e68ec16a34 | |||
68a0219d0f | |||
38d9533afd | |||
7538af5e09 | |||
2e659c1ab0 | |||
ad0cc5f0be | |||
7ae9482e7b | |||
7fb95dfabf | |||
83cc5d24f2 | |||
38b3096c9a | |||
df8f21e559 | |||
f4979fcf19 | |||
431b7375c1 | |||
a6627145c8 | |||
3045cf1aef | |||
c65b2944b3 | |||
2ae5a81c15 | |||
ed8b78600e | |||
644a03e40e | |||
88ce93ab04 | |||
8878dc61d3 | |||
03d38557e5 | |||
37b59bb5b9 | |||
19eea68e0f | |||
ce7aae16c9 | |||
fd9ba97479 | |||
919debdd13 | |||
36690de285 | |||
ca4ead8fd8 | |||
a81f981471 | |||
d6fd2b0afa | |||
0478ae3da8 | |||
9c33f4858f | |||
f2eaa9052e | |||
21d0641110 | |||
67d05f99e9 | |||
21d6a28715 | |||
1149a8d9a4 | |||
5e98172afb | |||
9b3e94c7c8 | |||
30a1b65e94 | |||
9bb46ecb88 | |||
269e6c4f38 | |||
7f65ae3f92 | |||
ee6b365003 | |||
2ad4bd5c0a | |||
0958740b51 | |||
9cdfd8b75a | |||
3c8a0081bc | |||
088e0e736a | |||
cbb0681f95 | |||
55c408a8bf | |||
07379acf7f | |||
a1af93f8be | |||
b9a9da4ec7 | |||
05a5b5b675 | |||
0fb17eee43 | |||
a1474e09e5 | |||
a33c7d7786 | |||
c08d9762d9 | |||
d43e6e5736 | |||
380786bfde | |||
ffcf064f83 | |||
252718bbaf | |||
5725e54334 | |||
c20856ca17 | |||
402afa1e85 | |||
5b4e75000b | |||
9c73e9cf4e | |||
b10c3db13d | |||
1a052913e9 | |||
e930a1d0dc | |||
fe290aa214 | |||
a2e69bd250 | |||
d2a35eb8de | |||
3437d8b4b0 | |||
b862bf4284 | |||
de22a367b1 | |||
17ab895652 | |||
a4d5815e1b | |||
4cbfaaa72b | |||
92943f08d9 | |||
10ef1c7e93 | |||
02c762c268 | |||
bbf0ca92af | |||
d2dfc6d63b | |||
a18240fcd7 | |||
d36e5dccf9 | |||
9af1d6f63b | |||
ab6d46558b | |||
e94abfc986 | |||
5c652c1f79 | |||
89aa0f0cc8 | |||
085589bcec | |||
95d0d6f3e8 | |||
c62ef4ae81 | |||
3df81ca6f0 | |||
578326eccd | |||
2335ccddaa | |||
477e30f542 | |||
7bf3d7e10a | |||
1bef659b10 | |||
e3f7bd8ab8 | |||
45c731de3c | |||
535770abbd | |||
eccea8eba0 | |||
ab200a1dfb | |||
ca122b20c9 | |||
74b407ebc7 | |||
fbf2fe2404 | |||
b968adffc1 | |||
c275992f7b | |||
4e2c686db1 | |||
bfc69562d8 | |||
9e6a7bf16b | |||
890e0e9054 | |||
cf7e7c44ff | |||
0f169f176d | |||
429fc921b1 | |||
e7a9a41a2f | |||
d1c24f47b2 | |||
007676b400 | |||
c0c235bead | |||
a3aacb5285 | |||
5977c09b05 | |||
e81d3dad3e | |||
5aabaebd96 | |||
7b60bca297 | |||
a07d7456c8 | |||
f33369bf0c | |||
1abcff39c7 | |||
c1caf84d92 | |||
86c069fe64 | |||
ce0140ef67 | |||
bba43c5109 | |||
d99a415502 | |||
9049593ff5 | |||
e74c098b7a | |||
d06a44378d | |||
0a8da376fc | |||
2a0f940a42 | |||
8aa067795a | |||
3cdb81c5ba | |||
e8259791f0 | |||
55af786852 | |||
8a916602c4 | |||
7101c7987c | |||
bd48955f39 | |||
53adcd9157 | |||
c5a2bb8914 | |||
66e5958283 | |||
9db445c3ee | |||
574438b51e | |||
a05885140d | |||
8878fac4e7 | |||
7ee97a961c | |||
737ff62e92 | |||
07ada5a1b7 | |||
8caeed6b18 | |||
b5adff5327 | |||
3894895d32 | |||
7f53c97fb2 | |||
44bd4b9511 | |||
2a1b5e0154 | |||
8c0d48fe0a | |||
0863e60d29 | |||
451c117ea4 | |||
388c8c8bec | |||
5904070bb2 | |||
35ac87ec10 | |||
8f8c2a291b | |||
592a2dcede | |||
a3221475e5 | |||
25f5031422 | |||
63b94263af | |||
217595bb01 | |||
2dd8119abe | |||
20e0fe3941 | |||
0fa97de06e | |||
38da13fea3 | |||
fb9880bff4 | |||
acc790f590 | |||
76c572cf7c | |||
0904fea109 | |||
6df89e7abf | |||
21afda6dc2 | |||
74c0ed27ba | |||
dc680a3385 | |||
88e5b22d16 | |||
27cd10e072 | |||
d35f524865 | |||
ca223fa4df | |||
14962eb6cc | |||
b9f409d6d9 | |||
a8681ac88f | |||
c1e6786ea1 | |||
1c8d101fc3 | |||
7a9140bdcd | |||
511f94fc7f | |||
548b1ead2f | |||
33f67140f2 | |||
8787dc23d0 | |||
e0ae92ccc7 | |||
bdb86d7119 | |||
a1a3d316e3 | |||
672b86ef88 | |||
a3c9d5873c | |||
0e975757b8 | |||
391ee10cb8 | |||
4f374c0c01 | |||
dde303f13a | |||
264c678eaa | |||
854d94056e | |||
9d4c22c706 | |||
9b12895fab | |||
93478a55d7 | |||
a76cbf8b70 | |||
6597d5bd28 | |||
fd28f37c0d | |||
d219f65e7a | |||
865f652476 | |||
8008918d8b | |||
75d0bd01c2 | |||
029c6cd182 | |||
71f771c22c | |||
0993d5ce4a | |||
38bd05867d | |||
79089d8981 | |||
44e51970e1 | |||
47bde052ca | |||
bd6a473d4f | |||
cd23053007 | |||
6e11fd0f2e | |||
277b4336d3 | |||
1c1f9b6cb8 | |||
c23df5e1d5 | |||
c47cef6fbf | |||
83b7b3257a | |||
270be95e68 | |||
1c919b8b88 | |||
1e51a2cdd7 | |||
7ba44b15a7 | |||
4a94f515b3 | |||
b229b2f40d | |||
e4f0613fab | |||
ecff810021 | |||
fdde97cbbf | |||
c2a5641e6a | |||
5a47c4850d | |||
70b8a941bb | |||
eb01b42425 | |||
8708e487ae | |||
e020b8bf32 | |||
8e27121e10 | |||
06870b4f64 | |||
4cfcc48b23 | |||
60c244c31d | |||
d122bddae2 | |||
69e6221906 | |||
68eefd083e | |||
a647917074 | |||
099197ba8c | |||
baa2ed5ecc | |||
f8ba623fc1 | |||
6bcdf36ca6 | |||
416d949d80 | |||
0b75a0028b | |||
0901d7461e | |||
61772b75ff | |||
0ade57b5a6 | |||
61604adf9a | |||
8bd147b205 | |||
724f53e972 | |||
c10478ec68 | |||
cdf12ee03d | |||
964a8dbb82 | |||
7ad48bfc44 | |||
da90510b98 | |||
4bd1598c2c | |||
6aa8d56d9f | |||
ccf7d794e9 | |||
50ed2fb257 | |||
5ae030997a | |||
52dabcaad9 | |||
35e8a0c374 | |||
be292729a5 | |||
1649c478b6 | |||
42feb54d80 | |||
bbd088a957 | |||
5417d0a90c | |||
417b5d61a4 | |||
f13aad21cb | |||
79e8ee46c0 | |||
e3eaaeaf17 | |||
e550216f85 | |||
1afb4a7a76 | |||
391eb9d469 | |||
494f094fa1 | |||
aa0f5df218 | |||
6fc740a98b | |||
7da90ff7e4 | |||
61b5714652 | |||
d2df426489 | |||
e6c75ed173 | |||
a353c6956e | |||
a367d8515f | |||
2b7a22a29a | |||
e6712a50d2 | |||
c621f62d92 | |||
a0648cd925 | |||
2650e672bb | |||
53b9376789 | |||
d15e50025c | |||
0af66a26ab | |||
bf754369d9 | |||
02dc112f8f | |||
2d4e7ebab5 | |||
a7d0a50859 | |||
71c9108f89 | |||
f8bcdb26b3 | |||
45f1d95bf9 | |||
5dab198c47 | |||
ad91abe9de | |||
fa30755241 | |||
552f8c6a9a | |||
101f916247 | |||
2acdcf74e1 | |||
ddb8610032 | |||
22ad850e6c | |||
57925ed60a | |||
48cc2f17c1 | |||
448108fca0 | |||
c1254f6212 | |||
c8120c0d3e | |||
52016e0806 | |||
e555bdd42b | |||
1a619c90de | |||
18faf30b0c | |||
b3bd979ecd | |||
db113c5e8f | |||
78bcb90a1e | |||
b64ecbde22 | |||
43bab840ec | |||
f020b79384 | |||
820f658b49 | |||
5d460a2537 | |||
efc46f52e6 | |||
9fac51f8c7 | |||
fe4b2d1a34 | |||
f8abe3e210 | |||
3ced67b151 | |||
cd5631ec76 | |||
95df7c7f30 | |||
1e934aa5d5 | |||
d93927755a | |||
ddb3b71dce | |||
bf9826873e | |||
6869b3c16a | |||
9b71b8da5f | |||
bfc8e9200f | |||
c4311abc9f | |||
ec42869e00 | |||
45963c2ffc | |||
1aa27b5e80 | |||
1737feec91 | |||
a0e0fb930a | |||
4a32c3ca11 | |||
d307539fd0 | |||
c060a3eec2 | |||
4612ae1ff4 | |||
7af883d80c | |||
4a5374d03f | |||
3b536f6e55 | |||
6aa13a8666 | |||
24e4924dec | |||
a252f303c0 |
@ -1,9 +1,11 @@
|
||||
[bumpversion]
|
||||
current_version = 2021.2.1-rc1
|
||||
current_version = 2021.3.4
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||
serialize = {major}.{minor}.{patch}-{release}
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
|
||||
serialize =
|
||||
{major}.{minor}.{patch}-{release}
|
||||
{major}.{minor}.{patch}
|
||||
message = release: {new_version}
|
||||
tag_name = version/{new_version}
|
||||
|
||||
@ -34,3 +36,7 @@ values =
|
||||
[bumpversion:file:outpost/pkg/version.go]
|
||||
|
||||
[bumpversion:file:web/src/constants.ts]
|
||||
|
||||
[bumpversion:file:website/docs/outposts/manual-deploy-docker-compose.md]
|
||||
|
||||
[bumpversion:file:website/docs/outposts/manual-deploy-kubernetes.md]
|
||||
|
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@ -1,7 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: "/proxy"
|
||||
directory: "/outpost"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@ -16,6 +16,14 @@ updates:
|
||||
open-pull-requests-limit: 10
|
||||
assignees:
|
||||
- BeryJu
|
||||
- package-ecosystem: npm
|
||||
directory: "/website"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
open-pull-requests-limit: 10
|
||||
assignees:
|
||||
- BeryJu
|
||||
- package-ecosystem: pip
|
||||
directory: "/"
|
||||
schedule:
|
||||
@ -33,7 +41,7 @@ updates:
|
||||
assignees:
|
||||
- BeryJu
|
||||
- package-ecosystem: docker
|
||||
directory: "/proxy"
|
||||
directory: "/outpost"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
|
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@ -18,11 +18,11 @@ jobs:
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
-t beryju/authentik:2021.2.1-rc1
|
||||
-t beryju/authentik:2021.3.4
|
||||
-t beryju/authentik:latest
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/authentik:2021.2.1-rc1
|
||||
run: docker push beryju/authentik:2021.3.4
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/authentik:latest
|
||||
build-proxy:
|
||||
@ -48,17 +48,20 @@ jobs:
|
||||
cd outpost/
|
||||
docker build \
|
||||
--no-cache \
|
||||
-t beryju/authentik-proxy:2021.2.1-rc1 \
|
||||
-t beryju/authentik-proxy:2021.3.4 \
|
||||
-t beryju/authentik-proxy:latest \
|
||||
-f proxy.Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/authentik-proxy:2021.2.1-rc1
|
||||
run: docker push beryju/authentik-proxy:2021.3.4
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/authentik-proxy:latest
|
||||
build-static:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare ts api client
|
||||
run: |
|
||||
docker run --rm -v $(pwd):/local openapitools/openapi-generator-cli generate -i /local/swagger.yaml -g typescript-fetch -o /local/web/src/api --additional-properties=typescriptThreePlus=true
|
||||
- name: Docker Login Registry
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
@ -69,11 +72,11 @@ jobs:
|
||||
cd web/
|
||||
docker build \
|
||||
--no-cache \
|
||||
-t beryju/authentik-static:2021.2.1-rc1 \
|
||||
-t beryju/authentik-static:2021.3.4 \
|
||||
-t beryju/authentik-static:latest \
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/authentik-static:2021.2.1-rc1
|
||||
run: docker push beryju/authentik-static:2021.3.4
|
||||
- name: Push Docker Container to Registry (latest)
|
||||
run: docker push beryju/authentik-static:latest
|
||||
test-release:
|
||||
@ -107,5 +110,5 @@ jobs:
|
||||
SENTRY_PROJECT: authentik
|
||||
SENTRY_URL: https://sentry.beryju.org
|
||||
with:
|
||||
tagName: 2021.2.1-rc1
|
||||
tagName: 2021.3.4
|
||||
environment: beryjuorg-prod
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -201,3 +201,4 @@ local.env.yml
|
||||
selenium_screenshots/
|
||||
backups/
|
||||
media/
|
||||
*mmdb
|
||||
|
@ -15,12 +15,15 @@ WORKDIR /
|
||||
COPY --from=locker /app/requirements.txt /
|
||||
COPY --from=locker /app/requirements-dev.txt /
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends curl ca-certificates gnupg && \
|
||||
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends postgresql-client-12 postgresql-client-11 build-essential libxmlsec1-dev pkg-config && \
|
||||
apt-get install -y --no-install-recommends postgresql-client-12 postgresql-client-11 build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
|
||||
apt-get clean && \
|
||||
pip install -r /requirements.txt --no-cache-dir && \
|
||||
apt-get remove --purge -y build-essential && \
|
||||
@ -45,4 +48,5 @@ COPY ./lifecycle/ /lifecycle
|
||||
USER authentik
|
||||
STOPSIGNAL SIGINT
|
||||
ENV TMPDIR /dev/shm/
|
||||
ENV PYTHONUBUFFERED 1
|
||||
ENTRYPOINT [ "/lifecycle/bootstrap.sh" ]
|
||||
|
11
Makefile
11
Makefile
@ -1,20 +1,15 @@
|
||||
all: lint-fix lint coverage gen
|
||||
|
||||
test-full:
|
||||
coverage run manage.py test --failfast -v 3 .
|
||||
coverage html
|
||||
coverage report
|
||||
|
||||
test-integration:
|
||||
k3d cluster create || exit 0
|
||||
k3d kubeconfig write -o ~/.kube/config --overwrite
|
||||
coverage run manage.py test --failfast -v 3 tests/integration
|
||||
coverage run manage.py test -v 3 tests/integration
|
||||
|
||||
test-e2e:
|
||||
coverage run manage.py test --failfast -v 3 tests/e2e
|
||||
coverage run manage.py test -v 3 tests/e2e
|
||||
|
||||
coverage:
|
||||
coverage run manage.py test --failfast -v 3 authentik
|
||||
coverage run manage.py test -v 3 authentik
|
||||
coverage html
|
||||
coverage report
|
||||
|
||||
|
24
Pipfile
24
Pipfile
@ -6,6 +6,9 @@ verify_ssl = true
|
||||
[packages]
|
||||
boto3 = "*"
|
||||
celery = "*"
|
||||
channels = "*"
|
||||
channels-redis = "*"
|
||||
dacite = "*"
|
||||
defusedxml = "*"
|
||||
django = "*"
|
||||
django-cors-middleware = "*"
|
||||
@ -15,35 +18,33 @@ django-guardian = "*"
|
||||
django-model-utils = "*"
|
||||
django-otp = "*"
|
||||
django-prometheus = "*"
|
||||
django-recaptcha = "*"
|
||||
django-redis = "*"
|
||||
djangorestframework = "*"
|
||||
django-storages = "*"
|
||||
djangorestframework = "*"
|
||||
djangorestframework-guardian = "*"
|
||||
docker = "*"
|
||||
drf_yasg2 = "*"
|
||||
facebook-sdk = "*"
|
||||
geoip2 = "*"
|
||||
gunicorn = "*"
|
||||
kubernetes = "*"
|
||||
ldap3 = "*"
|
||||
lxml = "*"
|
||||
packaging = "*"
|
||||
psycopg2-binary = "*"
|
||||
pycryptodome = "*"
|
||||
pyjwkest = "*"
|
||||
uvicorn = {extras = ["standard"],version = "*"}
|
||||
gunicorn = "*"
|
||||
pyyaml = "*"
|
||||
qrcode = "*"
|
||||
requests-oauthlib = "*"
|
||||
sentry-sdk = "*"
|
||||
service_identity = "*"
|
||||
structlog = "*"
|
||||
swagger-spec-validator = "*"
|
||||
urllib3 = {extras = ["secure"],version = "*"}
|
||||
dacite = "*"
|
||||
channels = "*"
|
||||
channels-redis = "*"
|
||||
kubernetes = "*"
|
||||
docker = "*"
|
||||
uvicorn = {extras = ["standard"],version = "*"}
|
||||
webauthn = "*"
|
||||
xmlsec = "*"
|
||||
twisted = "==20.3.0"
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
||||
@ -55,8 +56,7 @@ black = "==20.8b1"
|
||||
bumpversion = "*"
|
||||
colorama = "*"
|
||||
coverage = "*"
|
||||
django-debug-toolbar = "*"
|
||||
pylint = "*"
|
||||
pylint = "<=2.6.0"
|
||||
pylint-django = "*"
|
||||
selenium = "*"
|
||||
prospector = "*"
|
||||
|
742
Pipfile.lock
generated
742
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
11
README.md
11
README.md
@ -1,7 +1,10 @@
|
||||
<img src="https://goauthentik.io/img/icon_top_brand_colour.svg" height="250" alt="authentik logo">
|
||||
<p align="center">
|
||||
<img src="https://goauthentik.io/img/icon_top_brand_colour.svg" height="150" alt="authentik logo">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
[](https://discord.gg/KPnmtNWy)
|
||||
[](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=1)
|
||||
[](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=1)
|
||||
[](https://codecov.io/gh/BeryJu/authentik)
|
||||
@ -21,8 +24,10 @@ For bigger setups, there is a Helm Chart in the `helm/` directory. This is docum
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
Light | Dark
|
||||
--- | ---
|
||||
 | 
|
||||
 | 
|
||||
|
||||
## Development
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | ------------------ |
|
||||
| 0.13.x | :white_check_mark: |
|
||||
| 0.14.x | :white_check_mark: |
|
||||
| 2021.1.x | :white_check_mark: |
|
||||
| 2021.2.x | :white_check_mark: |
|
||||
| 2021.3.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
"""authentik"""
|
||||
__version__ = "2021.2.1-rc1"
|
||||
__version__ = "2021.3.4"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
@ -2,14 +2,13 @@
|
||||
import time
|
||||
from collections import Counter
|
||||
from datetime import timedelta
|
||||
from typing import Dict, List
|
||||
|
||||
from django.db.models import Count, ExpressionWrapper, F, Model
|
||||
from django.db.models.fields import DurationField
|
||||
from django.db.models.functions import ExtractHour
|
||||
from django.utils.timezone import now
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
from drf_yasg2.utils import swagger_auto_schema, swagger_serializer_method
|
||||
from rest_framework.fields import IntegerField, SerializerMethodField
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
@ -19,7 +18,7 @@ from rest_framework.viewsets import ViewSet
|
||||
from authentik.events.models import Event, EventAction
|
||||
|
||||
|
||||
def get_events_per_1h(**filter_kwargs) -> List[Dict[str, int]]:
|
||||
def get_events_per_1h(**filter_kwargs) -> list[dict[str, int]]:
|
||||
"""Get event count by hour in the last day, fill with zeros"""
|
||||
date_from = now() - timedelta(days=1)
|
||||
result = (
|
||||
@ -32,29 +31,45 @@ def get_events_per_1h(**filter_kwargs) -> List[Dict[str, int]]:
|
||||
.annotate(count=Count("pk"))
|
||||
.order_by("age_hours")
|
||||
)
|
||||
data = Counter({d["age_hours"]: d["count"] for d in result})
|
||||
data = Counter({int(d["age_hours"]): d["count"] for d in result})
|
||||
results = []
|
||||
_now = now()
|
||||
for hour in range(0, -24, -1):
|
||||
results.append(
|
||||
{
|
||||
"x": time.mktime((_now + timedelta(hours=hour)).timetuple()) * 1000,
|
||||
"y": data[hour * -1],
|
||||
"x_cord": time.mktime((_now + timedelta(hours=hour)).timetuple())
|
||||
* 1000,
|
||||
"y_cord": data[hour * -1],
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
class AdministrationMetricsSerializer(Serializer):
|
||||
class CoordinateSerializer(Serializer):
|
||||
"""Coordinates for diagrams"""
|
||||
|
||||
x_cord = IntegerField(read_only=True)
|
||||
y_cord = IntegerField(read_only=True)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class LoginMetricsSerializer(Serializer):
|
||||
"""Login Metrics per 1h"""
|
||||
|
||||
logins_per_1h = SerializerMethodField()
|
||||
logins_failed_per_1h = SerializerMethodField()
|
||||
|
||||
@swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True))
|
||||
def get_logins_per_1h(self, _):
|
||||
"""Get successful logins per hour for the last 24 hours"""
|
||||
return get_events_per_1h(action=EventAction.LOGIN)
|
||||
|
||||
@swagger_serializer_method(serializer_or_field=CoordinateSerializer(many=True))
|
||||
def get_logins_failed_per_1h(self, _):
|
||||
"""Get failed logins per hour for the last 24 hours"""
|
||||
return get_events_per_1h(action=EventAction.LOGIN_FAILED)
|
||||
@ -71,8 +86,8 @@ class AdministrationMetricsViewSet(ViewSet):
|
||||
|
||||
permission_classes = [IsAdminUser]
|
||||
|
||||
@swagger_auto_schema(responses={200: AdministrationMetricsSerializer(many=True)})
|
||||
@swagger_auto_schema(responses={200: LoginMetricsSerializer(many=False)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Login Metrics per 1h"""
|
||||
serializer = AdministrationMetricsSerializer(True)
|
||||
serializer = LoginMetricsSerializer(True)
|
||||
return Response(serializer.data)
|
||||
|
@ -7,14 +7,14 @@ from django.http.response import Http404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField, DateTimeField, IntegerField, ListField
|
||||
from rest_framework.fields import CharField, ChoiceField, DateTimeField, ListField
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.events.monitored_tasks import TaskInfo
|
||||
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
|
||||
|
||||
|
||||
class TaskSerializer(Serializer):
|
||||
@ -24,7 +24,10 @@ class TaskSerializer(Serializer):
|
||||
task_description = CharField()
|
||||
task_finish_timestamp = DateTimeField(source="finish_timestamp")
|
||||
|
||||
status = IntegerField(source="result.status.value")
|
||||
status = ChoiceField(
|
||||
source="result.status.name",
|
||||
choices=[(x.name, x.name) for x in TaskResultStatus],
|
||||
)
|
||||
messages = ListField(source="result.messages")
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
|
@ -1,4 +1,6 @@
|
||||
"""authentik administration overview"""
|
||||
from os import environ
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Model
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
@ -11,7 +13,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik import __version__
|
||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
|
||||
|
||||
|
||||
@ -20,8 +22,13 @@ class VersionSerializer(Serializer):
|
||||
|
||||
version_current = SerializerMethodField()
|
||||
version_latest = SerializerMethodField()
|
||||
build_hash = SerializerMethodField()
|
||||
outdated = SerializerMethodField()
|
||||
|
||||
def get_build_hash(self, _) -> str:
|
||||
"""Get build hash, if version is not latest or released"""
|
||||
return environ.get(ENV_GIT_HASH_KEY, "")
|
||||
|
||||
def get_version_current(self, _) -> str:
|
||||
"""Get current version"""
|
||||
return __version__
|
||||
@ -55,7 +62,7 @@ class VersionViewSet(ListModelMixin, GenericViewSet):
|
||||
def get_queryset(self): # pragma: no cover
|
||||
return None
|
||||
|
||||
@swagger_auto_schema(responses={200: VersionSerializer(many=True)})
|
||||
@swagger_auto_schema(responses={200: VersionSerializer(many=False)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Get running and latest version."""
|
||||
return Response(VersionSerializer(True).data)
|
||||
|
@ -1,19 +0,0 @@
|
||||
"""authentik core source form fields"""
|
||||
|
||||
SOURCE_FORM_FIELDS = [
|
||||
"name",
|
||||
"slug",
|
||||
"enabled",
|
||||
"authentication_flow",
|
||||
"enrollment_flow",
|
||||
]
|
||||
SOURCE_SERIALIZER_FIELDS = [
|
||||
"pk",
|
||||
"name",
|
||||
"slug",
|
||||
"enabled",
|
||||
"authentication_flow",
|
||||
"enrollment_flow",
|
||||
"verbose_name",
|
||||
"verbose_name_plural",
|
||||
]
|
@ -0,0 +1,14 @@
|
||||
{% extends base_template|default:"generic/form.html" %}
|
||||
|
||||
{% load authentik_utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block above_form %}
|
||||
<h1>
|
||||
{% trans 'Generate Certificate-Key Pair' %}
|
||||
</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block action %}
|
||||
{% trans 'Generate Certificate-Key Pair' %}
|
||||
{% endblock %}
|
@ -1,116 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-key"></i>
|
||||
{% trans 'Certificate-Key Pairs' %}
|
||||
</h1>
|
||||
<p>{% trans "Import certificates of external providers or create certificates to sign requests with." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:certificatekeypair-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Private Key available' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Fingerprint' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for kp in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ kp.name }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{% if kp.key_data is not None %}
|
||||
{% trans 'Yes' %}
|
||||
{% else %}
|
||||
{% trans 'No' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<code>{{ kp.fingerprint }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:certificatekeypair-update' pk=kp.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:certificatekeypair-delete' pk=kp.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-key pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Certificates.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any certificates." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no certificates exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:certificatekeypair-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,135 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-process-automation"></i>
|
||||
{% trans 'Flows' %}
|
||||
</h1>
|
||||
<p>{% trans "Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:flow-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:flow-import' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Import' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Identifier' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Designation' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Stages' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Policies' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for flow in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<a href="/flows/{{ flow.slug }}">
|
||||
<div><code>{{ flow.slug }}</code></div>
|
||||
<small>{{ flow.name }}</small>
|
||||
</a>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ flow.designation }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ flow.stages.all|length }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ flow.policies.all|length }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:flow-update' pk=flow.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:flow-delete' pk=flow.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<a class="pf-c-button pf-m-secondary ak-root-link" href="{% url 'authentik_admin:flow-execute' pk=flow.pk %}?next={{ request.get_full_path }}">{% trans 'Execute' %}</a>
|
||||
<a class="pf-c-button pf-m-secondary ak-root-link" href="{% url 'authentik_admin:flow-export' pk=flow.pk %}?next={{ request.get_full_path }}">{% trans 'Export' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-process-automation pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Flows.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any flows." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no flows exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:flow-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:flow-import' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Import' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,114 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-users"></i>
|
||||
{% trans 'Groups' %}
|
||||
</h1>
|
||||
<p>{% trans "Group users together and give them permissions based on the membership." %}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:group-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Parent' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Members' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for group in object_list %}
|
||||
<tr role="row">
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ group.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ group.parent }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ group.users.all|length }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:group-update' pk=group.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:group-delete' pk=group.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-users pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Groups.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any groups." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no group exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:group-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,149 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load authentik_utils %}
|
||||
{% load admin_reflection %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-zone"></i>
|
||||
{% trans 'Outposts' %}
|
||||
</h1>
|
||||
<p>{% trans "Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Providers' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Health' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Version' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for outpost in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<span>{{ outpost.name }}</span>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ outpost.providers.all.select_subclasses|join:", " }}
|
||||
</span>
|
||||
</td>
|
||||
{% with states=outpost.state %}
|
||||
{% if states|length > 0 %}
|
||||
<td role="cell">
|
||||
{% for state in states %}
|
||||
<div>
|
||||
{% if state.last_seen %}
|
||||
<i class="fas fa-check pf-m-success"></i> {{ state.last_seen|naturaltime }}
|
||||
{% else %}
|
||||
<i class="fas fa-times pf-m-danger"></i> {% trans 'Unhealthy' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td role="cell">
|
||||
{% for state in states %}
|
||||
<div>
|
||||
{% if not state.version %}
|
||||
<i class="fas fa-question-circle"></i>
|
||||
{% elif state.version_outdated %}
|
||||
<i class="fas fa-times pf-m-danger"></i> {% blocktrans with is=state.version should=state.version_should %}{{ is }}, should be {{ should }}{% endblocktrans %}
|
||||
{% else %}
|
||||
<i class="fas fa-check pf-m-success"></i> {{ state.version }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td role="cell">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-update' pk=outpost.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-delete' pk=outpost.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% get_htmls outpost as htmls %}
|
||||
{% for html in htmls %}
|
||||
{{ html|safe }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-map-marker pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Outposts.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any outposts." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no outposts exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,154 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load authentik_utils %}
|
||||
{% load admin_reflection %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon-integration"></i>
|
||||
{% trans 'Outpost Service-Connections' %}
|
||||
</h1>
|
||||
<p>{% trans "Outpost Service-Connections define how authentik connects to external platforms to manage and deploy Outposts." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Local?' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Status' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for sc in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<span>{{ sc.name }}</span>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ sc|verbose_name }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ sc.local|yesno:"Yes,No" }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{% if sc.state.healthy %}
|
||||
<i class="fas fa-check pf-m-success"></i> {{ sc.state.version }}
|
||||
{% else %}
|
||||
<i class="fas fa-times pf-m-danger"></i> {% trans 'Unhealthy' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-update' pk=sc.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-delete' pk=sc.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-map-marker pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Outpost Service Connections.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any outposts." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no service connections exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:outpost-service-connection-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,148 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-infrastructure"></i>
|
||||
{% trans 'Policies' %}
|
||||
</h1>
|
||||
<p>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for policy in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ policy.name }}</div>
|
||||
{% if not policy.bindings.exists and not policy.promptstage_set.exists %}
|
||||
<i class="pf-icon pf-icon-warning-triangle"></i>
|
||||
<small>{% trans 'Warning: Policy is not assigned.' %}</small>
|
||||
{% else %}
|
||||
<i class="pf-icon pf-icon-ok"></i>
|
||||
<small>{% blocktrans with object_count=policy.bindings.all|length %}Assigned to {{ object_count }} objects.{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ policy|verbose_name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-update' pk=policy.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-test' pk=policy.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Test' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-delete' pk=policy.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-infrastructure pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Policies.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any policies." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no policies exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -14,7 +14,7 @@
|
||||
<span class="pf-c-form__label-text">{% trans 'Passing' %}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<span class="pf-c-form__label-text">{{ result.passing|yesno:"Yes,No" }}</span>
|
||||
</div>
|
||||
@ -26,7 +26,7 @@
|
||||
<span class="pf-c-form__label-text">{% trans 'Messages' %}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-form__group-label">
|
||||
<div class="c-form__horizontal-group">
|
||||
<ul>
|
||||
{% for m in result.messages %}
|
||||
|
@ -1,119 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-infrastructure"></i>
|
||||
{% trans 'Policy Bindings' %}
|
||||
</h1>
|
||||
<p>{% trans "Bind existing Policies to Models accepting policies." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-binding-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Policy' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Enabled' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Timeout' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for pbm in object_list %}
|
||||
<tr role="role">
|
||||
<td>
|
||||
{{ pbm }}
|
||||
<small>
|
||||
{{ pbm|fieldtype }}
|
||||
</small>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% for binding in pbm.bindings %}
|
||||
<tr class="row pf-c-table__expandable-row pf-m-expanded">
|
||||
<th role="cell">
|
||||
<div>{{ binding.policy }}</div>
|
||||
<small>
|
||||
{{ binding.policy|fieldtype }}
|
||||
</small>
|
||||
</th>
|
||||
<th role="cell">
|
||||
<div>{{ binding.enabled }}</div>
|
||||
</th>
|
||||
<th role="cell">
|
||||
<div>{{ binding.order }}</div>
|
||||
</th>
|
||||
<th role="cell">
|
||||
<div>{{ binding.timeout }}</div>
|
||||
</th>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-binding-update' pk=binding.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-binding-delete' pk=binding.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Policy Bindings.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% trans 'Currently no policy bindings exist. Click the button below to create one.' %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:policy-binding-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,139 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-blueprint"></i>
|
||||
{% trans 'Property Mappings' %}
|
||||
</h1>
|
||||
<p>{% trans "Control how authentik exposes and interprets information." %}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:property-mapping-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for property_mapping in object_list %}
|
||||
<tr role="row">
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ property_mapping.name }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ property_mapping|verbose_name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:property-mapping-update' pk=property_mapping.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:property-mapping-delete' pk=property_mapping.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-blueprint pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Property Mappings.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any property mappings." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no property mappings exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:property-mapping-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,28 +0,0 @@
|
||||
{% extends 'generic/form.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block above_form %}
|
||||
<h1>{% blocktrans with property_mapping=property_mapping %}Test {{ property_mapping }}{% endblocktrans %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block beneath_form %}
|
||||
{% if result %}
|
||||
<div class="pf-c-form__group ">
|
||||
<div class="pf-c-form__group-label">
|
||||
<label class="pf-c-form__label" for="context-1">
|
||||
<span class="pf-c-form__label-text">{% trans 'Result' %}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="c-form__horizontal-group">
|
||||
<ak-codemirror mode="javascript"><textarea class="pf-c-form-control">{{ result }}</textarea></ak-codemirror>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block action %}
|
||||
{% trans 'Test' %}
|
||||
{% endblock %}
|
@ -1,181 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
{% load admin_reflection %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-integration"></i>
|
||||
{% trans 'Providers' %}
|
||||
</h1>
|
||||
<p>{% trans "Provide support for protocols like SAML and OAuth to assigned applications." %}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:provider-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:provider-saml-from-metadata' %}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{% trans 'SAML Provider from Metadata' %}<br>
|
||||
<small>
|
||||
{% trans "Create a SAML Provider by importing its Metadata." %}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for provider in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ provider.name }}</div>
|
||||
{% if not provider.application %}
|
||||
<i class="pf-icon pf-icon-warning-triangle"></i>
|
||||
<small>{% trans 'Warning: Provider not assigned to any application.' %}</small>
|
||||
{% else %}
|
||||
<i class="pf-icon pf-icon-ok"></i>
|
||||
<small>
|
||||
{% blocktrans with app=provider.application %}
|
||||
Assigned to application {{ app }}.
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ provider|verbose_name }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:provider-update' pk=provider.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:provider-delete' pk=provider.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% get_links provider as links %}
|
||||
{% for name, href in links.items %}
|
||||
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||
{% endfor %}
|
||||
{% get_htmls provider as htmls %}
|
||||
{% for html in htmls %}
|
||||
{{ html|safe }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon-integration pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Providers.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any providers." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no providers exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:provider-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:provider-saml-from-metadata' %}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{% trans 'SAML Provider from Metadata' %}<br>
|
||||
<small>
|
||||
{% trans "Create a SAML Provider by importing its Metadata." %}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,153 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
{% load admin_reflection %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-middleware"></i>
|
||||
{% trans 'Source' %}
|
||||
</h1>
|
||||
<p>{% trans "External Sources which can be used to get Identities into authentik, for example Social Providers like Twiter and GitHub or Enterprise Providers like ADFS and LDAP." %}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:source-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Additional Info' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for source in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<a href="/sources/{{ source.slug }}/">
|
||||
<div>{{ source.name }}</div>
|
||||
{% if not source.enabled %}
|
||||
<small>{% trans 'Disabled' %}</small>
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ source|fieldtype }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ source.ui_additional_info|default:""|safe }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:source-update' pk=source.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:source-delete' pk=source.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% get_links source as links %}
|
||||
{% for name, href in links %}
|
||||
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-middleware pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Sources.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any sources." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no sources exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:source-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,148 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
{% load admin_reflection %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-plugged"></i>
|
||||
{% trans 'Stages' %}
|
||||
</h1>
|
||||
<p>{% trans "Stages are single steps of a Flow that a user is guided through." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Flows' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for stage in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ stage.name }}</div>
|
||||
<small>{{ stage|verbose_name }}</small>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<ul>
|
||||
{% for flow in stage.flow_set.all %}
|
||||
<li>{{ flow.slug }}<</li>
|
||||
{% empty %}
|
||||
<li>-</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-update' pk=stage.stage_uuid %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-delete' pk=stage.stage_uuid %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% get_links stage as links %}
|
||||
{% for name, href in links.items %}
|
||||
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-plugged pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Stages.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any stages." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no stages exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">{% trans 'Create' %}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
{% for type, name in types.items %}
|
||||
<li>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-create' %}?type={{ type }}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
{{ name|verbose_name }}<br>
|
||||
<small>
|
||||
{{ name|doc }}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,125 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-infrastructure"></i>
|
||||
{% trans 'Stage Bindings' %}
|
||||
</h1>
|
||||
<p>{% trans "Bind existing Stages to Flows." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-binding-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Stage Type' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% regroup object_list by target as grouped_bindings %}
|
||||
{% for flow in grouped_bindings %}
|
||||
<tr role="role">
|
||||
<td>
|
||||
{% blocktrans with slug=flow.grouper.slug %}
|
||||
Flow {{ slug }}
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% for binding in flow.list %}
|
||||
<tr class="pf-c-table__expandable-row pf-m-expanded" role="row">
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ binding.order }}
|
||||
</span>
|
||||
</td>
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ binding.target.slug }}</div>
|
||||
<small>
|
||||
{{ binding.target.name }}
|
||||
</small>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<div>
|
||||
<div>
|
||||
{{ binding.stage.name }}
|
||||
</div>
|
||||
<small>
|
||||
{{ binding.stage }}
|
||||
</small>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-binding-update' pk=binding.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Update' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-binding-delete' pk=binding.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Flow-Stage Bindings.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% trans 'Currently no flow-stage bindings exist. Click the button below to create one.' %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-binding-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,109 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-migration"></i>
|
||||
{% trans 'Invitations' %}
|
||||
</h1>
|
||||
<p>{% trans "Create Invitation Links to enroll Users, and optionally force specific attributes of their account." %}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-invitation-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'ID' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Created by' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Expiry' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for invitation in object_list %}
|
||||
<tr role="row">
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ invitation.invite_uuid }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ invitation.created_by }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ invitation.expiry|default:"-" }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-invitation-delete' pk=invitation.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-migration pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Invitations.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any invitations." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no invitations exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-invitation-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,130 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
{% load admin_reflection %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-plugged"></i>
|
||||
{% trans 'Prompts' %}
|
||||
</h1>
|
||||
<p>{% trans "Single Prompts that can be used for Prompt Stages." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-prompt-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Field' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Label' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Type' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Order' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Flows' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for prompt in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ prompt.field_key }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<div>
|
||||
{{ prompt.label }}
|
||||
</div>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<div>
|
||||
{{ prompt.type }}
|
||||
</div>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<div>
|
||||
{{ prompt.order }}
|
||||
</div>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<ul>
|
||||
{% for flow in prompt.flow_set.all %}
|
||||
<li>{{ flow.slug }}</li>
|
||||
{% empty %}
|
||||
<li>-</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-prompt-update' pk=prompt.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Update' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_admin:stage-prompt-delete' pk=prompt.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% get_links prompt as links %}
|
||||
{% for name, href in links.items %}
|
||||
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-plugged pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Stage Prompts.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any stage prompts." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no stage prompts exist. Click the button below to create one.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<a href="{% url 'authentik_admin:stage-prompt-create' %}?back={{ request.get_full_path }}" class="pf-c-button pf-m-primary" type="button">{% trans 'Create' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,84 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-automation"></i>
|
||||
{% trans 'System Tasks' %}
|
||||
</h1>
|
||||
<p>{% trans "Long-running operations which authentik executes in the background." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Identifier' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Description' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Last Run' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Status' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Messages' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for task in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<span>{{ task.html_name|join:"_­" }}</span>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ task.task_description }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ task.finish_timestamp|naturaltime }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{% if task.result.status == task_successful %}
|
||||
<i class="fas fa-check pf-m-success"></i> {% trans 'Successful' %}
|
||||
{% elif task.result.status == task_warning %}
|
||||
<i class="fas fa-exclamation-triangle pf-m-warning"></i> {% trans 'Warning' %}
|
||||
{% elif task.result.status == task_error %}
|
||||
<i class="fas fa-times pf-m-danger"></i> {% trans 'Error' %}
|
||||
{% else %}
|
||||
<i class="fas fa-question-circle"></i> {% trans 'Unknown' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{% for message in task.result.messages %}
|
||||
<div>
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
<ak-action-button url="{% url 'authentik_api:admin_system_tasks-retry' pk=task.task_name %}">
|
||||
{% trans 'Retry Task' %}
|
||||
</ak-action-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,102 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-security"></i>
|
||||
{% trans 'Tokens' %}
|
||||
</h1>
|
||||
<p>{% trans "Tokens are used throughout authentik for Email validation stages, Recovery keys and API access." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Identifier' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'User' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Expires?' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Expiry Date' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for token in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>{{ token.identifier }}</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ token.user }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ token.expiring|yesno:"Yes,No" }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{% if not token.expiring %}
|
||||
-
|
||||
{% else %}
|
||||
{{ token.expires }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:token-delete' pk=token.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-token-copy-button identifier="{{ token.identifier }}">
|
||||
{% trans 'Copy token' %}
|
||||
</ak-token-copy-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-key pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Tokens.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any token." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no tokens exist.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,125 +0,0 @@
|
||||
{% extends "administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-user"></i>
|
||||
{% trans 'Users' %}
|
||||
</h1>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-c-card">
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_admin:user-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<button role="ak-refresh" class="pf-c-button pf-m-primary">
|
||||
{% trans 'Refresh' %}
|
||||
</button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Name' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Active' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Last Login' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for user in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>
|
||||
<div>{{ user.username }}</div>
|
||||
<small>{{ user.name }}</small>
|
||||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ user.is_active }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ user.last_login }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_admin:user-update' pk=user.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% if user.is_active %}
|
||||
<ak-modal-button href="{% url 'authentik_admin:user-disable' pk=user.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-warning">
|
||||
{% trans 'Disable' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% else %}
|
||||
<ak-modal-button href="{% url 'authentik_admin:user-delete' pk=user.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Enable' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
{% endif %}
|
||||
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{% url 'authentik_admin:user-password-reset' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Reset Password' %}</a>
|
||||
<a class="pf-c-button pf-m-tertiary ak-root-link" href="{% url 'authentik_core:impersonate-init' user_id=user.pk %}">{% trans 'Impersonate' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="pf-icon pf-icon-user pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Users.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% if request.GET.search != "" %}
|
||||
{% trans "Your search query doesn't match any users." %}
|
||||
{% else %}
|
||||
{% trans 'Currently no users exist. How did you even get here.' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_admin:user-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -27,7 +27,9 @@
|
||||
</div>
|
||||
</section>
|
||||
<footer class="pf-c-modal-box__footer">
|
||||
<input class="pf-c-button pf-m-primary" type="submit" form="main-form" value="{% block action %}{% endblock %}" />
|
||||
<ak-spinner-button form="main-form">
|
||||
{% block action %}{% endblock %}
|
||||
</ak-spinner-button>
|
||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Cancel" %}</a>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
@ -1,62 +0,0 @@
|
||||
"""authentik admin templatetags"""
|
||||
from django import template
|
||||
from django.db.models import Model
|
||||
from django.utils.html import mark_safe
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
register = template.Library()
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def get_links(model_instance):
|
||||
"""Find all link_ methods on an object instance, run them and return as dict"""
|
||||
prefix = "link_"
|
||||
links = {}
|
||||
|
||||
if not isinstance(model_instance, Model):
|
||||
LOGGER.warning("Model is not instance of Model", model_instance=model_instance)
|
||||
return links
|
||||
|
||||
try:
|
||||
for name in dir(model_instance):
|
||||
if not name.startswith(prefix):
|
||||
continue
|
||||
value = getattr(model_instance, name)
|
||||
if not callable(value):
|
||||
continue
|
||||
human_name = name.replace(prefix, "").replace("_", " ").capitalize()
|
||||
link = value()
|
||||
if link:
|
||||
links[human_name] = link
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
return links
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def get_htmls(context, model_instance):
|
||||
"""Find all html_ methods on an object instance, run them and return as dict"""
|
||||
prefix = "html_"
|
||||
htmls = []
|
||||
|
||||
if not isinstance(model_instance, Model):
|
||||
LOGGER.warning("Model is not instance of Model", model_instance=model_instance)
|
||||
return htmls
|
||||
|
||||
try:
|
||||
for name in dir(model_instance):
|
||||
if not name.startswith(prefix):
|
||||
continue
|
||||
value = getattr(model_instance, name)
|
||||
if not callable(value):
|
||||
continue
|
||||
if name.startswith(prefix):
|
||||
html = value(context.get("request"))
|
||||
if html:
|
||||
htmls.append(mark_safe(html))
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
return htmls
|
@ -1,8 +1,8 @@
|
||||
"""test admin api"""
|
||||
from json import loads
|
||||
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from authentik import __version__
|
||||
from authentik.core.models import Group, User
|
||||
|
@ -3,8 +3,8 @@ from importlib import import_module
|
||||
from typing import Callable
|
||||
|
||||
from django.forms import ModelForm
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.urls.exceptions import NoReverseMatch
|
||||
|
||||
from authentik.admin.urls import urlpatterns
|
||||
|
@ -20,11 +20,10 @@ from authentik.admin.views import (
|
||||
stages_bindings,
|
||||
stages_invitations,
|
||||
stages_prompts,
|
||||
tasks,
|
||||
tokens,
|
||||
users,
|
||||
)
|
||||
from authentik.providers.saml.views import MetadataImportView
|
||||
from authentik.providers.saml.views.metadata import MetadataImportView
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
@ -54,14 +53,12 @@ urlpatterns = [
|
||||
name="application-delete",
|
||||
),
|
||||
# Tokens
|
||||
path("tokens/", tokens.TokenListView.as_view(), name="tokens"),
|
||||
path(
|
||||
"tokens/<uuid:pk>/delete/",
|
||||
tokens.TokenDeleteView.as_view(),
|
||||
name="token-delete",
|
||||
),
|
||||
# Sources
|
||||
path("sources/", sources.SourceListView.as_view(), name="sources"),
|
||||
path("sources/create/", sources.SourceCreateView.as_view(), name="source-create"),
|
||||
path(
|
||||
"sources/<uuid:pk>/update/",
|
||||
@ -74,7 +71,6 @@ urlpatterns = [
|
||||
name="source-delete",
|
||||
),
|
||||
# Policies
|
||||
path("policies/", policies.PolicyListView.as_view(), name="policies"),
|
||||
path("policies/create/", policies.PolicyCreateView.as_view(), name="policy-create"),
|
||||
path(
|
||||
"policies/<uuid:pk>/update/",
|
||||
@ -92,11 +88,6 @@ urlpatterns = [
|
||||
name="policy-test",
|
||||
),
|
||||
# Policy bindings
|
||||
path(
|
||||
"policies/bindings/",
|
||||
policies_bindings.PolicyBindingListView.as_view(),
|
||||
name="policies-bindings",
|
||||
),
|
||||
path(
|
||||
"policies/bindings/create/",
|
||||
policies_bindings.PolicyBindingCreateView.as_view(),
|
||||
@ -113,7 +104,6 @@ urlpatterns = [
|
||||
name="policy-binding-delete",
|
||||
),
|
||||
# Providers
|
||||
path("providers/", providers.ProviderListView.as_view(), name="providers"),
|
||||
path(
|
||||
"providers/create/",
|
||||
providers.ProviderCreateView.as_view(),
|
||||
@ -135,7 +125,6 @@ urlpatterns = [
|
||||
name="provider-delete",
|
||||
),
|
||||
# Stages
|
||||
path("stages/", stages.StageListView.as_view(), name="stages"),
|
||||
path("stages/create/", stages.StageCreateView.as_view(), name="stage-create"),
|
||||
path(
|
||||
"stages/<uuid:pk>/update/",
|
||||
@ -148,11 +137,6 @@ urlpatterns = [
|
||||
name="stage-delete",
|
||||
),
|
||||
# Stage bindings
|
||||
path(
|
||||
"stages/bindings/",
|
||||
stages_bindings.StageBindingListView.as_view(),
|
||||
name="stage-bindings",
|
||||
),
|
||||
path(
|
||||
"stages/bindings/create/",
|
||||
stages_bindings.StageBindingCreateView.as_view(),
|
||||
@ -170,31 +154,21 @@ urlpatterns = [
|
||||
),
|
||||
# Stage Prompts
|
||||
path(
|
||||
"stages/prompts/",
|
||||
stages_prompts.PromptListView.as_view(),
|
||||
name="stage-prompts",
|
||||
),
|
||||
path(
|
||||
"stages/prompts/create/",
|
||||
"stages_prompts/create/",
|
||||
stages_prompts.PromptCreateView.as_view(),
|
||||
name="stage-prompt-create",
|
||||
),
|
||||
path(
|
||||
"stages/prompts/<uuid:pk>/update/",
|
||||
"stages_prompts/<uuid:pk>/update/",
|
||||
stages_prompts.PromptUpdateView.as_view(),
|
||||
name="stage-prompt-update",
|
||||
),
|
||||
path(
|
||||
"stages/prompts/<uuid:pk>/delete/",
|
||||
"stages_prompts/<uuid:pk>/delete/",
|
||||
stages_prompts.PromptDeleteView.as_view(),
|
||||
name="stage-prompt-delete",
|
||||
),
|
||||
# Stage Invitations
|
||||
path(
|
||||
"stages/invitations/",
|
||||
stages_invitations.InvitationListView.as_view(),
|
||||
name="stage-invitations",
|
||||
),
|
||||
path(
|
||||
"stages/invitations/create/",
|
||||
stages_invitations.InvitationCreateView.as_view(),
|
||||
@ -206,7 +180,6 @@ urlpatterns = [
|
||||
name="stage-invitation-delete",
|
||||
),
|
||||
# Flows
|
||||
path("flows/", flows.FlowListView.as_view(), name="flows"),
|
||||
path(
|
||||
"flows/create/",
|
||||
flows.FlowCreateView.as_view(),
|
||||
@ -259,7 +232,6 @@ urlpatterns = [
|
||||
name="property-mapping-test",
|
||||
),
|
||||
# Users
|
||||
path("users/", users.UserListView.as_view(), name="users"),
|
||||
path("users/create/", users.UserCreateView.as_view(), name="user-create"),
|
||||
path("users/<int:pk>/update/", users.UserUpdateView.as_view(), name="user-update"),
|
||||
path("users/<int:pk>/delete/", users.UserDeleteView.as_view(), name="user-delete"),
|
||||
@ -273,7 +245,6 @@ urlpatterns = [
|
||||
name="user-password-reset",
|
||||
),
|
||||
# Groups
|
||||
path("groups/", groups.GroupListView.as_view(), name="groups"),
|
||||
path("groups/create/", groups.GroupCreateView.as_view(), name="group-create"),
|
||||
path(
|
||||
"groups/<uuid:pk>/update/",
|
||||
@ -286,16 +257,16 @@ urlpatterns = [
|
||||
name="group-delete",
|
||||
),
|
||||
# Certificate-Key Pairs
|
||||
path(
|
||||
"crypto/certificates/",
|
||||
certificate_key_pair.CertificateKeyPairListView.as_view(),
|
||||
name="certificate_key_pair",
|
||||
),
|
||||
path(
|
||||
"crypto/certificates/create/",
|
||||
certificate_key_pair.CertificateKeyPairCreateView.as_view(),
|
||||
name="certificatekeypair-create",
|
||||
),
|
||||
path(
|
||||
"crypto/certificates/generate/",
|
||||
certificate_key_pair.CertificateKeyPairGenerateView.as_view(),
|
||||
name="certificatekeypair-generate",
|
||||
),
|
||||
path(
|
||||
"crypto/certificates/<uuid:pk>/update/",
|
||||
certificate_key_pair.CertificateKeyPairUpdateView.as_view(),
|
||||
@ -307,11 +278,6 @@ urlpatterns = [
|
||||
name="certificatekeypair-delete",
|
||||
),
|
||||
# Outposts
|
||||
path(
|
||||
"outposts/",
|
||||
outposts.OutpostListView.as_view(),
|
||||
name="outposts",
|
||||
),
|
||||
path(
|
||||
"outposts/create/",
|
||||
outposts.OutpostCreateView.as_view(),
|
||||
@ -329,31 +295,20 @@ urlpatterns = [
|
||||
),
|
||||
# Outpost Service Connections
|
||||
path(
|
||||
"outposts/service_connections/",
|
||||
outposts_service_connections.OutpostServiceConnectionListView.as_view(),
|
||||
name="outpost-service-connections",
|
||||
),
|
||||
path(
|
||||
"outposts/service_connections/create/",
|
||||
"outpost_service_connections/create/",
|
||||
outposts_service_connections.OutpostServiceConnectionCreateView.as_view(),
|
||||
name="outpost-service-connection-create",
|
||||
),
|
||||
path(
|
||||
"outposts/service_connections/<uuid:pk>/update/",
|
||||
"outpost_service_connections/<uuid:pk>/update/",
|
||||
outposts_service_connections.OutpostServiceConnectionUpdateView.as_view(),
|
||||
name="outpost-service-connection-update",
|
||||
),
|
||||
path(
|
||||
"outposts/service_connections/<uuid:pk>/delete/",
|
||||
"outpost_service_connections/<uuid:pk>/delete/",
|
||||
outposts_service_connections.OutpostServiceConnectionDeleteView.as_view(),
|
||||
name="outpost-service-connection-delete",
|
||||
),
|
||||
# Tasks
|
||||
path(
|
||||
"tasks/",
|
||||
tasks.TaskListView.as_view(),
|
||||
name="tasks",
|
||||
),
|
||||
# Event Notification Transpots
|
||||
path(
|
||||
"events/transports/create/",
|
||||
|
@ -1,15 +1,17 @@
|
||||
"""authentik Application administration"""
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
|
||||
from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.core.forms.applications import ApplicationForm
|
||||
from authentik.core.models import Application
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
@ -17,7 +19,6 @@ from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
class ApplicationCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -28,14 +29,29 @@ class ApplicationCreateView(
|
||||
form_class = ApplicationForm
|
||||
permission_required = "authentik_core.add_application"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Application")
|
||||
|
||||
def get_initial(self) -> dict[str, Any]:
|
||||
if "provider" in self.request.GET:
|
||||
try:
|
||||
initial_provider_pk = int(self.request.GET["provider"])
|
||||
except ValueError:
|
||||
return super().get_initial()
|
||||
providers = (
|
||||
get_objects_for_user(self.request.user, "authentik_core.view_provider")
|
||||
.filter(pk=initial_provider_pk)
|
||||
.select_subclasses()
|
||||
)
|
||||
if not providers.exists():
|
||||
return {}
|
||||
return {"provider": providers.first()}
|
||||
return super().get_initial()
|
||||
|
||||
|
||||
class ApplicationUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -46,8 +62,8 @@ class ApplicationUpdateView(
|
||||
form_class = ApplicationForm
|
||||
permission_required = "authentik_core.change_application"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Application")
|
||||
|
||||
|
||||
@ -59,6 +75,6 @@ class ApplicationDeleteView(
|
||||
model = Application
|
||||
permission_required = "authentik_core.delete_application"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Application")
|
||||
|
@ -4,42 +4,25 @@ from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http.response import HttpResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.crypto.builder import CertificateBuilder
|
||||
from authentik.crypto.forms import (
|
||||
CertificateKeyPairForm,
|
||||
CertificateKeyPairGenerateForm,
|
||||
)
|
||||
from authentik.crypto.forms import CertificateKeyPairForm
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
|
||||
class CertificateKeyPairListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all keypairs"""
|
||||
|
||||
model = CertificateKeyPair
|
||||
permission_required = "authentik_crypto.view_certificatekeypair"
|
||||
ordering = "name"
|
||||
template_name = "administration/certificatekeypair/list.html"
|
||||
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
class CertificateKeyPairCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -51,13 +34,39 @@ class CertificateKeyPairCreateView(
|
||||
permission_required = "authentik_crypto.add_certificatekeypair"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:certificate_key_pair")
|
||||
success_message = _("Successfully created CertificateKeyPair")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Certificate-Key Pair")
|
||||
|
||||
|
||||
class CertificateKeyPairGenerateView(
|
||||
SuccessMessageMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
FormView,
|
||||
):
|
||||
"""Generate new CertificateKeyPair"""
|
||||
|
||||
model = CertificateKeyPair
|
||||
form_class = CertificateKeyPairGenerateForm
|
||||
permission_required = "authentik_crypto.add_certificatekeypair"
|
||||
|
||||
template_name = "administration/certificatekeypair/generate.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully generated Certificate-Key Pair")
|
||||
|
||||
def form_valid(self, form: CertificateKeyPairGenerateForm) -> HttpResponse:
|
||||
builder = CertificateBuilder()
|
||||
builder.common_name = form.data["common_name"]
|
||||
builder.build(
|
||||
subject_alt_names=form.data.get("subject_alt_name", "").split(","),
|
||||
validity_days=int(form.data["validity_days"]),
|
||||
)
|
||||
builder.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class CertificateKeyPairUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -69,7 +78,7 @@ class CertificateKeyPairUpdateView(
|
||||
permission_required = "authentik_crypto.change_certificatekeypair"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:certificate_key_pair")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Certificate-Key Pair")
|
||||
|
||||
|
||||
@ -82,5 +91,5 @@ class CertificateKeyPairDeleteView(
|
||||
permission_required = "authentik_crypto.delete_certificatekeypair"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:certificate_key_pair")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Certificate-Key Pair")
|
||||
|
@ -4,12 +4,11 @@ from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.events.forms import NotificationRuleForm
|
||||
from authentik.events.models import NotificationRule
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
@ -17,7 +16,6 @@ from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
class NotificationRuleCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -28,14 +26,13 @@ class NotificationRuleCreateView(
|
||||
form_class = NotificationRuleForm
|
||||
permission_required = "authentik_events.add_NotificationRule"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Notification Rule")
|
||||
|
||||
|
||||
class NotificationRuleUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -46,8 +43,8 @@ class NotificationRuleUpdateView(
|
||||
form_class = NotificationRuleForm
|
||||
permission_required = "authentik_events.change_NotificationRule"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Notification Rule")
|
||||
|
||||
|
||||
@ -59,6 +56,6 @@ class NotificationRuleDeleteView(
|
||||
model = NotificationRule
|
||||
permission_required = "authentik_events.delete_NotificationRule"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Notification Rule")
|
||||
|
@ -4,12 +4,11 @@ from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import BackSuccessUrlMixin, DeleteMessageView
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.events.forms import NotificationTransportForm
|
||||
from authentik.events.models import NotificationTransport
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
@ -17,7 +16,6 @@ from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
class NotificationTransportCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -27,15 +25,13 @@ class NotificationTransportCreateView(
|
||||
model = NotificationTransport
|
||||
form_class = NotificationTransportForm
|
||||
permission_required = "authentik_events.add_notificationtransport"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Notification Transport")
|
||||
|
||||
|
||||
class NotificationTransportUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -45,9 +41,8 @@ class NotificationTransportUpdateView(
|
||||
model = NotificationTransport
|
||||
form_class = NotificationTransportForm
|
||||
permission_required = "authentik_events.change_notificationtransport"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Notification Transport")
|
||||
|
||||
|
||||
@ -58,7 +53,6 @@ class NotificationTransportDeleteView(
|
||||
|
||||
model = NotificationTransport
|
||||
permission_required = "authentik_events.delete_notificationtransport"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Notification Transport")
|
||||
|
@ -8,15 +8,10 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import DetailView, FormView, ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from django.views.generic import DetailView, FormView, UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.flows.exceptions import FlowNonApplicableException
|
||||
from authentik.flows.forms import FlowForm, FlowImportForm
|
||||
from authentik.flows.models import Flow
|
||||
@ -29,25 +24,8 @@ from authentik.lib.utils.urls import redirect_with_qs
|
||||
from authentik.lib.views import CreateAssignPermView, bad_request_message
|
||||
|
||||
|
||||
class FlowListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all flows"""
|
||||
|
||||
model = Flow
|
||||
permission_required = "authentik_flows.view_flow"
|
||||
ordering = "name"
|
||||
template_name = "administration/flow/list.html"
|
||||
search_fields = ["name", "slug", "designation", "title"]
|
||||
|
||||
|
||||
class FlowCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -59,13 +37,12 @@ class FlowCreateView(
|
||||
permission_required = "authentik_flows.add_flow"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:flows")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Flow")
|
||||
|
||||
|
||||
class FlowUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -77,7 +54,7 @@ class FlowUpdateView(
|
||||
permission_required = "authentik_flows.change_flow"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:flows")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Flow")
|
||||
|
||||
|
||||
@ -88,7 +65,7 @@ class FlowDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageV
|
||||
permission_required = "authentik_flows.delete_flow"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:flows")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Flow")
|
||||
|
||||
|
||||
@ -128,7 +105,7 @@ class FlowImportView(LoginRequiredMixin, FormView):
|
||||
|
||||
form_class = FlowImportForm
|
||||
template_name = "administration/flow/import.html"
|
||||
success_url = reverse_lazy("authentik_admin:flows")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_superuser:
|
||||
|
@ -6,39 +6,17 @@ from django.contrib.auth.mixins import (
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.core.forms.groups import GroupForm
|
||||
from authentik.core.models import Group
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
|
||||
class GroupListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all groups"""
|
||||
|
||||
model = Group
|
||||
permission_required = "authentik_core.view_group"
|
||||
ordering = "name"
|
||||
template_name = "administration/group/list.html"
|
||||
search_fields = ["name", "attributes"]
|
||||
|
||||
|
||||
class GroupCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -50,13 +28,12 @@ class GroupCreateView(
|
||||
permission_required = "authentik_core.add_group"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:groups")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Group")
|
||||
|
||||
|
||||
class GroupUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -68,7 +45,7 @@ class GroupUpdateView(
|
||||
permission_required = "authentik_core.change_group"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:groups")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Group")
|
||||
|
||||
|
||||
@ -79,5 +56,5 @@ class GroupDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage
|
||||
permission_required = "authentik_flows.delete_group"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:groups")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Group")
|
||||
|
@ -1,47 +1,24 @@
|
||||
"""authentik Outpost administration"""
|
||||
from dataclasses import asdict
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
from authentik.outposts.forms import OutpostForm
|
||||
from authentik.outposts.models import Outpost, OutpostConfig
|
||||
|
||||
|
||||
class OutpostListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all outposts"""
|
||||
|
||||
model = Outpost
|
||||
permission_required = "authentik_outposts.view_outpost"
|
||||
ordering = "name"
|
||||
template_name = "administration/outpost/list.html"
|
||||
search_fields = ["name", "_config"]
|
||||
|
||||
|
||||
class OutpostCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -51,12 +28,11 @@ class OutpostCreateView(
|
||||
model = Outpost
|
||||
form_class = OutpostForm
|
||||
permission_required = "authentik_outposts.add_outpost"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:outposts")
|
||||
success_message = _("Successfully created Outpost")
|
||||
|
||||
def get_initial(self) -> Dict[str, Any]:
|
||||
def get_initial(self) -> dict[str, Any]:
|
||||
return {
|
||||
"_config": asdict(
|
||||
OutpostConfig(authentik_host=self.request.build_absolute_uri("/"))
|
||||
@ -66,7 +42,6 @@ class OutpostCreateView(
|
||||
|
||||
class OutpostUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -76,9 +51,8 @@ class OutpostUpdateView(
|
||||
model = Outpost
|
||||
form_class = OutpostForm
|
||||
permission_required = "authentik_outposts.change_outpost"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:outposts")
|
||||
success_message = _("Successfully updated Outpost")
|
||||
|
||||
|
||||
@ -87,7 +61,6 @@ class OutpostDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessa
|
||||
|
||||
model = Outpost
|
||||
permission_required = "authentik_outposts.delete_outpost"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:outposts")
|
||||
success_message = _("Successfully deleted Outpost")
|
||||
|
@ -6,39 +6,18 @@ from django.contrib.auth.mixins import (
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
InheritanceCreateView,
|
||||
InheritanceListView,
|
||||
InheritanceUpdateView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.outposts.models import OutpostServiceConnection
|
||||
|
||||
|
||||
class OutpostServiceConnectionListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
InheritanceListView,
|
||||
):
|
||||
"""Show list of all outpost-service-connections"""
|
||||
|
||||
model = OutpostServiceConnection
|
||||
permission_required = "authentik_outposts.add_outpostserviceconnection"
|
||||
template_name = "administration/outpost_service_connection/list.html"
|
||||
ordering = "pk"
|
||||
search_fields = ["pk", "name"]
|
||||
|
||||
|
||||
class OutpostServiceConnectionCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
InheritanceCreateView,
|
||||
@ -49,13 +28,12 @@ class OutpostServiceConnectionCreateView(
|
||||
permission_required = "authentik_outposts.add_outpostserviceconnection"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:outpost-service-connections")
|
||||
success_message = _("Successfully created OutpostServiceConnection")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Outpost Service Connection")
|
||||
|
||||
|
||||
class OutpostServiceConnectionUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
InheritanceUpdateView,
|
||||
@ -66,8 +44,8 @@ class OutpostServiceConnectionUpdateView(
|
||||
permission_required = "authentik_outposts.change_outpostserviceconnection"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:outpost-service-connections")
|
||||
success_message = _("Successfully updated OutpostServiceConnection")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Outpost Service Connection")
|
||||
|
||||
|
||||
class OutpostServiceConnectionDeleteView(
|
||||
@ -79,5 +57,5 @@ class OutpostServiceConnectionDeleteView(
|
||||
permission_required = "authentik_outposts.delete_outpostserviceconnection"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:outpost-service-connections")
|
||||
success_message = _("Successfully deleted OutpostServiceConnection")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Outpost Service Connection")
|
||||
|
@ -18,9 +18,8 @@ class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
|
||||
"""View to clear Policy cache"""
|
||||
|
||||
form_class = PolicyCacheClearForm
|
||||
|
||||
template_name = "generic/form_non_model.html"
|
||||
success_url = "/"
|
||||
template_name = "generic/form_non_model.html"
|
||||
success_message = _("Successfully cleared Policy cache")
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
@ -37,9 +36,8 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
|
||||
"""View to clear Flow cache"""
|
||||
|
||||
form_class = FlowCacheClearForm
|
||||
|
||||
template_name = "generic/form_non_model.html"
|
||||
success_url = "/"
|
||||
template_name = "generic/form_non_model.html"
|
||||
success_message = _("Successfully cleared Flow cache")
|
||||
|
||||
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""authentik Policy administration"""
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
@ -11,41 +11,20 @@ from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import FormView
|
||||
from django.views.generic.detail import DetailView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.forms.policies import PolicyTestForm
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
InheritanceCreateView,
|
||||
InheritanceListView,
|
||||
InheritanceUpdateView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.policies.models import Policy, PolicyBinding
|
||||
from authentik.policies.process import PolicyProcess, PolicyRequest
|
||||
|
||||
|
||||
class PolicyListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
InheritanceListView,
|
||||
):
|
||||
"""Show list of all policies"""
|
||||
|
||||
model = Policy
|
||||
permission_required = "authentik_policies.view_policy"
|
||||
ordering = "name"
|
||||
template_name = "administration/policy/list.html"
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
class PolicyCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
InheritanceCreateView,
|
||||
@ -56,13 +35,12 @@ class PolicyCreateView(
|
||||
permission_required = "authentik_policies.add_policy"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:policies")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Policy")
|
||||
|
||||
|
||||
class PolicyUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
InheritanceUpdateView,
|
||||
@ -73,7 +51,7 @@ class PolicyUpdateView(
|
||||
permission_required = "authentik_policies.change_policy"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:policies")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Policy")
|
||||
|
||||
|
||||
@ -84,7 +62,7 @@ class PolicyDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag
|
||||
permission_required = "authentik_policies.delete_policy"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:policies")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Policy")
|
||||
|
||||
|
||||
@ -102,7 +80,7 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
|
||||
Policy.objects.filter(pk=self.kwargs.get("pk")).select_subclasses().first()
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
kwargs["policy"] = self.get_object()
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
@ -115,7 +93,8 @@ class PolicyTestView(LoginRequiredMixin, DetailView, PermissionRequiredMixin, Fo
|
||||
user = form.cleaned_data.get("user")
|
||||
|
||||
p_request = PolicyRequest(user)
|
||||
p_request.http_request = self.request
|
||||
p_request.debug = True
|
||||
p_request.set_http_request(self.request)
|
||||
p_request.context = form.cleaned_data.get("context", {})
|
||||
|
||||
proc = PolicyProcess(PolicyBinding(policy=policy), p_request, None)
|
||||
|
@ -6,55 +6,20 @@ from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.db.models import Max, QuerySet
|
||||
from django.db.models import Max
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
from authentik.policies.forms import PolicyBindingForm
|
||||
from authentik.policies.models import PolicyBinding, PolicyBindingModel
|
||||
|
||||
|
||||
class PolicyBindingListView(
|
||||
LoginRequiredMixin, PermissionListMixin, UserPaginateListMixin, ListView
|
||||
):
|
||||
"""Show list of all policies"""
|
||||
|
||||
model = PolicyBinding
|
||||
permission_required = "authentik_policies.view_policybinding"
|
||||
ordering = ["order", "target"]
|
||||
template_name = "administration/policy_binding/list.html"
|
||||
|
||||
def get_queryset(self) -> QuerySet:
|
||||
# Since `select_subclasses` does not work with a foreign key, we have to do two queries here
|
||||
# First, get all pbm objects that have bindings attached
|
||||
objects = (
|
||||
get_objects_for_user(
|
||||
self.request.user, "authentik_policies.view_policybindingmodel"
|
||||
)
|
||||
.filter(policies__isnull=False)
|
||||
.select_subclasses()
|
||||
.select_related()
|
||||
.order_by("pk")
|
||||
)
|
||||
for pbm in objects:
|
||||
pbm.bindings = get_objects_for_user(
|
||||
self.request.user, self.permission_required
|
||||
).filter(target__pk=pbm.pbm_uuid)
|
||||
return objects
|
||||
|
||||
|
||||
class PolicyBindingCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -66,7 +31,7 @@ class PolicyBindingCreateView(
|
||||
form_class = PolicyBindingForm
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:policies-bindings")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created PolicyBinding")
|
||||
|
||||
def get_initial(self) -> dict[str, Any]:
|
||||
@ -88,7 +53,6 @@ class PolicyBindingCreateView(
|
||||
|
||||
class PolicyBindingUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -100,7 +64,7 @@ class PolicyBindingUpdateView(
|
||||
form_class = PolicyBindingForm
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:policies-bindings")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated PolicyBinding")
|
||||
|
||||
|
||||
@ -113,5 +77,5 @@ class PolicyBindingDeleteView(
|
||||
permission_required = "authentik_policies.delete_policybinding"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:policies-bindings")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted PolicyBinding")
|
||||
|
@ -8,7 +8,6 @@ from django.contrib.auth.mixins import (
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import FormView
|
||||
from django.views.generic.detail import DetailView
|
||||
@ -16,7 +15,6 @@ from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.forms.policies import PolicyTestForm
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
InheritanceCreateView,
|
||||
InheritanceUpdateView,
|
||||
@ -26,7 +24,6 @@ from authentik.core.models import PropertyMapping
|
||||
|
||||
class PropertyMappingCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
InheritanceCreateView,
|
||||
@ -35,15 +32,13 @@ class PropertyMappingCreateView(
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = "authentik_core.add_propertymapping"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:property-mappings")
|
||||
success_message = _("Successfully created Property Mapping")
|
||||
|
||||
|
||||
class PropertyMappingUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
InheritanceUpdateView,
|
||||
@ -52,9 +47,8 @@ class PropertyMappingUpdateView(
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = "authentik_core.change_propertymapping"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:property-mappings")
|
||||
success_message = _("Successfully updated Property Mapping")
|
||||
|
||||
|
||||
@ -65,9 +59,8 @@ class PropertyMappingDeleteView(
|
||||
|
||||
model = PropertyMapping
|
||||
permission_required = "authentik_core.delete_propertymapping"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:property-mappings")
|
||||
success_message = _("Successfully deleted Property Mapping")
|
||||
|
||||
|
||||
|
@ -4,41 +4,19 @@ from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
InheritanceCreateView,
|
||||
InheritanceListView,
|
||||
InheritanceUpdateView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.core.models import Provider
|
||||
|
||||
|
||||
class ProviderListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
InheritanceListView,
|
||||
):
|
||||
"""Show list of all providers"""
|
||||
|
||||
model = Provider
|
||||
permission_required = "authentik_core.add_provider"
|
||||
template_name = "administration/provider/list.html"
|
||||
ordering = "pk"
|
||||
search_fields = ["pk", "name"]
|
||||
|
||||
|
||||
class ProviderCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
InheritanceCreateView,
|
||||
@ -47,15 +25,13 @@ class ProviderCreateView(
|
||||
|
||||
model = Provider
|
||||
permission_required = "authentik_core.add_provider"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:providers")
|
||||
success_message = _("Successfully created Provider")
|
||||
|
||||
|
||||
class ProviderUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
InheritanceUpdateView,
|
||||
@ -64,9 +40,8 @@ class ProviderUpdateView(
|
||||
|
||||
model = Provider
|
||||
permission_required = "authentik_core.change_provider"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:providers")
|
||||
success_message = _("Successfully updated Provider")
|
||||
|
||||
|
||||
@ -77,7 +52,6 @@ class ProviderDeleteView(
|
||||
|
||||
model = Provider
|
||||
permission_required = "authentik_core.delete_provider"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:providers")
|
||||
success_message = _("Successfully deleted Provider")
|
||||
|
@ -4,41 +4,19 @@ from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin as DjangoPermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
InheritanceCreateView,
|
||||
InheritanceListView,
|
||||
InheritanceUpdateView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.core.models import Source
|
||||
|
||||
|
||||
class SourceListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
InheritanceListView,
|
||||
):
|
||||
"""Show list of all sources"""
|
||||
|
||||
model = Source
|
||||
permission_required = "authentik_core.view_source"
|
||||
ordering = "name"
|
||||
template_name = "administration/source/list.html"
|
||||
search_fields = ["name", "slug"]
|
||||
|
||||
|
||||
class SourceCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
InheritanceCreateView,
|
||||
@ -48,14 +26,13 @@ class SourceCreateView(
|
||||
model = Source
|
||||
permission_required = "authentik_core.add_source"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:sources")
|
||||
success_message = _("Successfully created Source")
|
||||
|
||||
|
||||
class SourceUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
InheritanceUpdateView,
|
||||
@ -65,8 +42,8 @@ class SourceUpdateView(
|
||||
model = Source
|
||||
permission_required = "authentik_core.change_source"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:sources")
|
||||
success_message = _("Successfully updated Source")
|
||||
|
||||
|
||||
@ -76,6 +53,6 @@ class SourceDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag
|
||||
model = Source
|
||||
permission_required = "authentik_core.delete_source"
|
||||
|
||||
success_url = "/"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:sources")
|
||||
success_message = _("Successfully deleted Source")
|
||||
|
@ -6,39 +6,18 @@ from django.contrib.auth.mixins import (
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
InheritanceCreateView,
|
||||
InheritanceListView,
|
||||
InheritanceUpdateView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.flows.models import Stage
|
||||
|
||||
|
||||
class StageListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
InheritanceListView,
|
||||
):
|
||||
"""Show list of all stages"""
|
||||
|
||||
model = Stage
|
||||
template_name = "administration/stage/list.html"
|
||||
permission_required = "authentik_flows.view_stage"
|
||||
ordering = "name"
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
class StageCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
InheritanceCreateView,
|
||||
@ -49,13 +28,12 @@ class StageCreateView(
|
||||
template_name = "generic/create.html"
|
||||
permission_required = "authentik_flows.add_stage"
|
||||
|
||||
success_url = reverse_lazy("authentik_admin:stages")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Stage")
|
||||
|
||||
|
||||
class StageUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
InheritanceUpdateView,
|
||||
@ -65,7 +43,7 @@ class StageUpdateView(
|
||||
model = Stage
|
||||
permission_required = "authentik_flows.update_application"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:stages")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Stage")
|
||||
|
||||
|
||||
@ -75,5 +53,5 @@ class StageDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage
|
||||
model = Stage
|
||||
template_name = "generic/delete.html"
|
||||
permission_required = "authentik_flows.delete_stage"
|
||||
success_url = reverse_lazy("authentik_admin:stages")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Stage")
|
||||
|
@ -9,33 +9,17 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.db.models import Max
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.flows.forms import FlowStageBindingForm
|
||||
from authentik.flows.models import Flow, FlowStageBinding
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
|
||||
class StageBindingListView(
|
||||
LoginRequiredMixin, PermissionListMixin, UserPaginateListMixin, ListView
|
||||
):
|
||||
"""Show list of all flows"""
|
||||
|
||||
model = FlowStageBinding
|
||||
permission_required = "authentik_flows.view_flowstagebinding"
|
||||
ordering = ["target", "order"]
|
||||
template_name = "administration/stage_binding/list.html"
|
||||
|
||||
|
||||
class StageBindingCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -47,7 +31,7 @@ class StageBindingCreateView(
|
||||
form_class = FlowStageBindingForm
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-bindings")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created StageBinding")
|
||||
|
||||
def get_initial(self) -> dict[str, Any]:
|
||||
@ -67,7 +51,6 @@ class StageBindingCreateView(
|
||||
|
||||
class StageBindingUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -79,7 +62,7 @@ class StageBindingUpdateView(
|
||||
form_class = FlowStageBindingForm
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-bindings")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated StageBinding")
|
||||
|
||||
|
||||
@ -92,5 +75,5 @@ class StageBindingDeleteView(
|
||||
permission_required = "authentik_flows.delete_flowstagebinding"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-bindings")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted FlowStageBinding")
|
||||
|
@ -7,39 +7,16 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
from authentik.stages.invitation.forms import InvitationForm
|
||||
from authentik.stages.invitation.models import Invitation
|
||||
|
||||
|
||||
class InvitationListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all invitations"""
|
||||
|
||||
model = Invitation
|
||||
permission_required = "authentik_stages_invitation.view_invitation"
|
||||
template_name = "administration/stage_invitation/list.html"
|
||||
ordering = "-expires"
|
||||
search_fields = ["created_by__username", "expires", "fixed_data"]
|
||||
|
||||
|
||||
class InvitationCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -51,7 +28,7 @@ class InvitationCreateView(
|
||||
permission_required = "authentik_stages_invitation.add_invitation"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-invitations")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Invitation")
|
||||
|
||||
def form_valid(self, form):
|
||||
@ -70,5 +47,5 @@ class InvitationDeleteView(
|
||||
permission_required = "authentik_stages_invitation.delete_invitation"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-invitations")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Invitation")
|
||||
|
@ -6,44 +6,17 @@ from django.contrib.auth.mixins import (
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView, UpdateView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from django.views.generic import UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
from authentik.stages.prompt.forms import PromptAdminForm
|
||||
from authentik.stages.prompt.models import Prompt
|
||||
|
||||
|
||||
class PromptListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all prompts"""
|
||||
|
||||
model = Prompt
|
||||
permission_required = "authentik_stages_prompt.view_prompt"
|
||||
ordering = "order"
|
||||
template_name = "administration/stage_prompt/list.html"
|
||||
search_fields = [
|
||||
"field_key",
|
||||
"label",
|
||||
"type",
|
||||
"placeholder",
|
||||
]
|
||||
|
||||
|
||||
class PromptCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -55,13 +28,12 @@ class PromptCreateView(
|
||||
permission_required = "authentik_stages_prompt.add_prompt"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-prompts")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created Prompt")
|
||||
|
||||
|
||||
class PromptUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -73,7 +45,7 @@ class PromptUpdateView(
|
||||
permission_required = "authentik_stages_prompt.change_prompt"
|
||||
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-prompts")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated Prompt")
|
||||
|
||||
|
||||
@ -84,5 +56,5 @@ class PromptDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessag
|
||||
permission_required = "authentik_stages_prompt.delete_prompt"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:stage-prompts")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Prompt")
|
||||
|
@ -1,23 +0,0 @@
|
||||
"""authentik Tasks List"""
|
||||
from typing import Any, Dict
|
||||
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from authentik.admin.mixins import AdminRequiredMixin
|
||||
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
|
||||
|
||||
|
||||
class TaskListView(AdminRequiredMixin, TemplateView):
|
||||
"""Show list of all background tasks"""
|
||||
|
||||
template_name = "administration/task/list.html"
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["object_list"] = sorted(
|
||||
TaskInfo.all().values(), key=lambda x: x.task_name
|
||||
)
|
||||
kwargs["task_successful"] = TaskResultStatus.SUCCESSFUL
|
||||
kwargs["task_warning"] = TaskResultStatus.WARNING
|
||||
kwargs["task_error"] = TaskResultStatus.ERROR
|
||||
return kwargs
|
@ -2,38 +2,12 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import ListView
|
||||
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.views.utils import (
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.core.models import Token
|
||||
|
||||
|
||||
class TokenListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all tokens"""
|
||||
|
||||
model = Token
|
||||
permission_required = "authentik_core.view_token"
|
||||
ordering = "expires"
|
||||
template_name = "administration/token/list.html"
|
||||
search_fields = [
|
||||
"identifier",
|
||||
"intent",
|
||||
"user__username",
|
||||
"description",
|
||||
]
|
||||
|
||||
|
||||
class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView):
|
||||
"""Delete token"""
|
||||
|
||||
@ -41,5 +15,5 @@ class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage
|
||||
permission_required = "authentik_core.delete_token"
|
||||
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:tokens")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted Token")
|
||||
|
@ -8,49 +8,20 @@ from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import DetailView, ListView, UpdateView
|
||||
from guardian.mixins import (
|
||||
PermissionListMixin,
|
||||
PermissionRequiredMixin,
|
||||
get_anonymous_user,
|
||||
)
|
||||
from django.views.generic import DetailView, UpdateView
|
||||
from guardian.mixins import PermissionRequiredMixin
|
||||
|
||||
from authentik.admin.forms.users import UserForm
|
||||
from authentik.admin.views.utils import (
|
||||
BackSuccessUrlMixin,
|
||||
DeleteMessageView,
|
||||
SearchListMixin,
|
||||
UserPaginateListMixin,
|
||||
)
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.core.models import Token, User
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
|
||||
|
||||
class UserListView(
|
||||
LoginRequiredMixin,
|
||||
PermissionListMixin,
|
||||
UserPaginateListMixin,
|
||||
SearchListMixin,
|
||||
ListView,
|
||||
):
|
||||
"""Show list of all users"""
|
||||
|
||||
model = User
|
||||
permission_required = "authentik_core.view_user"
|
||||
ordering = "username"
|
||||
template_name = "administration/user/list.html"
|
||||
search_fields = ["username", "name", "attributes"]
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().exclude(pk=get_anonymous_user().pk)
|
||||
|
||||
|
||||
class UserCreateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
DjangoPermissionRequiredMixin,
|
||||
CreateAssignPermView,
|
||||
@ -62,13 +33,12 @@ class UserCreateView(
|
||||
permission_required = "authentik_core.add_user"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_admin:users")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully created User")
|
||||
|
||||
|
||||
class UserUpdateView(
|
||||
SuccessMessageMixin,
|
||||
BackSuccessUrlMixin,
|
||||
LoginRequiredMixin,
|
||||
PermissionRequiredMixin,
|
||||
UpdateView,
|
||||
@ -82,7 +52,7 @@ class UserUpdateView(
|
||||
# By default the object's name is user which is used by other checks
|
||||
context_object_name = "object"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_admin:users")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully updated User")
|
||||
|
||||
|
||||
@ -95,13 +65,11 @@ class UserDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageV
|
||||
# By default the object's name is user which is used by other checks
|
||||
context_object_name = "object"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_admin:users")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully deleted User")
|
||||
|
||||
|
||||
class UserDisableView(
|
||||
LoginRequiredMixin, PermissionRequiredMixin, BackSuccessUrlMixin, DeleteMessageView
|
||||
):
|
||||
class UserDisableView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView):
|
||||
"""Disable user"""
|
||||
|
||||
object: User
|
||||
@ -112,7 +80,7 @@ class UserDisableView(
|
||||
# By default the object's name is user which is used by other checks
|
||||
context_object_name = "object"
|
||||
template_name = "administration/user/disable.html"
|
||||
success_url = reverse_lazy("authentik_admin:users")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully disabled User")
|
||||
|
||||
def delete(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
@ -123,9 +91,7 @@ class UserDisableView(
|
||||
return HttpResponseRedirect(success_url)
|
||||
|
||||
|
||||
class UserEnableView(
|
||||
LoginRequiredMixin, PermissionRequiredMixin, BackSuccessUrlMixin, DetailView
|
||||
):
|
||||
class UserEnableView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
"""Enable user"""
|
||||
|
||||
object: User
|
||||
@ -135,15 +101,14 @@ class UserEnableView(
|
||||
|
||||
# By default the object's name is user which is used by other checks
|
||||
context_object_name = "object"
|
||||
success_url = reverse_lazy("authentik_admin:users")
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
success_message = _("Successfully enabled User")
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
self.object: User = self.get_object()
|
||||
success_url = self.get_success_url()
|
||||
self.object.is_active = True
|
||||
self.object.save()
|
||||
return HttpResponseRedirect(success_url)
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
|
||||
|
||||
class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
@ -160,9 +125,7 @@ class UserPasswordResetView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
|
||||
)
|
||||
querystring = urlencode({"token": token.key})
|
||||
link = request.build_absolute_uri(
|
||||
reverse("authentik_flows:default-recovery") + f"?{querystring}"
|
||||
reverse_lazy("authentik_flows:default-recovery") + f"?{querystring}"
|
||||
)
|
||||
messages.success(
|
||||
request, _("Password reset link: <pre>%(link)s</pre>" % {"link": link})
|
||||
)
|
||||
return redirect("authentik_admin:users")
|
||||
messages.success(request, _("Password reset link: %(link)s" % {"link": link}))
|
||||
return redirect("/")
|
||||
|
@ -1,15 +1,11 @@
|
||||
"""authentik admin util views"""
|
||||
from typing import Any, Dict, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
from typing import Any
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.contrib.postgres.search import SearchQuery, SearchVector
|
||||
from django.db.models import QuerySet
|
||||
from django.http import Http404
|
||||
from django.http.request import HttpRequest
|
||||
from django.views.generic import DeleteView, ListView, UpdateView
|
||||
from django.views.generic.list import MultipleObjectMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import DeleteView, UpdateView
|
||||
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
from authentik.lib.views import CreateAssignPermView
|
||||
@ -18,42 +14,13 @@ from authentik.lib.views import CreateAssignPermView
|
||||
class DeleteMessageView(SuccessMessageMixin, DeleteView):
|
||||
"""DeleteView which shows `self.success_message` on successful deletion"""
|
||||
|
||||
success_url = reverse_lazy("authentik_core:shell")
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
messages.success(self.request, self.success_message)
|
||||
return super().delete(request, *args, **kwargs)
|
||||
|
||||
|
||||
class InheritanceListView(ListView):
|
||||
"""ListView for objects using InheritanceManager"""
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs["types"] = {x.__name__: x for x in all_subclasses(self.model)}
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_subclasses()
|
||||
|
||||
|
||||
class SearchListMixin(MultipleObjectMixin):
|
||||
"""Accept search query using `search` querystring parameter. Requires self.search_fields,
|
||||
a list of all fields to search. Can contain special lookups like __icontains"""
|
||||
|
||||
search_fields: List[str]
|
||||
|
||||
def get_queryset(self) -> QuerySet:
|
||||
queryset = super().get_queryset()
|
||||
if "search" in self.request.GET:
|
||||
raw_query = self.request.GET["search"]
|
||||
if raw_query == "":
|
||||
# Empty query, don't search at all
|
||||
return queryset
|
||||
search = SearchQuery(raw_query, search_type="websearch")
|
||||
return queryset.annotate(search=SearchVector(*self.search_fields)).filter(
|
||||
search=search
|
||||
)
|
||||
return queryset
|
||||
|
||||
|
||||
class InheritanceCreateView(CreateAssignPermView):
|
||||
"""CreateView for objects using InheritanceManager"""
|
||||
|
||||
@ -67,7 +34,7 @@ class InheritanceCreateView(CreateAssignPermView):
|
||||
raise Http404 from exc
|
||||
return model().form
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
form_cls = self.get_form_class()
|
||||
if hasattr(form_cls, "template_name"):
|
||||
@ -78,7 +45,7 @@ class InheritanceCreateView(CreateAssignPermView):
|
||||
class InheritanceUpdateView(UpdateView):
|
||||
"""UpdateView for objects using InheritanceManager"""
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
form_cls = self.get_form_class()
|
||||
if hasattr(form_cls, "template_name"):
|
||||
@ -94,31 +61,3 @@ class InheritanceUpdateView(UpdateView):
|
||||
.select_subclasses()
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
class BackSuccessUrlMixin:
|
||||
"""Checks if a relative URL has been given as ?back param, and redirect to it. Otherwise
|
||||
default to self.success_url."""
|
||||
|
||||
request: HttpRequest
|
||||
|
||||
success_url: Optional[str]
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
"""get_success_url from FormMixin"""
|
||||
back_param = self.request.GET.get("back")
|
||||
if back_param:
|
||||
if not bool(urlparse(back_param).netloc):
|
||||
return back_param
|
||||
return str(self.success_url)
|
||||
|
||||
|
||||
class UserPaginateListMixin:
|
||||
"""Get paginate_by value from user's attributes, defaulting to 15"""
|
||||
|
||||
request: HttpRequest
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_paginate_by(self, queryset: QuerySet) -> int:
|
||||
"""get_paginate_by Function of ListView"""
|
||||
return self.request.user.attributes.get("paginate_by", 15)
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""API Authentication"""
|
||||
from base64 import b64decode
|
||||
from binascii import Error
|
||||
from typing import Any, Optional, Tuple, Union
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from rest_framework.authentication import BaseAuthentication, get_authorization_header
|
||||
from rest_framework.request import Request
|
||||
@ -44,7 +44,7 @@ def token_from_header(raw_header: bytes) -> Optional[Token]:
|
||||
class AuthentikTokenAuthentication(BaseAuthentication):
|
||||
"""Token-based authentication using HTTP Basic authentication"""
|
||||
|
||||
def authenticate(self, request: Request) -> Union[Tuple[User, Any], None]:
|
||||
def authenticate(self, request: Request) -> Union[tuple[User, Any], None]:
|
||||
"""Token-based authentication using HTTP Basic authentication"""
|
||||
auth = get_authorization_header(request)
|
||||
|
||||
|
@ -6,6 +6,7 @@ from rest_framework.response import Response
|
||||
class Pagination(pagination.PageNumberPagination):
|
||||
"""Pagination which includes total pages and current page"""
|
||||
|
||||
page_query_param = "page"
|
||||
page_size_query_param = "page_size"
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
|
97
authentik/api/pagination_schema.py
Normal file
97
authentik/api/pagination_schema.py
Normal file
@ -0,0 +1,97 @@
|
||||
"""Swagger Pagination Schema class"""
|
||||
from typing import OrderedDict
|
||||
|
||||
from drf_yasg2 import openapi
|
||||
from drf_yasg2.inspectors import PaginatorInspector
|
||||
|
||||
|
||||
class PaginationInspector(PaginatorInspector):
|
||||
"""Swagger Pagination Schema class"""
|
||||
|
||||
def get_paginated_response(self, paginator, response_schema):
|
||||
"""
|
||||
:param BasePagination paginator: the paginator
|
||||
:param openapi.Schema response_schema: the response schema that must be paged.
|
||||
:rtype: openapi.Schema
|
||||
"""
|
||||
|
||||
return openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties=OrderedDict(
|
||||
(
|
||||
(
|
||||
"pagination",
|
||||
openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties=OrderedDict(
|
||||
(
|
||||
("next", openapi.Schema(type=openapi.TYPE_NUMBER)),
|
||||
(
|
||||
"previous",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
("count", openapi.Schema(type=openapi.TYPE_NUMBER)),
|
||||
(
|
||||
"current",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
(
|
||||
"total_pages",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
(
|
||||
"start_index",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
(
|
||||
"end_index",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
)
|
||||
),
|
||||
required=[
|
||||
"next",
|
||||
"previous",
|
||||
"count",
|
||||
"current",
|
||||
"total_pages",
|
||||
"start_index",
|
||||
"end_index",
|
||||
],
|
||||
),
|
||||
),
|
||||
("results", response_schema),
|
||||
)
|
||||
),
|
||||
required=["results", "pagination"],
|
||||
)
|
||||
|
||||
def get_paginator_parameters(self, paginator):
|
||||
"""
|
||||
Get the pagination parameters for a single paginator **instance**.
|
||||
|
||||
Should return :data:`.NotHandled` if this inspector
|
||||
does not know how to handle the given `paginator`.
|
||||
|
||||
:param BasePagination paginator: the paginator
|
||||
:rtype: list[openapi.Parameter]
|
||||
"""
|
||||
|
||||
return [
|
||||
openapi.Parameter(
|
||||
"page",
|
||||
openapi.IN_QUERY,
|
||||
"Page Index",
|
||||
False,
|
||||
None,
|
||||
openapi.TYPE_INTEGER,
|
||||
),
|
||||
openapi.Parameter(
|
||||
"page_size",
|
||||
openapi.IN_QUERY,
|
||||
"Page Size",
|
||||
False,
|
||||
None,
|
||||
openapi.TYPE_INTEGER,
|
||||
),
|
||||
]
|
@ -1,10 +1,11 @@
|
||||
"""core Configs API"""
|
||||
from django.db.models import Model
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.fields import BooleanField, CharField
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ReadOnlyField, Serializer
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.lib.config import CONFIG
|
||||
@ -13,12 +14,12 @@ from authentik.lib.config import CONFIG
|
||||
class ConfigSerializer(Serializer):
|
||||
"""Serialize authentik Config into DRF Object"""
|
||||
|
||||
branding_logo = ReadOnlyField()
|
||||
branding_title = ReadOnlyField()
|
||||
branding_logo = CharField(read_only=True)
|
||||
branding_title = CharField(read_only=True)
|
||||
|
||||
error_reporting_enabled = ReadOnlyField()
|
||||
error_reporting_environment = ReadOnlyField()
|
||||
error_reporting_send_pii = ReadOnlyField()
|
||||
error_reporting_enabled = BooleanField(read_only=True)
|
||||
error_reporting_environment = CharField(read_only=True)
|
||||
error_reporting_send_pii = BooleanField(read_only=True)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
@ -32,7 +33,7 @@ class ConfigsViewSet(ViewSet):
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(responses={200: ConfigSerializer(many=True)})
|
||||
@swagger_auto_schema(responses={200: ConfigSerializer(many=False)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Retrive public configuration options"""
|
||||
config = ConfigSerializer(
|
||||
|
@ -1,37 +0,0 @@
|
||||
"""core messages API"""
|
||||
from django.contrib.messages import get_messages
|
||||
from django.db.models import Model
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ReadOnlyField, Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
|
||||
class MessageSerializer(Serializer):
|
||||
"""Serialize Django Message into DRF Object"""
|
||||
|
||||
message = ReadOnlyField()
|
||||
level = ReadOnlyField()
|
||||
tags = ReadOnlyField()
|
||||
extra_tags = ReadOnlyField()
|
||||
level_tag = ReadOnlyField()
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class MessagesViewSet(ViewSet):
|
||||
"""Read-only view set that returns the current session's messages"""
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(responses={200: MessageSerializer(many=True)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""List current messages and pass into Serializer"""
|
||||
all_messages = list(get_messages(request))
|
||||
return Response(MessageSerializer(all_messages, many=True).data)
|
@ -1,4 +1,5 @@
|
||||
"""api v2 urls"""
|
||||
from django.conf import settings
|
||||
from django.urls import path, re_path
|
||||
from drf_yasg2 import openapi
|
||||
from drf_yasg2.views import get_schema_view
|
||||
@ -10,7 +11,6 @@ from authentik.admin.api.tasks import TaskViewSet
|
||||
from authentik.admin.api.version import VersionViewSet
|
||||
from authentik.admin.api.workers import WorkerViewSet
|
||||
from authentik.api.v2.config import ConfigsViewSet
|
||||
from authentik.api.v2.messages import MessagesViewSet
|
||||
from authentik.core.api.applications import ApplicationViewSet
|
||||
from authentik.core.api.groups import GroupViewSet
|
||||
from authentik.core.api.propertymappings import PropertyMappingViewSet
|
||||
@ -23,22 +23,17 @@ from authentik.events.api.event import EventViewSet
|
||||
from authentik.events.api.notification import NotificationViewSet
|
||||
from authentik.events.api.notification_rule import NotificationRuleViewSet
|
||||
from authentik.events.api.notification_transport import NotificationTransportViewSet
|
||||
from authentik.flows.api import (
|
||||
FlowCacheViewSet,
|
||||
FlowStageBindingViewSet,
|
||||
FlowViewSet,
|
||||
StageViewSet,
|
||||
)
|
||||
from authentik.outposts.api import (
|
||||
from authentik.flows.api.bindings import FlowStageBindingViewSet
|
||||
from authentik.flows.api.flows import FlowViewSet
|
||||
from authentik.flows.api.stages import StageViewSet
|
||||
from authentik.flows.views import FlowExecutorView
|
||||
from authentik.outposts.api.outpost_service_connections import (
|
||||
DockerServiceConnectionViewSet,
|
||||
KubernetesServiceConnectionViewSet,
|
||||
OutpostViewSet,
|
||||
)
|
||||
from authentik.policies.api import (
|
||||
PolicyBindingViewSet,
|
||||
PolicyCacheViewSet,
|
||||
PolicyViewSet,
|
||||
ServiceConnectionViewSet,
|
||||
)
|
||||
from authentik.outposts.api.outposts import OutpostViewSet
|
||||
from authentik.policies.api import PolicyBindingViewSet, PolicyViewSet
|
||||
from authentik.policies.dummy.api import DummyPolicyViewSet
|
||||
from authentik.policies.event_matcher.api import EventMatcherPolicyViewSet
|
||||
from authentik.policies.expiry.api import PasswordExpiryPolicyViewSet
|
||||
@ -46,7 +41,11 @@ from authentik.policies.expression.api import ExpressionPolicyViewSet
|
||||
from authentik.policies.group_membership.api import GroupMembershipPolicyViewSet
|
||||
from authentik.policies.hibp.api import HaveIBeenPwendPolicyViewSet
|
||||
from authentik.policies.password.api import PasswordPolicyViewSet
|
||||
from authentik.policies.reputation.api import ReputationPolicyViewSet
|
||||
from authentik.policies.reputation.api import (
|
||||
IPReputationViewSet,
|
||||
ReputationPolicyViewSet,
|
||||
UserReputationViewSet,
|
||||
)
|
||||
from authentik.providers.oauth2.api import OAuth2ProviderViewSet, ScopeMappingViewSet
|
||||
from authentik.providers.proxy.api import (
|
||||
ProxyOutpostConfigViewSet,
|
||||
@ -56,15 +55,31 @@ from authentik.providers.saml.api import SAMLPropertyMappingViewSet, SAMLProvide
|
||||
from authentik.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet
|
||||
from authentik.sources.oauth.api import OAuthSourceViewSet
|
||||
from authentik.sources.saml.api import SAMLSourceViewSet
|
||||
from authentik.stages.authenticator_static.api import (
|
||||
AuthenticatorStaticStageViewSet,
|
||||
StaticAdminDeviceViewSet,
|
||||
StaticDeviceViewSet,
|
||||
)
|
||||
from authentik.stages.authenticator_totp.api import (
|
||||
AuthenticatorTOTPStageViewSet,
|
||||
TOTPAdminDeviceViewSet,
|
||||
TOTPDeviceViewSet,
|
||||
)
|
||||
from authentik.stages.authenticator_validate.api import (
|
||||
AuthenticatorValidateStageViewSet,
|
||||
)
|
||||
from authentik.stages.authenticator_webauthn.api import (
|
||||
AuthenticateWebAuthnStageViewSet,
|
||||
WebAuthnAdminDeviceViewSet,
|
||||
WebAuthnDeviceViewSet,
|
||||
)
|
||||
from authentik.stages.captcha.api import CaptchaStageViewSet
|
||||
from authentik.stages.consent.api import ConsentStageViewSet
|
||||
from authentik.stages.deny.api import DenyStageViewSet
|
||||
from authentik.stages.dummy.api import DummyStageViewSet
|
||||
from authentik.stages.email.api import EmailStageViewSet
|
||||
from authentik.stages.identification.api import IdentificationStageViewSet
|
||||
from authentik.stages.invitation.api import InvitationStageViewSet, InvitationViewSet
|
||||
from authentik.stages.otp_static.api import OTPStaticStageViewSet
|
||||
from authentik.stages.otp_time.api import OTPTimeStageViewSet
|
||||
from authentik.stages.otp_validate.api import OTPValidateStageViewSet
|
||||
from authentik.stages.password.api import PasswordStageViewSet
|
||||
from authentik.stages.prompt.api import PromptStageViewSet, PromptViewSet
|
||||
from authentik.stages.user_delete.api import UserDeleteStageViewSet
|
||||
@ -74,7 +89,6 @@ from authentik.stages.user_write.api import UserWriteStageViewSet
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
router.register("root/messages", MessagesViewSet, basename="messages")
|
||||
router.register("root/config", ConfigsViewSet, basename="configs")
|
||||
|
||||
router.register("admin/version", VersionViewSet, basename="admin_version")
|
||||
@ -88,6 +102,7 @@ router.register("core/users", UserViewSet)
|
||||
router.register("core/tokens", TokenViewSet)
|
||||
|
||||
router.register("outposts/outposts", OutpostViewSet)
|
||||
router.register("outposts/service_connections/all", ServiceConnectionViewSet)
|
||||
router.register("outposts/service_connections/docker", DockerServiceConnectionViewSet)
|
||||
router.register(
|
||||
"outposts/service_connections/kubernetes", KubernetesServiceConnectionViewSet
|
||||
@ -95,7 +110,6 @@ router.register(
|
||||
router.register("outposts/proxy", ProxyOutpostConfigViewSet)
|
||||
|
||||
router.register("flows/instances", FlowViewSet)
|
||||
router.register("flows/cached", FlowCacheViewSet, basename="flows_cache")
|
||||
router.register("flows/bindings", FlowStageBindingViewSet)
|
||||
|
||||
router.register("crypto/certificatekeypairs", CertificateKeyPairViewSet)
|
||||
@ -111,7 +125,6 @@ router.register("sources/saml", SAMLSourceViewSet)
|
||||
router.register("sources/oauth", OAuthSourceViewSet)
|
||||
|
||||
router.register("policies/all", PolicyViewSet)
|
||||
router.register("policies/cached", PolicyCacheViewSet, basename="policies_cache")
|
||||
router.register("policies/bindings", PolicyBindingViewSet)
|
||||
router.register("policies/expression", ExpressionPolicyViewSet)
|
||||
router.register("policies/event_matcher", EventMatcherPolicyViewSet)
|
||||
@ -119,6 +132,8 @@ router.register("policies/group_membership", GroupMembershipPolicyViewSet)
|
||||
router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet)
|
||||
router.register("policies/password_expiry", PasswordExpiryPolicyViewSet)
|
||||
router.register("policies/password", PasswordPolicyViewSet)
|
||||
router.register("policies/reputation/users", UserReputationViewSet)
|
||||
router.register("policies/reputation/ips", IPReputationViewSet)
|
||||
router.register("policies/reputation", ReputationPolicyViewSet)
|
||||
|
||||
router.register("providers/all", ProviderViewSet)
|
||||
@ -131,16 +146,25 @@ router.register("propertymappings/ldap", LDAPPropertyMappingViewSet)
|
||||
router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
|
||||
router.register("propertymappings/scope", ScopeMappingViewSet)
|
||||
|
||||
router.register("authenticators/static", StaticDeviceViewSet)
|
||||
router.register("authenticators/totp", TOTPDeviceViewSet)
|
||||
router.register("authenticators/webauthn", WebAuthnDeviceViewSet)
|
||||
router.register("authenticators/admin/static", StaticAdminDeviceViewSet)
|
||||
router.register("authenticators/admin/totp", TOTPAdminDeviceViewSet)
|
||||
router.register("authenticators/admin/webauthn", WebAuthnAdminDeviceViewSet)
|
||||
|
||||
router.register("stages/all", StageViewSet)
|
||||
router.register("stages/authenticator/static", AuthenticatorStaticStageViewSet)
|
||||
router.register("stages/authenticator/totp", AuthenticatorTOTPStageViewSet)
|
||||
router.register("stages/authenticator/validate", AuthenticatorValidateStageViewSet)
|
||||
router.register("stages/authenticator/webauthn", AuthenticateWebAuthnStageViewSet)
|
||||
router.register("stages/captcha", CaptchaStageViewSet)
|
||||
router.register("stages/consent", ConsentStageViewSet)
|
||||
router.register("stages/deny", DenyStageViewSet)
|
||||
router.register("stages/email", EmailStageViewSet)
|
||||
router.register("stages/identification", IdentificationStageViewSet)
|
||||
router.register("stages/invitation", InvitationStageViewSet)
|
||||
router.register("stages/invitation/invitations", InvitationViewSet)
|
||||
router.register("stages/otp_static", OTPStaticStageViewSet)
|
||||
router.register("stages/otp_time", OTPTimeStageViewSet)
|
||||
router.register("stages/otp_validate", OTPValidateStageViewSet)
|
||||
router.register("stages/invitation/stages", InvitationStageViewSet)
|
||||
router.register("stages/password", PasswordStageViewSet)
|
||||
router.register("stages/prompt/prompts", PromptViewSet)
|
||||
router.register("stages/prompt/stages", PromptStageViewSet)
|
||||
@ -160,22 +184,26 @@ info = openapi.Info(
|
||||
name="GNU GPLv3", url="https://github.com/BeryJu/authentik/blob/master/LICENSE"
|
||||
),
|
||||
)
|
||||
SchemaView = get_schema_view(
|
||||
info,
|
||||
public=True,
|
||||
permission_classes=(AllowAny,),
|
||||
)
|
||||
SchemaView = get_schema_view(info, public=True, permission_classes=(AllowAny,))
|
||||
|
||||
urlpatterns = [
|
||||
urlpatterns = router.urls + [
|
||||
path(
|
||||
"flows/executor/<slug:flow_slug>/",
|
||||
FlowExecutorView.as_view(),
|
||||
name="flow-executor",
|
||||
),
|
||||
re_path(
|
||||
r"^swagger(?P<format>\.json|\.yaml)$",
|
||||
SchemaView.without_ui(cache_timeout=0),
|
||||
name="schema-json",
|
||||
),
|
||||
path(
|
||||
"swagger/",
|
||||
SchemaView.with_ui("swagger", cache_timeout=0),
|
||||
name="schema-swagger-ui",
|
||||
),
|
||||
path("redoc/", SchemaView.with_ui("redoc", cache_timeout=0), name="schema-redoc"),
|
||||
] + router.urls
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns = urlpatterns + [
|
||||
path(
|
||||
"swagger/",
|
||||
SchemaView.with_ui("swagger", cache_timeout=0),
|
||||
name="schema-swagger-ui",
|
||||
),
|
||||
]
|
||||
|
@ -2,6 +2,7 @@
|
||||
from django.core.cache import cache
|
||||
from django.db.models import QuerySet
|
||||
from django.http.response import Http404
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
@ -13,7 +14,7 @@ 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 get_events_per_1h
|
||||
from authentik.admin.api.metrics import CoordinateSerializer, get_events_per_1h
|
||||
from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.models import Application
|
||||
from authentik.events.models import EventAction
|
||||
@ -109,6 +110,7 @@ class ApplicationViewSet(ModelViewSet):
|
||||
serializer = self.get_serializer(allowed_applications, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
@swagger_auto_schema(responses={200: CoordinateSerializer(many=True)})
|
||||
@action(detail=True)
|
||||
def metrics(self, request: Request, slug: str):
|
||||
"""Metrics for application logins"""
|
||||
|
@ -19,3 +19,6 @@ class GroupViewSet(ModelViewSet):
|
||||
|
||||
queryset = Group.objects.all()
|
||||
serializer_class = GroupSerializer
|
||||
search_fields = ["name", "is_superuser"]
|
||||
filterset_fields = ["name", "is_superuser"]
|
||||
ordering = ["name"]
|
||||
|
@ -1,9 +1,16 @@
|
||||
"""PropertyMapping API Views"""
|
||||
from django.urls import reverse
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from authentik.core.api.utils import MetaNameSerializer
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.models import PropertyMapping
|
||||
from authentik.lib.templatetags.authentik_utils import verbose_name
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
|
||||
class PropertyMappingSerializer(ModelSerializer, MetaNameSerializer):
|
||||
@ -47,3 +54,19 @@ class PropertyMappingViewSet(ReadOnlyModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return PropertyMapping.objects.select_subclasses()
|
||||
|
||||
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
|
||||
@action(detail=False)
|
||||
def types(self, request: Request) -> Response:
|
||||
"""Get all creatable property-mapping types"""
|
||||
data = []
|
||||
for subclass in all_subclasses(self.queryset.model):
|
||||
data.append(
|
||||
{
|
||||
"name": verbose_name(subclass),
|
||||
"description": subclass.__doc__,
|
||||
"link": reverse("authentik_admin:property-mapping-create")
|
||||
+ f"?type={subclass.__name__}",
|
||||
}
|
||||
)
|
||||
return Response(TypeCreateSerializer(data, many=True).data)
|
||||
|
@ -1,10 +1,18 @@
|
||||
"""Provider API Views"""
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import ReadOnlyField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.utils import MetaNameSerializer
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.models import Provider
|
||||
from authentik.lib.templatetags.authentik_utils import verbose_name
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
|
||||
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
|
||||
@ -51,3 +59,26 @@ class ProviderViewSet(ModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return Provider.objects.select_subclasses()
|
||||
|
||||
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
|
||||
@action(detail=False)
|
||||
def types(self, request: Request) -> Response:
|
||||
"""Get all creatable provider types"""
|
||||
data = []
|
||||
for subclass in all_subclasses(self.queryset.model):
|
||||
data.append(
|
||||
{
|
||||
"name": verbose_name(subclass),
|
||||
"description": subclass.__doc__,
|
||||
"link": reverse("authentik_admin:provider-create")
|
||||
+ f"?type={subclass.__name__}",
|
||||
}
|
||||
)
|
||||
data.append(
|
||||
{
|
||||
"name": _("SAML Provider from Metadata"),
|
||||
"description": _("Create a SAML Provider by importing its Metadata."),
|
||||
"link": reverse("authentik_admin:provider-saml-from-metadata"),
|
||||
}
|
||||
)
|
||||
return Response(TypeCreateSerializer(data, many=True).data)
|
||||
|
@ -1,31 +1,41 @@
|
||||
"""Source API Views"""
|
||||
from django.urls import reverse
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from authentik.admin.forms.source import SOURCE_SERIALIZER_FIELDS
|
||||
from authentik.core.api.utils import MetaNameSerializer
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.models import Source
|
||||
from authentik.lib.templatetags.authentik_utils import verbose_name
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
|
||||
class SourceSerializer(ModelSerializer, MetaNameSerializer):
|
||||
"""Source Serializer"""
|
||||
|
||||
__type__ = SerializerMethodField(method_name="get_type")
|
||||
object_type = SerializerMethodField()
|
||||
|
||||
def get_type(self, obj):
|
||||
def get_object_type(self, obj):
|
||||
"""Get object type so that we know which API Endpoint to use to get the full object"""
|
||||
return obj._meta.object_name.lower().replace("source", "")
|
||||
|
||||
def to_representation(self, instance: Source):
|
||||
# pyright: reportGeneralTypeIssues=false
|
||||
if instance.__class__ == Source:
|
||||
return super().to_representation(instance)
|
||||
return instance.serializer(instance=instance).data
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Source
|
||||
fields = SOURCE_SERIALIZER_FIELDS + ["__type__"]
|
||||
fields = [
|
||||
"pk",
|
||||
"name",
|
||||
"slug",
|
||||
"enabled",
|
||||
"authentication_flow",
|
||||
"enrollment_flow",
|
||||
"object_type",
|
||||
"verbose_name",
|
||||
"verbose_name_plural",
|
||||
]
|
||||
|
||||
|
||||
class SourceViewSet(ReadOnlyModelViewSet):
|
||||
@ -37,3 +47,19 @@ class SourceViewSet(ReadOnlyModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return Source.objects.select_subclasses()
|
||||
|
||||
@swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)})
|
||||
@action(detail=False)
|
||||
def types(self, request: Request) -> Response:
|
||||
"""Get all creatable source types"""
|
||||
data = []
|
||||
for subclass in all_subclasses(self.queryset.model):
|
||||
data.append(
|
||||
{
|
||||
"name": verbose_name(subclass),
|
||||
"description": subclass.__doc__,
|
||||
"link": reverse("authentik_admin:source-create")
|
||||
+ f"?type={subclass.__name__}",
|
||||
}
|
||||
)
|
||||
return Response(TypeCreateSerializer(data, many=True).data)
|
||||
|
@ -1,11 +1,15 @@
|
||||
"""Tokens API Viewset"""
|
||||
from django.db.models.base import Model
|
||||
from django.http.response import Http404
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.serializers import ModelSerializer, Serializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.users import UserSerializer
|
||||
from authentik.core.models import Token
|
||||
from authentik.events.models import Event, EventAction
|
||||
|
||||
@ -13,10 +17,33 @@ from authentik.events.models import Event, EventAction
|
||||
class TokenSerializer(ModelSerializer):
|
||||
"""Token Serializer"""
|
||||
|
||||
user = UserSerializer()
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Token
|
||||
fields = ["pk", "identifier", "intent", "user", "description"]
|
||||
fields = [
|
||||
"pk",
|
||||
"identifier",
|
||||
"intent",
|
||||
"user",
|
||||
"description",
|
||||
"expires",
|
||||
"expiring",
|
||||
]
|
||||
depth = 2
|
||||
|
||||
|
||||
class TokenViewSerializer(Serializer):
|
||||
"""Show token's current key"""
|
||||
|
||||
key = CharField(read_only=True)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class TokenViewSet(ModelViewSet):
|
||||
@ -25,13 +52,29 @@ class TokenViewSet(ModelViewSet):
|
||||
lookup_field = "identifier"
|
||||
queryset = Token.filter_not_expired()
|
||||
serializer_class = TokenSerializer
|
||||
search_fields = [
|
||||
"identifier",
|
||||
"intent",
|
||||
"user__username",
|
||||
"description",
|
||||
]
|
||||
filterset_fields = [
|
||||
"identifier",
|
||||
"intent",
|
||||
"user__username",
|
||||
"description",
|
||||
]
|
||||
ordering = ["expires"]
|
||||
|
||||
@swagger_auto_schema(responses={200: TokenViewSerializer(many=False)})
|
||||
@action(detail=True)
|
||||
# pylint: disable=unused-argument
|
||||
def view_key(self, request: Request, identifier: str) -> Response:
|
||||
"""Return token key and log access"""
|
||||
tokens = Token.filter_not_expired(identifier=identifier)
|
||||
if not tokens.exists():
|
||||
token: Token = self.get_object()
|
||||
if token.is_expired:
|
||||
raise Http404
|
||||
token = tokens.first()
|
||||
Event.new(EventAction.TOKEN_VIEW, token=token).from_http(request)
|
||||
return Response({"key": token.key})
|
||||
Event.new(EventAction.SECRET_VIEW, secret=token).from_http( # noqa # nosec
|
||||
request
|
||||
)
|
||||
return Response(TokenViewSerializer({"key": token.key}).data)
|
||||
|
@ -2,33 +2,35 @@
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from guardian.utils import get_anonymous_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import (
|
||||
BooleanField,
|
||||
ModelSerializer,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.serializers import BooleanField, ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.lib.templatetags.authentik_utils import avatar
|
||||
|
||||
|
||||
class UserSerializer(ModelSerializer):
|
||||
"""User Serializer"""
|
||||
|
||||
is_superuser = BooleanField(read_only=True)
|
||||
avatar = SerializerMethodField()
|
||||
|
||||
def get_avatar(self, user: User) -> str:
|
||||
"""Add user's avatar as URL"""
|
||||
return avatar(user)
|
||||
avatar = CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = User
|
||||
fields = ["pk", "username", "name", "is_superuser", "email", "avatar"]
|
||||
fields = [
|
||||
"pk",
|
||||
"username",
|
||||
"name",
|
||||
"is_active",
|
||||
"last_login",
|
||||
"is_superuser",
|
||||
"email",
|
||||
"avatar",
|
||||
"attributes",
|
||||
]
|
||||
|
||||
|
||||
class UserViewSet(ModelViewSet):
|
||||
@ -36,6 +38,8 @@ class UserViewSet(ModelViewSet):
|
||||
|
||||
queryset = User.objects.none()
|
||||
serializer_class = UserSerializer
|
||||
search_fields = ["username", "name", "is_active"]
|
||||
filterset_fields = ["username", "name", "is_active"]
|
||||
|
||||
def get_queryset(self):
|
||||
return User.objects.all().exclude(pk=get_anonymous_user().pk)
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""API Utilities"""
|
||||
from django.db.models import Model
|
||||
from rest_framework.fields import CharField, IntegerField
|
||||
from rest_framework.serializers import Serializer, SerializerMethodField
|
||||
|
||||
|
||||
@ -22,3 +23,29 @@ class MetaNameSerializer(Serializer):
|
||||
def get_verbose_name_plural(self, obj: Model) -> str:
|
||||
"""Return object's plural verbose_name"""
|
||||
return obj._meta.verbose_name_plural
|
||||
|
||||
|
||||
class TypeCreateSerializer(Serializer):
|
||||
"""Types of an object that can be created"""
|
||||
|
||||
name = CharField(required=True)
|
||||
description = CharField(required=True)
|
||||
link = CharField(required=True)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CacheSerializer(Serializer):
|
||||
"""Generic cache stats for an object"""
|
||||
|
||||
count = IntegerField(read_only=True)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
@ -9,10 +9,10 @@ from authentik.lib.widgets import GroupedModelChoiceField
|
||||
class ApplicationForm(forms.ModelForm):
|
||||
"""Application Form"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, *args, **kwargs): # pragma: no cover
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["provider"].queryset = (
|
||||
Provider.objects.all().order_by("pk").select_subclasses()
|
||||
Provider.objects.all().order_by("name").select_subclasses()
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -9,6 +9,7 @@ from django.http import HttpRequest, HttpResponse
|
||||
SESSION_IMPERSONATE_USER = "authentik_impersonate_user"
|
||||
SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user"
|
||||
LOCAL = local()
|
||||
RESPONSE_HEADER_ID = "X-authentik-id"
|
||||
|
||||
|
||||
class ImpersonateMiddleware:
|
||||
@ -43,7 +44,7 @@ class RequestIDMiddleware:
|
||||
setattr(request, "request_id", request_id)
|
||||
LOCAL.authentik = {"request_id": request_id}
|
||||
response = self.get_response(request)
|
||||
response["X-authentik-id"] = request.request_id
|
||||
response[RESPONSE_HEADER_ID] = request.request_id
|
||||
del LOCAL.authentik["request_id"]
|
||||
return response
|
||||
|
||||
|
@ -1,15 +1,20 @@
|
||||
"""authentik core models"""
|
||||
from datetime import timedelta
|
||||
from typing import Any, Dict, Optional, Type
|
||||
from hashlib import md5, sha256
|
||||
from typing import Any, Optional, Type
|
||||
from urllib.parse import urlencode
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
from django.db import models
|
||||
from django.db.models import Q, QuerySet
|
||||
from django.forms import ModelForm
|
||||
from django.http import HttpRequest
|
||||
from django.templatetags.static import static
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from guardian.mixins import GuardianUserMixin
|
||||
@ -21,6 +26,7 @@ from authentik.core.exceptions import PropertyMappingExpressionException
|
||||
from authentik.core.signals import password_changed
|
||||
from authentik.core.types import UILoginButton
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
|
||||
from authentik.managed.models import ManagedModel
|
||||
from authentik.policies.models import PolicyBindingModel
|
||||
@ -29,6 +35,9 @@ LOGGER = get_logger()
|
||||
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
|
||||
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
|
||||
|
||||
GRAVATAR_URL = "https://secure.gravatar.com"
|
||||
DEFAULT_AVATAR = static("dist/assets/images/user_default.png")
|
||||
|
||||
|
||||
def default_token_duration():
|
||||
"""Default duration a Token is valid"""
|
||||
@ -81,7 +90,7 @@ class UserManager(DjangoUserManager):
|
||||
|
||||
|
||||
class User(GuardianUserMixin, AbstractUser):
|
||||
"""Custom User model to allow easier adding o f user-based settings"""
|
||||
"""Custom User model to allow easier adding of user-based settings"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||
name = models.TextField(help_text=_("User's display name."))
|
||||
@ -94,7 +103,7 @@ class User(GuardianUserMixin, AbstractUser):
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
def group_attributes(self) -> Dict[str, Any]:
|
||||
def group_attributes(self) -> dict[str, Any]:
|
||||
"""Get a dictionary containing the attributes from all groups the user belongs to,
|
||||
including the users attributes"""
|
||||
final_attributes = {}
|
||||
@ -119,6 +128,30 @@ class User(GuardianUserMixin, AbstractUser):
|
||||
self.password_change_date = now()
|
||||
return super().set_password(password)
|
||||
|
||||
@property
|
||||
def uid(self) -> str:
|
||||
"""Generate a globall unique UID, based on the user ID and the hashed secret key"""
|
||||
return sha256(f"{self.id}-{settings.SECRET_KEY}".encode("ascii")).hexdigest()
|
||||
|
||||
@property
|
||||
def avatar(self) -> str:
|
||||
"""Get avatar, depending on authentik.avatar setting"""
|
||||
mode = CONFIG.raw.get("authentik").get("avatars")
|
||||
if mode == "none":
|
||||
return DEFAULT_AVATAR
|
||||
if mode == "gravatar":
|
||||
parameters = [
|
||||
("s", "158"),
|
||||
("r", "g"),
|
||||
]
|
||||
# gravatar uses md5 for their URLs, so md5 can't be avoided
|
||||
mail_hash = md5(self.email.encode("utf-8")).hexdigest() # nosec
|
||||
gravatar_url = (
|
||||
f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters, doseq=True)}"
|
||||
)
|
||||
return escape(gravatar_url)
|
||||
raise ValueError(f"Invalid avatar mode {mode}")
|
||||
|
||||
class Meta:
|
||||
|
||||
permissions = (
|
||||
@ -252,11 +285,6 @@ class Source(SerializerModel, PolicyBindingModel):
|
||||
button. If source doesn't use http-based flow, return None."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def ui_additional_info(self) -> Optional[str]:
|
||||
"""Return additional Info, such as a callback URL. Show in the administration interface."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def ui_user_settings(self) -> Optional[str]:
|
||||
"""Entrypoint to integrate with User settings. Can either return None if no
|
||||
|
@ -46,8 +46,7 @@ def backup_database(self: MonitoredTask): # pragma: no cover
|
||||
TaskResult(
|
||||
TaskResultStatus.SUCCESSFUL,
|
||||
[
|
||||
f"Successfully finished database backup {naturaltime(start)}",
|
||||
out.getvalue(),
|
||||
f"Successfully finished database backup {naturaltime(start)} {out.getvalue()}",
|
||||
],
|
||||
)
|
||||
)
|
||||
|
@ -1,12 +0,0 @@
|
||||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<div class="pf-c-page">
|
||||
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">{% trans 'Skip to content' %}</a>
|
||||
{% block page_content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -11,12 +11,9 @@
|
||||
<title>{% block title %}{% trans title|default:config.authentik.branding.title %}{% endblock %}</title>
|
||||
<link rel="icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}">
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}?v={{ ak_version }}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.css' %}?v={{ ak_version }}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-addons.css' %}?v={{ ak_version }}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/fontawesome.min.css' %}?v={{ ak_version }}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}?v={{ ak_version }}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}?v={{ ak_version }}">
|
||||
<script src="{% url 'javascript-catalog' %}?v={{ ak_version }}"></script>
|
||||
<script src="{% static 'dist/main.js' %}?v={{ ak_version }}" type="module"></script>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends 'base/page.html' %}
|
||||
{% extends 'base/skeleton.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
@ -1,31 +0,0 @@
|
||||
{% extends "login/base.html" %}
|
||||
|
||||
{% load authentik_utils %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{{ title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<form method="POST" action="{{ url }}" autosubmit>
|
||||
{% csrf_token %}
|
||||
{% for key, value in attrs.items %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}">
|
||||
{% endfor %}
|
||||
<div class="pf-c-form__group pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-c-form__group-control">
|
||||
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<div class="pf-c-form__actions">
|
||||
<button class="pf-c-button pf-m-primary pf-m-block" type="submit">{% trans 'Continue' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
@ -1,9 +1,6 @@
|
||||
{% extends container_template|default:"administration/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
{% block above_form %}
|
||||
@ -38,4 +35,3 @@
|
||||
<input class="pf-c-button pf-m-danger" type="submit" form="delete-form" value="{% trans 'Delete' %}" />
|
||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Back" %}</a>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
@ -1,53 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
<i class="pf-icon pf-icon-applications"></i>
|
||||
{% trans 'Applications' %}
|
||||
</h1>
|
||||
</div>
|
||||
</section>
|
||||
<section class="pf-c-page__main-section">
|
||||
{% if applications %}
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
{% for app in applications %}
|
||||
<a href="{{ app.get_launch_url }}" class="pf-c-card pf-m-hoverable pf-m-compact ak-root-link">
|
||||
<div class="pf-c-card__header">
|
||||
{% if app.meta_icon %}
|
||||
<img class="app-icon pf-c-avatar" src="{{ app.meta_icon.url }}" alt="{% trans 'Application Icon' %}">
|
||||
{% else %}
|
||||
<i class="pf-icon pf-icon-arrow"></i>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<p id="card-1-check-label">{{ app.name }}</p>
|
||||
<div class="pf-c-content">
|
||||
<small>{{ app.meta_publisher }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
{% trans app.meta_description|truncatewords:35 %}
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-empty-state pf-m-full-height">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">{% trans 'No Applications available.' %}</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% trans "Either no applications are defined, or you don't have access to any." %}
|
||||
</div>
|
||||
{% if perms.authentik_core.add_application %}
|
||||
<a href="{% url 'authentik_admin:application-create' %}" class="pf-c-button pf-m-primary" type="button">
|
||||
{% trans 'Create Application' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
@ -28,10 +28,8 @@
|
||||
{% for source in sources %}
|
||||
<li class="pf-c-login__main-footer-links-item">
|
||||
<a href="{{ source.url }}" class="pf-c-login__main-footer-links-item-link">
|
||||
{% if source.icon_path %}
|
||||
<img src="{% static source.icon_path %}" alt="{{ source.name }}">
|
||||
{% elif source.icon_url %}
|
||||
<img src="icon_url" alt="{{ source.name }}">
|
||||
{% if source.icon_url %}
|
||||
<img src="{{ source.icon_url }}" alt="{{ source.name }}">
|
||||
{% else %}
|
||||
<i class="pf-icon pf-icon-arrow" title="{{ source.name }}"></i>
|
||||
{% endif %}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div class="pf-c-background-image">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="pf-c-background-image__filter" width="0" height="0">
|
||||
<filter id="image_overlay">
|
||||
<feColorMatrix type="matrix" values="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0"></feColorMatrix>
|
||||
<feColorMatrix in="SourceGraphic" type="matrix" values="1.3 0 0 0 0 0 1.3 0 0 0 0 0 1.3 0 0 0 0 0 1 0" />
|
||||
<feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
|
||||
<feFuncR type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncR>
|
||||
<feFuncG type="table" tableValues="0.086274509803922 0.43921568627451"></feFuncG>
|
||||
@ -48,7 +48,7 @@
|
||||
{% endfor %}
|
||||
{% if config.authentik.branding.title != "authentik" %}
|
||||
<li>
|
||||
<a href="https://github.com/beryju/authentik">
|
||||
<a href="https://goauthentik.io">
|
||||
{% trans 'Powered by authentik' %}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -1,19 +0,0 @@
|
||||
{% extends 'login/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block card %}
|
||||
<form method="POST" class="pf-c-form">
|
||||
{% block above_form %}
|
||||
{% endblock %}
|
||||
|
||||
{% include 'partials/form.html' %}
|
||||
|
||||
{% block beneath_form %}
|
||||
{% endblock %}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button class="pf-c-button pf-m-primary pf-m-block" type="submit">{% trans primary_action %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
@ -1,18 +0,0 @@
|
||||
{% extends 'login/form.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block above_form %}
|
||||
<div class="pf-c-form__group">
|
||||
<div class="form-control-static">
|
||||
<div class="left">
|
||||
<img class="pf-c-avatar" src="{% avatar user %}" alt="">
|
||||
{{ user.username }}
|
||||
</div>
|
||||
<div class="right">
|
||||
<a href="{% url 'authentik_flows:cancel' %}">{% trans 'Not you?' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,24 +0,0 @@
|
||||
{% extends 'login/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block title %}
|
||||
{% trans title %}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<meta http-equiv="refresh" content="0; url={{ target_url }}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans title %}</h1>
|
||||
</header>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="spinner spinner-lg"></div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
@ -3,7 +3,7 @@
|
||||
|
||||
{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="pf-c-form__group has-error">
|
||||
<div class="pf-c-form__group">
|
||||
<p class="pf-c-form__helper-text pf-m-error">
|
||||
{{ form.non_field_errors }}
|
||||
</p>
|
||||
@ -13,7 +13,7 @@
|
||||
{% if field.field.widget|fieldtype == 'HiddenInput' %}
|
||||
{{ field }}
|
||||
{% else %}
|
||||
<div class="pf-c-form__group {% if field.errors %} has-error {% endif %}">
|
||||
<div class="pf-c-form__group">
|
||||
{% if field.field.widget|fieldtype == 'RadioSelect' %}
|
||||
<label class="pf-c-form__label" {% if field.field.required %}class="required" {% endif %}
|
||||
for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||
|
@ -1,42 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
<div class="pf-c-toolbar__item pf-m-pagination ">
|
||||
<div class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md">
|
||||
<div class="pf-c-pagination pf-m-compact pf-m-compact pf-m-hidden pf-m-visible-on-md">
|
||||
<div class="pf-c-options-menu">
|
||||
<div class="pf-c-options-menu__toggle pf-m-text pf-m-plain">
|
||||
<span class="pf-c-options-menu__toggle-text">
|
||||
{% blocktrans with start_index=page_obj.start_index end_index=page_obj.end_index total_items=paginator.count %}
|
||||
{{ start_index }} - {{ end_index }} of {{ total_items }}
|
||||
{% endblocktrans %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="pf-c-pagination__nav" aria-label="Pagination">
|
||||
<div class="pf-c-pagination__nav-control pf-m-prev">
|
||||
<a class="pf-c-button pf-m-plain"
|
||||
{% if page_obj.has_previous %}
|
||||
href="{{ request.path }}?{% query_transform page=page_obj.previous_page_number %}"
|
||||
{% else %}
|
||||
disabled
|
||||
{% endif %}
|
||||
aria-label="{% trans 'Go to previous page' %}">
|
||||
<i class="fas fa-angle-left" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-pagination__nav-control pf-m-next">
|
||||
<a class="pf-c-button pf-m-plain"
|
||||
{% if page_obj.has_next %}
|
||||
href="{{ request.path }}?{% query_transform page=page_obj.next_page_number %}"
|
||||
{% else %}
|
||||
disabled
|
||||
{% endif %}
|
||||
aria-label="{% trans 'Go to next page' %}">
|
||||
<i class="fas fa-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,13 +0,0 @@
|
||||
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<form class="pf-c-input-group" method="GET">
|
||||
{# include page data for pagination #}
|
||||
<input type="hidden" name="page" value="{{ page_obj.number }}">
|
||||
<input class="pf-c-form-control" name="search" type="search" placeholder="Search..." value="{{ request.GET.search }}">
|
||||
<button class="pf-c-button pf-m-control" type="submit">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,11 @@
|
||||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<script src="{% static 'dist/main.js' %}?v={{ ak_version }}" type="module"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<ak-interface-admin></ak-interface-admin>
|
||||
{% endblock %}
|
||||
|
@ -13,7 +13,7 @@
|
||||
<p>{% trans "Configure settings relevant to your user profile." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<ak-tabs>
|
||||
<ak-tabs vertical="true" style="height: 100%;">
|
||||
<section slot="page-1" data-tab-title="{% trans 'User details' %}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
@ -24,9 +24,7 @@
|
||||
</div>
|
||||
</section>
|
||||
<section slot="page-2" data-tab-title="{% trans 'Tokens' %}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<ak-site-shell url="{% url 'authentik_core:user-tokens' %}">
|
||||
<div slot="body"></div>
|
||||
</ak-site-shell>
|
||||
<ak-token-user-list></ak-token-user-list>
|
||||
</section>
|
||||
{% user_stages as user_stages_loc %}
|
||||
{% for stage, stage_link in user_stages_loc.items %}
|
||||
@ -41,8 +39,8 @@
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% user_sources as user_sources_loc %}
|
||||
{% for source, source_link in user_sources_loc.item %}
|
||||
<section slot="page-{{ source.pk }}" data-tab-title="{{ source|verbose_name }}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
{% for source, source_link in user_sources_loc.items %}
|
||||
<section slot="page-{{ source.pk }}" data-tab-title="{{ source.name }}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
<ak-site-shell url="{{ source_link }}">
|
||||
|
@ -1,100 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||
<p>{% trans "Tokens can be used to access authentik's API." %}</p>
|
||||
</div>
|
||||
{% if object_list %}
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
{% include 'partials/toolbar_search.html' %}
|
||||
<div class="pf-c-toolbar__bulk-select">
|
||||
<ak-modal-button href="{% url 'authentik_core:user-tokens-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-xl" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th role="columnheader" scope="col">{% trans 'Identifier' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Expires?' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Expiry Date' %}</th>
|
||||
<th role="columnheader" scope="col">{% trans 'Description' %}</th>
|
||||
<th role="cell"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody role="rowgroup">
|
||||
{% for token in object_list %}
|
||||
<tr role="row">
|
||||
<th role="columnheader">
|
||||
<div>{{ token.identifier }}</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ token.expiring|yesno:"Yes,No" }}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{% if not token.expiring %}
|
||||
-
|
||||
{% else %}
|
||||
{{ token.expires }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ token.description }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<ak-modal-button href="{% url 'authentik_core:user-tokens-update' identifier=token.identifier %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
{% trans 'Edit' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_core:user-tokens-delete' identifier=token.identifier %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-token-copy-button identifier="{{ token.identifier }}">
|
||||
{% trans 'Copy token' %}
|
||||
</ak-token-copy-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pf-c-pagination pf-m-bottom">
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pf-c-empty-state">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans 'No Tokens.' %}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
{% trans 'Currently no tokens exist. Click the button below to create one.' %}
|
||||
</div>
|
||||
<ak-modal-button href="{% url 'authentik_core:user-tokens-create' %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Create' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
"""impersonation tests"""
|
||||
from django.shortcuts import reverse
|
||||
from django.test.testcases import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from authentik.core.models import User
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
import string
|
||||
from random import SystemRandom
|
||||
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from authentik.core.models import User
|
||||
|
||||
@ -28,9 +28,3 @@ class TestOverviewViews(TestCase):
|
||||
self.assertEqual(
|
||||
self.client.get(reverse("authentik_core:shell")).status_code, 200
|
||||
)
|
||||
|
||||
def test_overview(self):
|
||||
"""Test overview"""
|
||||
self.assertEqual(
|
||||
self.client.get(reverse("authentik_core:overview")).status_code, 200
|
||||
)
|
||||
|
@ -2,8 +2,8 @@
|
||||
import string
|
||||
from random import SystemRandom
|
||||
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from authentik.core.models import User
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user