mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-29 13:15:46 +00:00
Compare commits
75 Commits
feat/cooki
...
v3.6.1-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3215bb6baa | ||
|
|
a11aba72d8 | ||
|
|
10d1b48505 | ||
|
|
f73eb9571f | ||
|
|
da2877a682 | ||
|
|
33cbfef02a | ||
|
|
c1a6428ed3 | ||
|
|
2ee7932cba | ||
|
|
fe440a6f2e | ||
|
|
0ace88a877 | ||
|
|
476ed6964d | ||
|
|
b3dca0429f | ||
|
|
9e4b68112c | ||
|
|
364f0e221e | ||
|
|
09635666aa | ||
|
|
9f02710114 | ||
|
|
64bdab5e5b | ||
|
|
0f4a6b5924 | ||
|
|
c662b9e222 | ||
|
|
a4722db7d7 | ||
|
|
f48bb65d7b | ||
|
|
7e604419ab | ||
|
|
60cd0a216f | ||
|
|
ec6e3aa718 | ||
|
|
6dc57ddf0f | ||
|
|
f780e81ec2 | ||
|
|
8b70ab47a4 | ||
|
|
b800359bb2 | ||
|
|
6ec8c9766c | ||
|
|
4524e3322c | ||
|
|
1941de1125 | ||
|
|
49c4c7a455 | ||
|
|
c10bff55de | ||
|
|
7640e956c2 | ||
|
|
4c18a4c44d | ||
|
|
87a5c0f3f1 | ||
|
|
84d4c84ed2 | ||
|
|
9008b67f7d | ||
|
|
47980a3dd0 | ||
|
|
6cbb9e93c0 | ||
|
|
f3ec4baf3c | ||
|
|
b5799da703 | ||
|
|
80b1820333 | ||
|
|
aed29d2923 | ||
|
|
3397e2aa8e | ||
|
|
ee83c177f4 | ||
|
|
9eb296f146 | ||
|
|
7ac25f0a2a | ||
|
|
6af079d369 | ||
|
|
523267e55b | ||
|
|
e96a2bcaec | ||
|
|
8494fbec8b | ||
|
|
4a2aa6ef43 | ||
|
|
07a5429b6f | ||
|
|
ad4275bee5 | ||
|
|
3665c3a283 | ||
|
|
38fdb38b92 | ||
|
|
bc0a38a857 | ||
|
|
f2c81b6a5c | ||
|
|
34c8d16c7d | ||
|
|
75f07a9d7f | ||
|
|
74f1c10826 | ||
|
|
168467d648 | ||
|
|
d527900991 | ||
|
|
f3acec7daf | ||
|
|
bbaa036d85 | ||
|
|
fc73e25d51 | ||
|
|
e50ffe7907 | ||
|
|
dda6a8c47f | ||
|
|
eba2df3dd9 | ||
|
|
11731f78d1 | ||
|
|
3a7b71ae3e | ||
|
|
3c3bd719db | ||
|
|
a6aa97bcfa | ||
|
|
1a7b6cfb99 |
@@ -30,3 +30,4 @@ APP_TITLE=Tinyauth SSO
|
|||||||
FORGOT_PASSWORD_MESSAGE=Some message about resetting the password
|
FORGOT_PASSWORD_MESSAGE=Some message about resetting the password
|
||||||
OAUTH_AUTO_REDIRECT=none
|
OAUTH_AUTO_REDIRECT=none
|
||||||
BACKGROUND_IMAGE=some_image_url
|
BACKGROUND_IMAGE=some_image_url
|
||||||
|
GENERIC_SKIP_SSL=false
|
||||||
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -39,4 +39,9 @@ jobs:
|
|||||||
cp -r frontend/dist internal/assets/dist
|
cp -r frontend/dist internal/assets/dist
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go test -v ./...
|
run: go test -coverprofile=coverage.txt -v ./...
|
||||||
|
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
uses: codecov/codecov-action@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
6
.github/workflows/nightly.yml
vendored
6
.github/workflows/nightly.yml
vendored
@@ -1,6 +1,8 @@
|
|||||||
name: Nightly Release
|
name: Nightly Release
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
create-release:
|
create-release:
|
||||||
@@ -79,6 +81,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -r frontend/dist internal/assets/dist
|
cp -r frontend/dist internal/assets/dist
|
||||||
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64
|
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64
|
||||||
|
env:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -123,6 +127,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -r frontend/dist internal/assets/dist
|
cp -r frontend/dist internal/assets/dist
|
||||||
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64
|
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64
|
||||||
|
env:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Generate metadata
|
- name: Generate metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
run: |
|
run: |
|
||||||
echo "VERSION=nightly" >> "$GITHUB_OUTPUT"
|
echo "VERSION=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
|
||||||
echo "COMMIT_HASH=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
echo "COMMIT_HASH=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
echo "BUILD_TIMESTAMP=$(date '+%Y-%m-%dT%H:%M:%S')" >> "$GITHUB_OUTPUT"
|
echo "BUILD_TIMESTAMP=$(date '+%Y-%m-%dT%H:%M:%S')" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
@@ -59,6 +59,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -r frontend/dist internal/assets/dist
|
cp -r frontend/dist internal/assets/dist
|
||||||
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64
|
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-amd64
|
||||||
|
env:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -100,6 +102,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp -r frontend/dist internal/assets/dist
|
cp -r frontend/dist internal/assets/dist
|
||||||
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64
|
go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${{ needs.generate-metadata.outputs.VERSION }} -X tinyauth/internal/constants.CommitHash=${{ needs.generate-metadata.outputs.COMMIT_HASH }} -X tinyauth/internal/constants.BuildTimestamp=${{ needs.generate-metadata.outputs.BUILD_TIMESTAMP }}" -o tinyauth-arm64
|
||||||
|
env:
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -11,11 +11,7 @@ docker-compose.test*
|
|||||||
users.txt
|
users.txt
|
||||||
|
|
||||||
# secret test file
|
# secret test file
|
||||||
secret.txt
|
secret*
|
||||||
secret_oauth.txt
|
|
||||||
|
|
||||||
# vscode
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# apple stuff
|
# apple stuff
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Connect to server",
|
||||||
|
"type": "go",
|
||||||
|
"request": "attach",
|
||||||
|
"mode": "remote",
|
||||||
|
"remotePath": "/tinyauth",
|
||||||
|
"port": 4000,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"debugAdapter": "legacy"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
Dockerfile
18
Dockerfile
@@ -1,10 +1,5 @@
|
|||||||
# Arguments
|
|
||||||
ARG VERSION
|
|
||||||
ARG COMMIT_HASH
|
|
||||||
ARG BUILD_TIMESTAMP
|
|
||||||
|
|
||||||
# Site builder
|
# Site builder
|
||||||
FROM oven/bun:1.2.12-alpine AS frontend-builder
|
FROM oven/bun:1.2.18-alpine AS frontend-builder
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
|
|
||||||
@@ -27,6 +22,10 @@ RUN bun run build
|
|||||||
# Builder
|
# Builder
|
||||||
FROM golang:1.24-alpine3.21 AS builder
|
FROM golang:1.24-alpine3.21 AS builder
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ARG COMMIT_HASH
|
||||||
|
ARG BUILD_TIMESTAMP
|
||||||
|
|
||||||
WORKDIR /tinyauth
|
WORKDIR /tinyauth
|
||||||
|
|
||||||
COPY go.mod ./
|
COPY go.mod ./
|
||||||
@@ -39,10 +38,10 @@ COPY ./cmd ./cmd
|
|||||||
COPY ./internal ./internal
|
COPY ./internal ./internal
|
||||||
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
|
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
|
||||||
|
|
||||||
RUN go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${VERSION} -X tinyauth/internal/constants.CommitHash=${COMMIT_HASH} -X tinyauth/internal/constants.BuildTimestamp=${BUILD_TIMESTAMP}"
|
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X tinyauth/internal/constants.Version=${VERSION} -X tinyauth/internal/constants.CommitHash=${COMMIT_HASH} -X tinyauth/internal/constants.BuildTimestamp=${BUILD_TIMESTAMP}"
|
||||||
|
|
||||||
# Runner
|
# Runner
|
||||||
FROM alpine:3.21 AS runner
|
FROM alpine:3.22 AS runner
|
||||||
|
|
||||||
WORKDIR /tinyauth
|
WORKDIR /tinyauth
|
||||||
|
|
||||||
@@ -52,7 +51,4 @@ COPY --from=builder /tinyauth/tinyauth ./
|
|||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
HEALTHCHECK --interval=10s --timeout=5s \
|
|
||||||
CMD curl -f http://localhost:3000/api/healthcheck || exit 1
|
|
||||||
|
|
||||||
ENTRYPOINT ["./tinyauth"]
|
ENTRYPOINT ["./tinyauth"]
|
||||||
25
README.md
25
README.md
@@ -14,39 +14,36 @@
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic provider to all of your docker apps. It is designed for traefik but it can be extended to work with other reverse proxies like caddy and nginx.
|
Tinyauth is a simple authentication middleware that adds a simple login screen or OAuth with Google, Github and any provider to all of your docker apps. It supports all the popular proxies like Traefik, Nginx and Caddy.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Tinyauth is in active development and configuration may change often. Please make sure to carefully read the release notes before updating.
|
> Tinyauth is in active development and configuration may change often. Please make sure to carefully read the release notes before updating.
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Tinyauth is intended for homelab use only and it is not made for production use cases. If you are looking for something production ready please use [authentik](https://goauthentik.io) instead.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
You can easily get started with tinyauth by following the guide in the [documentation](https://tinyauth.app/docs/getting-started.html). There is also an available [docker compose file](./docker-compose.example.yml) that has traefik, whoami and tinyauth to demonstrate its capabilities.
|
You can easily get started with Tinyauth by following the guide in the [documentation](https://tinyauth.app/docs/getting-started.html). There is also an available [docker compose](./docker-compose.example.yml) file that has Traefik, Whoami and Tinyauth to demonstrate its capabilities.
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
If you are still not sure if tinyauth suits your needs you can try out the [demo](https://demo.tinyauth.app). The default username is `user` and the default password is `password`.
|
If you are still not sure if Tinyauth suits your needs you can try out the [demo](https://demo.tinyauth.app). The default username is `user` and the default password is `password`.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
You can find documentation and guides on all of the available configuration of tinyauth in the [website](https://tinyauth.app).
|
You can find documentation and guides on all of the available configuration of Tinyauth in the [website](https://tinyauth.app).
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
|
|
||||||
I just made a Discord server for tinyauth! It is not only for tinyauth but general self-hosting and homelabbing. [See you there!](https://discord.gg/eHzVaCzRRd).
|
Tinyauth has a [discord](https://discord.gg/eHzVaCzRRd) server. Feel free to hop in to chat about self-hosting, homelabs and of course Tinyauth. See you there!
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
All contributions to the codebase are welcome! If you have any recommendations on how to improve security or find a security issue in tinyauth please open an issue or pull request so it can be fixed as soon as possible!
|
All contributions to the codebase are welcome! If you have any free time feel free to pick up an [issue](https://github.com/steveiliop56/tinyauth/issues) or add your own missing features. Make sure to check out the [contributing guide](./CONTRIBUTING.md) for instructions on how to get the development server up and running.
|
||||||
|
|
||||||
## Localization
|
## Localization
|
||||||
|
|
||||||
If you would like to help translating the project in more languages you can do so by visiting the [Crowdin](https://crowdin.com/project/tinyauth) page.
|
If you would like to help translate Tinyauth into more languages, visit the [Crowdin](https://crowdin.com/project/tinyauth) page.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -54,18 +51,16 @@ Tinyauth is licensed under the GNU General Public License v3.0. TL;DR — You ma
|
|||||||
|
|
||||||
## Sponsors
|
## Sponsors
|
||||||
|
|
||||||
Thanks a lot to the following people for providing me with more coffee:
|
A big thank you to the following people for providing me with more coffee:
|
||||||
|
|
||||||
<!-- sponsors --><a href="https://github.com/erwinkramer"><img src="https://github.com/erwinkramer.png" width="64px" alt="User avatar: erwinkramer" /></a> <a href="https://github.com/nicotsx"><img src="https://github.com/nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a> <a href="https://github.com/SimpleHomelab"><img src="https://github.com/SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a> <a href="https://github.com/jmadden91"><img src="https://github.com/jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a> <a href="https://github.com/tribor"><img src="https://github.com/tribor.png" width="64px" alt="User avatar: tribor" /></a> <a href="https://github.com/eliasbenb"><img src="https://github.com/eliasbenb.png" width="64px" alt="User avatar: eliasbenb" /></a> <!-- sponsors -->
|
<!-- sponsors --><a href="https://github.com/erwinkramer"><img src="https://github.com/erwinkramer.png" width="64px" alt="User avatar: erwinkramer" /></a> <a href="https://github.com/nicotsx"><img src="https://github.com/nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a> <a href="https://github.com/SimpleHomelab"><img src="https://github.com/SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a> <a href="https://github.com/jmadden91"><img src="https://github.com/jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a> <a href="https://github.com/tribor"><img src="https://github.com/tribor.png" width="64px" alt="User avatar: tribor" /></a> <a href="https://github.com/eliasbenb"><img src="https://github.com/eliasbenb.png" width="64px" alt="User avatar: eliasbenb" /></a> <a href="https://github.com/afunworm"><img src="https://github.com/afunworm.png" width="64px" alt="User avatar: afunworm" /></a> <!-- sponsors -->
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
Credits for the logo of this app go to:
|
|
||||||
|
|
||||||
- **Freepik** for providing the police hat and badge.
|
- **Freepik** for providing the police hat and badge.
|
||||||
- **Renee French** for the original gopher logo.
|
- **Renee French** for the original gopher logo.
|
||||||
- **Coderabbit AI** for providing free AI code reviews.
|
- **Coderabbit AI** for providing free AI code reviews.
|
||||||
- **Kurt Cotoaga** for providing the bacgkround image of the app.
|
- **Syrhu** for providing the background image of the app.
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
Please always use the latest available Tinyauth version which can be found [here](https://github.com/steveiliop56/tinyauth/releases/latest). Older versions (especially major) may contain security issues which I cannot go back and fix.
|
It is recommended to use the [latest](https://github.com/steveiliop56/tinyauth/releases/latest) available version of tinyauth. This is because it includes security fixes, new features and dependency updates. Older versions, especially major ones, are not supported and won't receive security or patch updates.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Due to the nature of this app, it needs to be secure. If you find any security issues in the OAuth or login flow of the app please contact me at <steve@doesmycode.work> and include a concise description of the issue. Please do not use the issues section for reporting major security issues.
|
Due to the nature of this app, it needs to be secure. If you discover any security issues or vulnerabilities in the app please contact me as soon as possible at <steve@doesmycode.work>. Please do not use the issues section to report security issues as I won't be able to patch them in time and they may get exploited by malicious actors.
|
||||||
|
|||||||
6
air.toml
6
air.toml
@@ -2,9 +2,9 @@ root = "/tinyauth"
|
|||||||
tmp_dir = "tmp"
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
pre_cmd = ["mkdir -p internal/assets/dist", "echo 'backend running' > internal/assets/dist/index.html"]
|
pre_cmd = ["mkdir -p internal/assets/dist", "echo 'backend running' > internal/assets/dist/index.html", "go install github.com/go-delve/delve/cmd/dlv@v1.25.0"]
|
||||||
cmd = "go build -o ./tmp/tinyauth ."
|
cmd = "CGO_ENABLED=0 go build -gcflags=\"all=-N -l\" -o tmp/tinyauth ."
|
||||||
bin = "tmp/tinyauth"
|
bin = "/go/bin/dlv --listen :4000 --headless=true --api-version=2 --accept-multiclient --log=true exec tmp/tinyauth --continue"
|
||||||
include_ext = ["go"]
|
include_ext = ["go"]
|
||||||
exclude_dir = ["internal/assets/dist"]
|
exclude_dir = ["internal/assets/dist"]
|
||||||
exclude_regex = [".*_test\\.go"]
|
exclude_regex = [".*_test\\.go"]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"embeds": [
|
"embeds": [
|
||||||
{
|
{
|
||||||
"title": "Welcome to Tinyauth Discord!",
|
"title": "Welcome to Tinyauth Discord!",
|
||||||
"description": "Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps.\n\n**Information**\n\n• Github: <https://github.com/steveiliop56/tinyauth>\n• Website: <https://tinyauth.app>",
|
"description": "Tinyauth is a simple authentication middleware that adds a simple login screen or OAuth with Google, Github and any provider to all of your docker apps. It supports all the popular proxies like Traefik, Nginx and Caddy.\n\n**Information**\n\n• Github: <https://github.com/steveiliop56/tinyauth>\n• Website: <https://tinyauth.app>",
|
||||||
"url": "https://tinyauth.app",
|
"url": "https://tinyauth.app",
|
||||||
"color": 7002085,
|
"color": 7002085,
|
||||||
"author": {
|
"author": {
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
"footer": {
|
"footer": {
|
||||||
"text": "Updated at"
|
"text": "Updated at"
|
||||||
},
|
},
|
||||||
"timestamp": "2025-03-10T19:00:00.000Z",
|
"timestamp": "2025-06-06T12:25:27.629Z",
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"url": "https://github.com/steveiliop56/tinyauth/blob/main/frontend/public/logo.png?raw=true"
|
"url": "https://github.com/steveiliop56/tinyauth/blob/main/assets/logo.png?raw=true"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 4.5 MiB |
91
cmd/root.go
91
cmd/root.go
@@ -8,13 +8,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
totpCmd "tinyauth/cmd/totp"
|
totpCmd "tinyauth/cmd/totp"
|
||||||
userCmd "tinyauth/cmd/user"
|
userCmd "tinyauth/cmd/user"
|
||||||
"tinyauth/internal/api"
|
|
||||||
"tinyauth/internal/auth"
|
"tinyauth/internal/auth"
|
||||||
"tinyauth/internal/constants"
|
"tinyauth/internal/constants"
|
||||||
"tinyauth/internal/docker"
|
"tinyauth/internal/docker"
|
||||||
"tinyauth/internal/handlers"
|
"tinyauth/internal/handlers"
|
||||||
"tinyauth/internal/hooks"
|
"tinyauth/internal/hooks"
|
||||||
|
"tinyauth/internal/ldap"
|
||||||
"tinyauth/internal/providers"
|
"tinyauth/internal/providers"
|
||||||
|
"tinyauth/internal/server"
|
||||||
"tinyauth/internal/types"
|
"tinyauth/internal/types"
|
||||||
"tinyauth/internal/utils"
|
"tinyauth/internal/utils"
|
||||||
|
|
||||||
@@ -58,10 +59,6 @@ var rootCmd = &cobra.Command{
|
|||||||
users, err := utils.GetUsers(config.Users, config.UsersFile)
|
users, err := utils.GetUsers(config.Users, config.UsersFile)
|
||||||
HandleError(err, "Failed to parse users")
|
HandleError(err, "Failed to parse users")
|
||||||
|
|
||||||
if len(users) == 0 && !utils.OAuthConfigured(config) {
|
|
||||||
HandleError(errors.New("no users or OAuth configured"), "No users or OAuth configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get domain
|
// Get domain
|
||||||
log.Debug().Msg("Getting domain")
|
log.Debug().Msg("Getting domain")
|
||||||
domain, err := utils.GetUpperDomain(config.AppURL)
|
domain, err := utils.GetUpperDomain(config.AppURL)
|
||||||
@@ -74,6 +71,15 @@ var rootCmd = &cobra.Command{
|
|||||||
csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId)
|
csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId)
|
||||||
redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId)
|
redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId)
|
||||||
|
|
||||||
|
// Generate HMAC and encryption secrets
|
||||||
|
log.Debug().Msg("Deriving HMAC and encryption secrets")
|
||||||
|
|
||||||
|
hmacSecret, err := utils.DeriveKey(config.Secret, "hmac")
|
||||||
|
HandleError(err, "Failed to derive HMAC secret")
|
||||||
|
|
||||||
|
encryptionSecret, err := utils.DeriveKey(config.Secret, "encryption")
|
||||||
|
HandleError(err, "Failed to derive encryption secret")
|
||||||
|
|
||||||
// Create OAuth config
|
// Create OAuth config
|
||||||
oauthConfig := types.OAuthConfig{
|
oauthConfig := types.OAuthConfig{
|
||||||
GithubClientId: config.GithubClientId,
|
GithubClientId: config.GithubClientId,
|
||||||
@@ -86,6 +92,7 @@ var rootCmd = &cobra.Command{
|
|||||||
GenericAuthURL: config.GenericAuthURL,
|
GenericAuthURL: config.GenericAuthURL,
|
||||||
GenericTokenURL: config.GenericTokenURL,
|
GenericTokenURL: config.GenericTokenURL,
|
||||||
GenericUserURL: config.GenericUserURL,
|
GenericUserURL: config.GenericUserURL,
|
||||||
|
GenericSkipSSL: config.GenericSkipSSL,
|
||||||
AppURL: config.AppURL,
|
AppURL: config.AppURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,8 +111,8 @@ var rootCmd = &cobra.Command{
|
|||||||
RedirectCookieName: redirectCookieName,
|
RedirectCookieName: redirectCookieName,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create api config
|
// Create server config
|
||||||
apiConfig := types.APIConfig{
|
serverConfig := types.ServerConfig{
|
||||||
Port: config.Port,
|
Port: config.Port,
|
||||||
Address: config.Address,
|
Address: config.Address,
|
||||||
}
|
}
|
||||||
@@ -114,13 +121,14 @@ var rootCmd = &cobra.Command{
|
|||||||
authConfig := types.AuthConfig{
|
authConfig := types.AuthConfig{
|
||||||
Users: users,
|
Users: users,
|
||||||
OauthWhitelist: config.OAuthWhitelist,
|
OauthWhitelist: config.OAuthWhitelist,
|
||||||
Secret: config.Secret,
|
|
||||||
CookieSecure: config.CookieSecure,
|
CookieSecure: config.CookieSecure,
|
||||||
SessionExpiry: config.SessionExpiry,
|
SessionExpiry: config.SessionExpiry,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
LoginTimeout: config.LoginTimeout,
|
LoginTimeout: config.LoginTimeout,
|
||||||
LoginMaxRetries: config.LoginMaxRetries,
|
LoginMaxRetries: config.LoginMaxRetries,
|
||||||
SessionCookieName: sessionCookieName,
|
SessionCookieName: sessionCookieName,
|
||||||
|
HMACSecret: hmacSecret,
|
||||||
|
EncryptionSecret: encryptionSecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create hooks config
|
// Create hooks config
|
||||||
@@ -129,36 +137,55 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create docker service
|
// Create docker service
|
||||||
docker := docker.NewDocker()
|
docker, err := docker.NewDocker()
|
||||||
|
|
||||||
// Initialize docker
|
|
||||||
err = docker.Init()
|
|
||||||
HandleError(err, "Failed to initialize docker")
|
HandleError(err, "Failed to initialize docker")
|
||||||
|
|
||||||
|
// Create LDAP service if configured
|
||||||
|
var ldapService *ldap.LDAP
|
||||||
|
|
||||||
|
if config.LdapAddress != "" {
|
||||||
|
log.Info().Msg("Using LDAP for authentication")
|
||||||
|
|
||||||
|
ldapConfig := types.LdapConfig{
|
||||||
|
Address: config.LdapAddress,
|
||||||
|
BindDN: config.LdapBindDN,
|
||||||
|
BindPassword: config.LdapBindPassword,
|
||||||
|
BaseDN: config.LdapBaseDN,
|
||||||
|
Insecure: config.LdapInsecure,
|
||||||
|
SearchFilter: config.LdapSearchFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create LDAP service
|
||||||
|
ldapService, err = ldap.NewLDAP(ldapConfig)
|
||||||
|
HandleError(err, "Failed to create LDAP service")
|
||||||
|
} else {
|
||||||
|
log.Info().Msg("LDAP not configured, using local users or OAuth")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any users configured
|
||||||
|
if len(users) == 0 && !utils.OAuthConfigured(config) && ldapService == nil {
|
||||||
|
HandleError(errors.New("err no users"), "Unable to find a source of users")
|
||||||
|
}
|
||||||
|
|
||||||
// Create auth service
|
// Create auth service
|
||||||
auth := auth.NewAuth(authConfig, docker)
|
auth := auth.NewAuth(authConfig, docker, ldapService)
|
||||||
|
|
||||||
// Create OAuth providers service
|
// Create OAuth providers service
|
||||||
providers := providers.NewProviders(oauthConfig)
|
providers := providers.NewProviders(oauthConfig)
|
||||||
|
|
||||||
// Initialize providers
|
|
||||||
providers.Init()
|
|
||||||
|
|
||||||
// Create hooks service
|
// Create hooks service
|
||||||
hooks := hooks.NewHooks(hooksConfig, auth, providers)
|
hooks := hooks.NewHooks(hooksConfig, auth, providers)
|
||||||
|
|
||||||
// Create handlers
|
// Create handlers
|
||||||
handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
|
handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
|
||||||
|
|
||||||
// Create API
|
// Create server
|
||||||
api := api.NewAPI(apiConfig, handlers)
|
srv, err := server.NewServer(serverConfig, handlers)
|
||||||
|
HandleError(err, "Failed to create server")
|
||||||
|
|
||||||
// Setup routes
|
// Start server
|
||||||
api.Init()
|
err = srv.Start()
|
||||||
api.SetupRoutes()
|
HandleError(err, "Failed to start server")
|
||||||
|
|
||||||
// Start
|
|
||||||
api.Run()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +234,7 @@ func init() {
|
|||||||
rootCmd.Flags().String("generic-token-url", "", "Generic OAuth token URL.")
|
rootCmd.Flags().String("generic-token-url", "", "Generic OAuth token URL.")
|
||||||
rootCmd.Flags().String("generic-user-url", "", "Generic OAuth user info URL.")
|
rootCmd.Flags().String("generic-user-url", "", "Generic OAuth user info URL.")
|
||||||
rootCmd.Flags().String("generic-name", "Generic", "Generic OAuth provider name.")
|
rootCmd.Flags().String("generic-name", "Generic", "Generic OAuth provider name.")
|
||||||
|
rootCmd.Flags().Bool("generic-skip-ssl", false, "Skip SSL verification for the generic OAuth provider.")
|
||||||
rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.")
|
rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.")
|
||||||
rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.")
|
rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.")
|
||||||
rootCmd.Flags().String("oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)")
|
rootCmd.Flags().String("oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)")
|
||||||
@@ -215,8 +243,14 @@ func init() {
|
|||||||
rootCmd.Flags().Int("login-max-retries", 5, "Maximum login attempts before timeout (0 to disable).")
|
rootCmd.Flags().Int("login-max-retries", 5, "Maximum login attempts before timeout (0 to disable).")
|
||||||
rootCmd.Flags().Int("log-level", 1, "Log level.")
|
rootCmd.Flags().Int("log-level", 1, "Log level.")
|
||||||
rootCmd.Flags().String("app-title", "Tinyauth", "Title of the app.")
|
rootCmd.Flags().String("app-title", "Tinyauth", "Title of the app.")
|
||||||
rootCmd.Flags().String("forgot-password-message", "You can reset your password by changing the `USERS` environment variable.", "Message to show on the forgot password page.")
|
rootCmd.Flags().String("forgot-password-message", "", "Message to show on the forgot password page.")
|
||||||
rootCmd.Flags().String("background-image", "/background.jpg", "Background image URL for the login page.")
|
rootCmd.Flags().String("background-image", "/background.jpg", "Background image URL for the login page.")
|
||||||
|
rootCmd.Flags().String("ldap-address", "", "LDAP server address (e.g. ldap://localhost:389).")
|
||||||
|
rootCmd.Flags().String("ldap-bind-dn", "", "LDAP bind DN (e.g. uid=user,dc=example,dc=com).")
|
||||||
|
rootCmd.Flags().String("ldap-bind-password", "", "LDAP bind password.")
|
||||||
|
rootCmd.Flags().String("ldap-base-dn", "", "LDAP base DN (e.g. dc=example,dc=com).")
|
||||||
|
rootCmd.Flags().Bool("ldap-insecure", false, "Skip certificate verification for the LDAP server.")
|
||||||
|
rootCmd.Flags().String("ldap-search-filter", "(uid=%s)", "LDAP search filter for user lookup.")
|
||||||
|
|
||||||
// Bind flags to environment
|
// Bind flags to environment
|
||||||
viper.BindEnv("port", "PORT")
|
viper.BindEnv("port", "PORT")
|
||||||
@@ -241,6 +275,7 @@ func init() {
|
|||||||
viper.BindEnv("generic-token-url", "GENERIC_TOKEN_URL")
|
viper.BindEnv("generic-token-url", "GENERIC_TOKEN_URL")
|
||||||
viper.BindEnv("generic-user-url", "GENERIC_USER_URL")
|
viper.BindEnv("generic-user-url", "GENERIC_USER_URL")
|
||||||
viper.BindEnv("generic-name", "GENERIC_NAME")
|
viper.BindEnv("generic-name", "GENERIC_NAME")
|
||||||
|
viper.BindEnv("generic-skip-ssl", "GENERIC_SKIP_SSL")
|
||||||
viper.BindEnv("disable-continue", "DISABLE_CONTINUE")
|
viper.BindEnv("disable-continue", "DISABLE_CONTINUE")
|
||||||
viper.BindEnv("oauth-whitelist", "OAUTH_WHITELIST")
|
viper.BindEnv("oauth-whitelist", "OAUTH_WHITELIST")
|
||||||
viper.BindEnv("oauth-auto-redirect", "OAUTH_AUTO_REDIRECT")
|
viper.BindEnv("oauth-auto-redirect", "OAUTH_AUTO_REDIRECT")
|
||||||
@@ -251,6 +286,12 @@ func init() {
|
|||||||
viper.BindEnv("login-max-retries", "LOGIN_MAX_RETRIES")
|
viper.BindEnv("login-max-retries", "LOGIN_MAX_RETRIES")
|
||||||
viper.BindEnv("forgot-password-message", "FORGOT_PASSWORD_MESSAGE")
|
viper.BindEnv("forgot-password-message", "FORGOT_PASSWORD_MESSAGE")
|
||||||
viper.BindEnv("background-image", "BACKGROUND_IMAGE")
|
viper.BindEnv("background-image", "BACKGROUND_IMAGE")
|
||||||
|
viper.BindEnv("ldap-address", "LDAP_ADDRESS")
|
||||||
|
viper.BindEnv("ldap-bind-dn", "LDAP_BIND_DN")
|
||||||
|
viper.BindEnv("ldap-bind-password", "LDAP_BIND_PASSWORD")
|
||||||
|
viper.BindEnv("ldap-base-dn", "LDAP_BASE_DN")
|
||||||
|
viper.BindEnv("ldap-insecure", "LDAP_INSECURE")
|
||||||
|
viper.BindEnv("ldap-search-filter", "LDAP_SEARCH_FILTER")
|
||||||
|
|
||||||
// Bind flags to viper
|
// Bind flags to viper
|
||||||
viper.BindPFlags(rootCmd.Flags())
|
viper.BindPFlags(rootCmd.Flags())
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
|
- 4000:4000
|
||||||
labels:
|
labels:
|
||||||
traefik.enable: true
|
traefik.enable: true
|
||||||
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth-backend:3000/api/auth/traefik
|
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth-backend:3000/api/auth/traefik
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM oven/bun:1.1.45-alpine
|
FROM oven/bun:1.2.16-alpine
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,49 +10,49 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.1.1",
|
||||||
"@radix-ui/react-label": "^2.1.6",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-select": "^2.2.2",
|
"@radix-ui/react-select": "^2.2.5",
|
||||||
"@radix-ui/react-separator": "^1.1.4",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.2",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"@tanstack/react-query": "^5.75.6",
|
"@tanstack/react-query": "^5.82.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.10.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dompurify": "^3.2.5",
|
"dompurify": "^3.2.6",
|
||||||
"i18next": "^25.0.2",
|
"i18next": "^25.3.2",
|
||||||
"i18next-browser-languagedetector": "^8.0.5",
|
"i18next-browser-languagedetector": "^8.2.0",
|
||||||
"i18next-resources-to-backend": "^1.2.1",
|
"i18next-resources-to-backend": "^1.2.1",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"lucide-react": "^0.503.0",
|
"lucide-react": "^0.525.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-hook-form": "^7.56.3",
|
"react-hook-form": "^7.60.0",
|
||||||
"react-i18next": "^15.5.1",
|
"react-i18next": "^15.6.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-router": "^7.5.3",
|
"react-router": "^7.6.3",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.6",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.11",
|
||||||
"zod": "^3.24.4"
|
"zod": "^4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.22.0",
|
"@eslint/js": "^9.30.1",
|
||||||
"@tanstack/eslint-plugin-query": "^5.74.7",
|
"@tanstack/eslint-plugin-query": "^5.81.2",
|
||||||
"@types/node": "^22.15.3",
|
"@types/node": "^24.0.13",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
"eslint": "^9.22.0",
|
"eslint": "^9.30.1",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.19",
|
"eslint-plugin-react-refresh": "^0.4.19",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.3.0",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.6.2",
|
||||||
"tw-animate-css": "^1.2.8",
|
"tw-animate-css": "^1.3.5",
|
||||||
"typescript": "~5.7.2",
|
"typescript": "~5.8.3",
|
||||||
"typescript-eslint": "^8.26.1",
|
"typescript-eslint": "^8.36.0",
|
||||||
"vite": "^6.3.1"
|
"vite": "^7.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 588 KiB After Width: | Height: | Size: 530 KiB |
@@ -33,9 +33,9 @@ export const LoginForm = (props: Props) => {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="username"
|
name="username"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="mb-4">
|
<FormItem className="mb-4 gap-0">
|
||||||
<FormLabel>{t("loginUsername")}</FormLabel>
|
<FormLabel className="mb-2">{t("loginUsername")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl className="mb-1">
|
||||||
<Input
|
<Input
|
||||||
placeholder={t("loginUsername")}
|
placeholder={t("loginUsername")}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
@@ -50,8 +50,8 @@ export const LoginForm = (props: Props) => {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="mb-4">
|
<FormItem className="mb-4 gap-0">
|
||||||
<div className="relative">
|
<div className="relative mb-1">
|
||||||
<FormLabel className="mb-2">{t("loginPassword")}</FormLabel>
|
<FormLabel className="mb-2">{t("loginPassword")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
@@ -61,14 +61,14 @@ export const LoginForm = (props: Props) => {
|
|||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
|
||||||
<a
|
<a
|
||||||
href="/forgot-password"
|
href="/forgot-password"
|
||||||
className="text-muted-foreground text-sm absolute right-0 bottom-10"
|
className="text-muted-foreground text-sm absolute right-0 bottom-[2.565rem]" // 2.565 is *just* perfect
|
||||||
>
|
>
|
||||||
{t("forgotPasswordTitle")}
|
{t("forgotPasswordTitle")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useAppContext } from "@/context/app-context";
|
import { useAppContext } from "@/context/app-context";
|
||||||
import { LanguageSelector } from "../language/language";
|
import { LanguageSelector } from "../language/language";
|
||||||
|
import { Outlet } from "react-router";
|
||||||
|
|
||||||
export const Layout = ({ children }: { children: React.ReactNode }) => {
|
export const Layout = () => {
|
||||||
const { backgroundImage } = useAppContext();
|
const { backgroundImage } = useAppContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -14,7 +15,7 @@ export const Layout = ({ children }: { children: React.ReactNode }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
{children}
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ h4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply leading-7 [&:not(:first-child)]:mt-6;
|
@apply leading-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export const languages = {
|
|||||||
"tr-TR": "Türkçe",
|
"tr-TR": "Türkçe",
|
||||||
"uk-UA": "Українська",
|
"uk-UA": "Українська",
|
||||||
"vi-VN": "Tiếng Việt",
|
"vi-VN": "Tiếng Việt",
|
||||||
"zh-CN": "中文",
|
"zh-CN": "简体中文",
|
||||||
"zh-TW": "中文",
|
"zh-TW": "繁體中文(台灣)",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SupportedLanguage = keyof typeof languages;
|
export type SupportedLanguage = keyof typeof languages;
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "مرحبا بعودتك، قم بتسجيل الدخول باستخدام",
|
"loginTitle": "مرحبا بعودتك، ادخل باستخدام",
|
||||||
"loginDivider": "أو المتابعة بكلمة المرور",
|
"loginTitleSimple": "مرحبا بعودتك، سجل دخولك",
|
||||||
|
"loginDivider": "أو",
|
||||||
"loginUsername": "اسم المستخدم",
|
"loginUsername": "اسم المستخدم",
|
||||||
"loginPassword": "كلمة المرور",
|
"loginPassword": "كلمة المرور",
|
||||||
"loginSubmit": "تسجيل الدخول",
|
"loginSubmit": "تسجيل الدخول",
|
||||||
"loginFailTitle": "فشل تسجيل الدخول",
|
"loginFailTitle": "فشل تسجيل الدخول",
|
||||||
"loginFailSubtitle": "الرجاء التحقق من اسم المستخدم وكلمة المرور",
|
"loginFailSubtitle": "الرجاء التحقق من اسم المستخدم وكلمة المرور",
|
||||||
"loginFailRateLimit": "فشلت في تسجيل الدخول عدة مرات، الرجاء المحاولة مرة أخرى لاحقا",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "تم تسجيل الدخول",
|
"loginSuccessTitle": "تم تسجيل الدخول",
|
||||||
"loginSuccessSubtitle": "مرحبا بعودتك!",
|
"loginSuccessSubtitle": "مرحبا بعودتك!",
|
||||||
"loginOauthFailTitle": "خطأ داخلي",
|
"loginOauthFailTitle": "حدث خطأ",
|
||||||
"loginOauthFailSubtitle": "فشل في الحصول على رابط OAuth",
|
"loginOauthFailSubtitle": "أخفق الحصول على رابط OAuth",
|
||||||
"loginOauthSuccessTitle": "إعادة توجيه",
|
"loginOauthSuccessTitle": "إعادة توجيه",
|
||||||
"loginOauthSuccessSubtitle": "إعادة توجيه إلى مزود OAuth الخاص بك",
|
"loginOauthSuccessSubtitle": "إعادة توجيه إلى مزود OAuth الخاص بك",
|
||||||
"continueRedirectingTitle": "إعادة توجيه...",
|
"continueRedirectingTitle": "إعادة توجيه...",
|
||||||
@@ -18,34 +19,36 @@
|
|||||||
"continueInvalidRedirectTitle": "إعادة توجيه غير صالحة",
|
"continueInvalidRedirectTitle": "إعادة توجيه غير صالحة",
|
||||||
"continueInvalidRedirectSubtitle": "رابط إعادة التوجيه غير صالح",
|
"continueInvalidRedirectSubtitle": "رابط إعادة التوجيه غير صالح",
|
||||||
"continueInsecureRedirectTitle": "إعادة توجيه غير آمنة",
|
"continueInsecureRedirectTitle": "إعادة توجيه غير آمنة",
|
||||||
"continueInsecureRedirectSubtitle": "أنت تحاول إعادة التوجيه من <Code>https</Code> إلى <Code>http</Code>، هل أنت متأكد أنك تريد المتابعة؟",
|
"continueInsecureRedirectSubtitle": "أنت تحاول إعادة التوجيه من <code>https</code> إلى <code>http</code>، هل أنت متأكد أنك تريد المتابعة؟",
|
||||||
"continueTitle": "متابعة",
|
"continueTitle": "متابعة",
|
||||||
"continueSubtitle": "انقر الزر للمتابعة إلى التطبيق الخاص بك.",
|
"continueSubtitle": "انقر الزر للمتابعة إلى التطبيق الخاص بك.",
|
||||||
"internalErrorTitle": "خطأ داخلي في الخادم",
|
|
||||||
"internalErrorSubtitle": "حدث خطأ على الخادم ولا يمكن حاليا تلبية طلبك.",
|
|
||||||
"internalErrorButton": "حاول مجددا",
|
|
||||||
"logoutFailTitle": "فشل تسجيل الخروج",
|
"logoutFailTitle": "فشل تسجيل الخروج",
|
||||||
"logoutFailSubtitle": "يرجى إعادة المحاولة",
|
"logoutFailSubtitle": "يرجى إعادة المحاولة",
|
||||||
"logoutSuccessTitle": "تم تسجيل الخروج",
|
"logoutSuccessTitle": "تم تسجيل الخروج",
|
||||||
"logoutSuccessSubtitle": "تم تسجيل خروجك",
|
"logoutSuccessSubtitle": "تم تسجيل خروجك",
|
||||||
"logoutTitle": "تسجيل الخروج",
|
"logoutTitle": "تسجيل الخروج",
|
||||||
"logoutUsernameSubtitle": "أنت حاليا مسجل الدخول ك <Code>{{username}}</Code>، انقر الزر أدناه لتسجيل الخروج.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "أنت حاليا مسجل الدخول ك <Code>{{username}}</Code> باستخدام مزود OAuth {{provider}} ، انقر الزر أدناه لتسجيل الخروج.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "الصفحة غير موجودة",
|
"notFoundTitle": "الصفحة غير موجودة",
|
||||||
"notFoundSubtitle": "الصفحة التي تبحث عنها غير موجودة.",
|
"notFoundSubtitle": "الصفحة التي تبحث عنها غير موجودة.",
|
||||||
"notFoundButton": "انتقل إلى الرئيسية",
|
"notFoundButton": "انتقل إلى الرئيسية",
|
||||||
"totpFailTitle": "فشل في التحقق من الرمز",
|
"totpFailTitle": "أخفق التحقق من الرمز",
|
||||||
"totpFailSubtitle": "الرجاء التحقق من الرمز الخاص بك وحاول مرة أخرى",
|
"totpFailSubtitle": "الرجاء التحقق من الرمز الخاص بك وحاول مرة أخرى",
|
||||||
"totpSuccessTitle": "تم التحقق",
|
"totpSuccessTitle": "تم التحقق",
|
||||||
"totpSuccessSubtitle": "إعادة توجيه إلى تطبيقك",
|
"totpSuccessSubtitle": "إعادة توجيه إلى تطبيقك",
|
||||||
"totpTitle": "أدخل رمز TOTP الخاص بك",
|
"totpTitle": "أدخل رمز TOTP الخاص بك",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "غير مرخص",
|
"unauthorizedTitle": "غير مرخص",
|
||||||
"unauthorizedResourceSubtitle": "المستخدم الذي يحمل اسم المستخدم <Code>{{username}}</Code> غير مصرح له بالوصول إلى المورد <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "حاول مجددا",
|
"unauthorizedButton": "حاول مجددا",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "إعادة توجيه غير موثوقة",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "أنت تحاول إعادة التوجيه إلى نطاق لا يتطابق مع النطاق المكون الخاص بك (<code>{{domain}}</code>). هل أنت متأكد من أنك تريد المتابعة؟",
|
||||||
"cancelTitle": "إلغاء",
|
"cancelTitle": "إلغاء",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "نسيت كلمة المرور؟",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "حدث خطأ",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,54 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Velkommen tilbage, log ind med",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Velkommen tilbage, log venligst ind",
|
||||||
"loginUsername": "Username",
|
"loginDivider": "Eller",
|
||||||
"loginPassword": "Password",
|
"loginUsername": "Brugernavn",
|
||||||
"loginSubmit": "Login",
|
"loginPassword": "Adgangskode",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginSubmit": "Log ind",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailTitle": "Login mislykkedes",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailSubtitle": "Tjek venligst dit brugernavn og adgangskode",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginFailRateLimit": "Du har forsøgt at logge ind for mange gange, prøv igen senere",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessTitle": "Logget ind",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginSuccessSubtitle": "Velkommen tilbage!",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailTitle": "Der opstod en fejl",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthFailSubtitle": "Kunne ikke hente OAuth-URL",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessTitle": "Omdirigerer",
|
||||||
"continueRedirectingTitle": "Redirecting...",
|
"loginOauthSuccessSubtitle": "Omdirigerer til din OAuth-udbyder",
|
||||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
"continueRedirectingTitle": "Omdirigerer...",
|
||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueRedirectingSubtitle": "Du bør blive omdirigeret til appen snart",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectTitle": "Ugyldig omdirigering",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInvalidRedirectSubtitle": "Omdirigerings-URL'en er ugyldig",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectTitle": "Usikker omdirigering",
|
||||||
"continueTitle": "Continue",
|
"continueInsecureRedirectSubtitle": "Du forsøger at omdirigere fra <code>https</code> til <code>http</code>, som ikke er sikker. Er du sikker på, at du vil fortsætte?",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueTitle": "Fortsæt",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
"continueSubtitle": "Klik på knappen for at fortsætte til din app.",
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
"logoutFailTitle": "Log ud mislykkedes",
|
||||||
"internalErrorButton": "Try again",
|
"logoutFailSubtitle": "Prøv venligst igen",
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutSuccessTitle": "Logget ud",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutSuccessSubtitle": "Du er blevet logget ud",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutTitle": "Log ud",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutUsernameSubtitle": "Du er i øjeblikket logget ind som <code>{{username}}</code>. Klik på knappen nedenfor for at logge ud.",
|
||||||
"logoutTitle": "Logout",
|
"logoutOauthSubtitle": "Du er i øjeblikket logget ind som <code>{{username}}</code> via {{provider}} OAuth-udbyderen. Klik på knappen nedenfor for at logge ud.",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"notFoundTitle": "Siden blev ikke fundet",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"notFoundSubtitle": "Siden du leder efter, findes ikke.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundButton": "Gå til forsiden",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"totpFailTitle": "Verificering af kode mislykkedes",
|
||||||
"notFoundButton": "Go home",
|
"totpFailSubtitle": "Tjek venligst din kode og prøv igen",
|
||||||
"totpFailTitle": "Failed to verify code",
|
"totpSuccessTitle": "Verificeret",
|
||||||
"totpFailSubtitle": "Please check your code and try again",
|
"totpSuccessSubtitle": "Omdirigerer til din app",
|
||||||
"totpSuccessTitle": "Verified",
|
"totpTitle": "Indtast din TOTP-kode",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSubtitle": "Indtast venligst koden fra din to-faktor-godkendelsesapp.",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"unauthorizedTitle": "Uautoriseret",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedResourceSubtitle": "Brugeren med brugernavnet <code>{{username}}</code> har ikke tilladelse til at tilgå ressourcen <code>{{resource}}</code>.",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedLoginSubtitle": "Brugeren med brugernavnet <code>{{username}}</code> har ikke tilladelse til at logge ind.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedGroupsSubtitle": "Brugeren med brugernavnet <code>{{username}}</code> er ikke i de grupper, som ressourcen <code>{{resource}}</code> kræver.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedIpSubtitle": "Din IP adresse <code>{{ip}}</code> er ikke autoriseret til at tilgå ressourcen <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Prøv igen",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Usikker omdirigering",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "Du forsøger at omdirigere til et domæne, der ikke matcher dit konfigurerede domæne (<code>{{domain}}</code>). Er du sikker på, at du vil fortsætte?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Annuller",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Glemt din adgangskode?",
|
||||||
|
"failedToFetchProvidersTitle": "Kunne ikke indlæse godkendelsesudbydere. Tjek venligst din konfiguration.",
|
||||||
|
"errorTitle": "Der opstod en fejl",
|
||||||
|
"errorSubtitle": "Der opstod en fejl under forsøget på at udføre denne handling. Tjek venligst konsollen for mere information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Willkommen zurück, logge dich ein mit",
|
"loginTitle": "Willkommen zurück, logge dich ein mit",
|
||||||
"loginDivider": "Oder mit Passwort fortfahren",
|
"loginTitleSimple": "Willkommen zurück, bitte anmelden",
|
||||||
|
"loginDivider": "Oder",
|
||||||
"loginUsername": "Benutzername",
|
"loginUsername": "Benutzername",
|
||||||
"loginPassword": "Passwort",
|
"loginPassword": "Passwort",
|
||||||
"loginSubmit": "Anmelden",
|
"loginSubmit": "Anmelden",
|
||||||
"loginFailTitle": "Login fehlgeschlagen",
|
"loginFailTitle": "Login fehlgeschlagen",
|
||||||
"loginFailSubtitle": "Bitte überprüfe deinen Benutzernamen und Passwort",
|
"loginFailSubtitle": "Bitte überprüfe deinen Benutzernamen und Passwort",
|
||||||
"loginFailRateLimit": "Sie konnten sich zu oft nicht einloggen, bitte versuchen Sie es später erneut",
|
"loginFailRateLimit": "Zu viele fehlgeschlagene Loginversuche. Versuche es später erneut",
|
||||||
"loginSuccessTitle": "Angemeldet",
|
"loginSuccessTitle": "Angemeldet",
|
||||||
"loginSuccessSubtitle": "Willkommen zurück!",
|
"loginSuccessSubtitle": "Willkommen zurück!",
|
||||||
"loginOauthFailTitle": "Interner Fehler",
|
"loginOauthFailTitle": "Ein Fehler ist aufgetreten",
|
||||||
"loginOauthFailSubtitle": "Fehler beim Abrufen der OAuth-URL",
|
"loginOauthFailSubtitle": "Fehler beim Abrufen der OAuth-URL",
|
||||||
"loginOauthSuccessTitle": "Leite weiter",
|
"loginOauthSuccessTitle": "Leite weiter",
|
||||||
"loginOauthSuccessSubtitle": "Weiterleitung zu Ihrem OAuth-Provider",
|
"loginOauthSuccessSubtitle": "Weiterleitung zu Ihrem OAuth-Provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Ungültige Weiterleitung",
|
"continueInvalidRedirectTitle": "Ungültige Weiterleitung",
|
||||||
"continueInvalidRedirectSubtitle": "Die Weiterleitungs-URL ist ungültig",
|
"continueInvalidRedirectSubtitle": "Die Weiterleitungs-URL ist ungültig",
|
||||||
"continueInsecureRedirectTitle": "Unsichere Weiterleitung",
|
"continueInsecureRedirectTitle": "Unsichere Weiterleitung",
|
||||||
"continueInsecureRedirectSubtitle": "Sie versuchen von <Code>https</Code> auf <Code>http</Code>weiterzuleiten. Sind Sie sicher, dass Sie fortfahren möchten?",
|
"continueInsecureRedirectSubtitle": "Sie versuchen von <code>https</code> auf <code>http</code> weiterzuleiten, was unsicher ist. Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||||
"continueTitle": "Weiter",
|
"continueTitle": "Weiter",
|
||||||
"continueSubtitle": "Klicken Sie auf den Button, um zur App zu gelangen.",
|
"continueSubtitle": "Klicken Sie auf den Button, um zur App zu gelangen.",
|
||||||
"internalErrorTitle": "Interner Serverfehler",
|
|
||||||
"internalErrorSubtitle": "Ein Error ist auf dem Server aufgetreten, weshalb ihre Anfrage derzeit nicht bearbeitet werden kann.",
|
|
||||||
"internalErrorButton": "Erneut versuchen",
|
|
||||||
"logoutFailTitle": "Abmelden fehlgeschlagen",
|
"logoutFailTitle": "Abmelden fehlgeschlagen",
|
||||||
"logoutFailSubtitle": "Bitte versuchen Sie es erneut",
|
"logoutFailSubtitle": "Bitte versuchen Sie es erneut",
|
||||||
"logoutSuccessTitle": "Abgemeldet",
|
"logoutSuccessTitle": "Abgemeldet",
|
||||||
"logoutSuccessSubtitle": "Sie wurden abgemeldet",
|
"logoutSuccessSubtitle": "Sie wurden abgemeldet",
|
||||||
"logoutTitle": "Abmelden",
|
"logoutTitle": "Abmelden",
|
||||||
"logoutUsernameSubtitle": "Sie sind derzeit als <Code>{{username}}</Code>angemeldet. Klicken Sie auf den Button unten, um sich abzumelden.",
|
"logoutUsernameSubtitle": "Sie sind derzeit als <code>{{username}}</code> angemeldet. Klicken Sie auf den Button unten, um sich abzumelden.",
|
||||||
"logoutOauthSubtitle": "Sie sind derzeit als <Code>{{username}}</Code> mit dem {{provider}} OAuth-Anbieter angemeldet. Klicken Sie auf den Button unten, um sich abzumelden.",
|
"logoutOauthSubtitle": "Sie sind derzeit als <code>{{username}}</code> über den OAuth-Anbieter {{provider}} angemeldet. Klicken Sie auf den Button unten, um sich abzumelden.",
|
||||||
"notFoundTitle": "Seite nicht gefunden",
|
"notFoundTitle": "Seite nicht gefunden",
|
||||||
"notFoundSubtitle": "Die gesuchte Seite existiert nicht.",
|
"notFoundSubtitle": "Die gesuchte Seite existiert nicht.",
|
||||||
"notFoundButton": "Nach Hause",
|
"notFoundButton": "Nach Hause",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verifiziert",
|
"totpSuccessTitle": "Verifiziert",
|
||||||
"totpSuccessSubtitle": "Leite zur App weiter",
|
"totpSuccessSubtitle": "Leite zur App weiter",
|
||||||
"totpTitle": "Geben Sie Ihren TOTP Code ein",
|
"totpTitle": "Geben Sie Ihren TOTP Code ein",
|
||||||
|
"totpSubtitle": "Bitte geben Sie den Code aus Ihrer Authenticator-App ein.",
|
||||||
"unauthorizedTitle": "Unautorisiert",
|
"unauthorizedTitle": "Unautorisiert",
|
||||||
"unauthorizedResourceSubtitle": "Der Benutzer mit Benutzername <Code>{{username}}</Code> ist nicht berechtigt auf die Ressource <Code>{{resource}}</Code> zuzugreifen.",
|
"unauthorizedResourceSubtitle": "Der Benutzer mit Benutzername <code>{{username}}</code> ist nicht berechtigt, auf die Ressource <code>{{resource}}</code> zuzugreifen.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "Der Benutzer mit Benutzername <code>{{username}}</code> ist nicht berechtigt, sich anzumelden.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "Der Benutzer mit Benutzername <code>{{username}}</code> ist nicht in den Gruppen, die von der Ressource <code>{{resource}}</code> benötigt werden.",
|
||||||
|
"unauthorizedIpSubtitle": "Ihre IP-Adresse <code>{{ip}}</code> ist nicht berechtigt, auf die Ressource <code>{{resource}}</code> zuzugreifen.",
|
||||||
"unauthorizedButton": "Erneut versuchen",
|
"unauthorizedButton": "Erneut versuchen",
|
||||||
"untrustedRedirectTitle": "Nicht vertrauenswürdige Weiterleitung",
|
"untrustedRedirectTitle": "Nicht vertrauenswürdige Weiterleitung",
|
||||||
"untrustedRedirectSubtitle": "Sie versuchen auf eine Domain umzuleiten, die nicht mit Ihrer konfigurierten Domain übereinstimmt (<Code>{{domain}}</Code>). Sind Sie sicher, dass Sie fortfahren möchten?",
|
"untrustedRedirectSubtitle": "Sie versuchen auf eine Domain umzuleiten, die nicht mit Ihrer konfigurierten Domain übereinstimmt (<code>{{domain}}</code>). Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||||
"cancelTitle": "Abbrechen",
|
"cancelTitle": "Abbrechen",
|
||||||
"forgotPasswordTitle": "Passwort vergessen?"
|
"forgotPasswordTitle": "Passwort vergessen?",
|
||||||
|
"failedToFetchProvidersTitle": "Fehler beim Laden der Authentifizierungsanbieter. Bitte überprüfen Sie Ihre Konfiguration.",
|
||||||
|
"errorTitle": "Ein Fehler ist aufgetreten",
|
||||||
|
"errorSubtitle": "Beim Versuch, diese Aktion auszuführen, ist ein Fehler aufgetreten. Bitte überprüfen Sie die Konsole für weitere Informationen."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Καλώς ήρθατε, συνδεθείτε με",
|
"loginTitle": "Καλώς ήρθατε, συνδεθείτε με",
|
||||||
"loginDivider": "Ή συνεχίστε με κωδικό πρόσβασης",
|
"loginTitleSimple": "Καλώς ορίσατε, παρακαλώ συνδεθείτε",
|
||||||
|
"loginDivider": "Ή",
|
||||||
"loginUsername": "Όνομα Χρήστη",
|
"loginUsername": "Όνομα Χρήστη",
|
||||||
"loginPassword": "Κωδικός",
|
"loginPassword": "Κωδικός",
|
||||||
"loginSubmit": "Είσοδος",
|
"loginSubmit": "Είσοδος",
|
||||||
"loginFailTitle": "Αποτυχία σύνδεσης",
|
"loginFailTitle": "Αποτυχία σύνδεσης",
|
||||||
"loginFailSubtitle": "Παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό πρόσβασης",
|
"loginFailSubtitle": "Παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό πρόσβασης",
|
||||||
"loginFailRateLimit": "Αποτύχατε να συνδεθείτε πάρα πολλές φορές, παρακαλώ προσπαθήστε ξανά αργότερα",
|
"loginFailRateLimit": "Αποτύχατε να συνδεθείτε πάρα πολλές φορές. Παρακαλώ προσπαθήστε ξανά αργότερα",
|
||||||
"loginSuccessTitle": "Συνδεδεμένος",
|
"loginSuccessTitle": "Συνδεδεμένος",
|
||||||
"loginSuccessSubtitle": "Καλώς ήρθατε!",
|
"loginSuccessSubtitle": "Καλώς ήρθατε!",
|
||||||
"loginOauthFailTitle": "Εσωτερικό σφάλμα",
|
"loginOauthFailTitle": "Παρουσιάστηκε ένα σφάλμα",
|
||||||
"loginOauthFailSubtitle": "Αποτυχία λήψης OAuth URL",
|
"loginOauthFailSubtitle": "Αποτυχία λήψης OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Ανακατεύθυνση",
|
"loginOauthSuccessTitle": "Ανακατεύθυνση",
|
||||||
"loginOauthSuccessSubtitle": "Ανακατεύθυνση στον πάροχο OAuth σας",
|
"loginOauthSuccessSubtitle": "Ανακατεύθυνση στον πάροχο OAuth σας",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Μη έγκυρη ανακατεύθυνση",
|
"continueInvalidRedirectTitle": "Μη έγκυρη ανακατεύθυνση",
|
||||||
"continueInvalidRedirectSubtitle": "Το URL ανακατεύθυνσης δεν είναι έγκυρο",
|
"continueInvalidRedirectSubtitle": "Το URL ανακατεύθυνσης δεν είναι έγκυρο",
|
||||||
"continueInsecureRedirectTitle": "Μη ασφαλής ανακατεύθυνση",
|
"continueInsecureRedirectTitle": "Μη ασφαλής ανακατεύθυνση",
|
||||||
"continueInsecureRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε από <Code>https</Code> σε <Code>http</Code>, είστε σίγουροι ότι θέλετε να συνεχίσετε;",
|
"continueInsecureRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε από <code>https</code> σε <code>http</code> το οποίο δεν είναι ασφαλές. Είστε σίγουροι ότι θέλετε να συνεχίσετε;",
|
||||||
"continueTitle": "Συνέχεια",
|
"continueTitle": "Συνέχεια",
|
||||||
"continueSubtitle": "Κάντε κλικ στο κουμπί για να συνεχίσετε στην εφαρμογή σας.",
|
"continueSubtitle": "Κάντε κλικ στο κουμπί για να συνεχίσετε στην εφαρμογή σας.",
|
||||||
"internalErrorTitle": "Εσωτερικό Σφάλμα Διακομιστή",
|
|
||||||
"internalErrorSubtitle": "Παρουσιάστηκε σφάλμα στο διακομιστή και δεν μπορεί να εξυπηρετήσει το αίτημά σας.",
|
|
||||||
"internalErrorButton": "Προσπαθήστε ξανά",
|
|
||||||
"logoutFailTitle": "Αποτυχία αποσύνδεσης",
|
"logoutFailTitle": "Αποτυχία αποσύνδεσης",
|
||||||
"logoutFailSubtitle": "Παρακαλώ δοκιμάστε ξανά",
|
"logoutFailSubtitle": "Παρακαλώ δοκιμάστε ξανά",
|
||||||
"logoutSuccessTitle": "Αποσυνδεδεμένος",
|
"logoutSuccessTitle": "Αποσυνδεδεμένος",
|
||||||
"logoutSuccessSubtitle": "Έχετε αποσυνδεθεί",
|
"logoutSuccessSubtitle": "Έχετε αποσυνδεθεί",
|
||||||
"logoutTitle": "Αποσύνδεση",
|
"logoutTitle": "Αποσύνδεση",
|
||||||
"logoutUsernameSubtitle": "Αυτή τη στιγμή είστε συνδεδεμένοι ως <Code>{{username}}</Code>, κάντε κλικ στο παρακάτω κουμπί για να αποσυνδεθείτε.",
|
"logoutUsernameSubtitle": "Αυτή τη στιγμή είστε συνδεδεμένοι ως <code>{{username}}</code>. Κάντε κλικ στο παρακάτω κουμπί για να αποσυνδεθείτε.",
|
||||||
"logoutOauthSubtitle": "Αυτή τη στιγμή είστε συνδεδεμένοι ως <Code>{{username}}</Code> χρησιμοποιώντας την υπηρεσία παροχής {{provider}} OAuth, κάντε κλικ στο παρακάτω κουμπί για να αποσυνδεθείτε.",
|
"logoutOauthSubtitle": "Αυτή τη στιγμή είστε συνδεδεμένοι ως <code>{{username}}</code> χρησιμοποιώντας την υπηρεσία παροχής {{provider}} OAuth. Κάντε κλικ στο παρακάτω κουμπί για να αποσυνδεθείτε.",
|
||||||
"notFoundTitle": "Η σελίδα δε βρέθηκε",
|
"notFoundTitle": "Η σελίδα δε βρέθηκε",
|
||||||
"notFoundSubtitle": "Η σελίδα που ψάχνετε δεν υπάρχει.",
|
"notFoundSubtitle": "Η σελίδα που ψάχνετε δεν υπάρχει.",
|
||||||
"notFoundButton": "Μετάβαση στην αρχική",
|
"notFoundButton": "Μετάβαση στην αρχική",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Επαληθεύθηκε",
|
"totpSuccessTitle": "Επαληθεύθηκε",
|
||||||
"totpSuccessSubtitle": "Ανακατεύθυνση στην εφαρμογή σας",
|
"totpSuccessSubtitle": "Ανακατεύθυνση στην εφαρμογή σας",
|
||||||
"totpTitle": "Εισάγετε τον κωδικό TOTP",
|
"totpTitle": "Εισάγετε τον κωδικό TOTP",
|
||||||
|
"totpSubtitle": "Παρακαλώ εισάγετε τον κωδικό από την εφαρμογή ελέγχου ταυτότητας.",
|
||||||
"unauthorizedTitle": "Μη εξουσιοδοτημένο",
|
"unauthorizedTitle": "Μη εξουσιοδοτημένο",
|
||||||
"unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν έχει άδεια πρόσβασης στον πόρο <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <code>{{username}}</code> δεν έχει άδεια πρόσβασης στον πόρο <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν είναι εξουσιοδοτημένος να συνδεθεί.",
|
"unauthorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <code>{{username}}</code> δεν είναι εξουσιοδοτημένος να συνδεθεί.",
|
||||||
"unauthorizedGroupsSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν είναι στις ομάδες που απαιτούνται από τον πόρο <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "Ο χρήστης με όνομα χρήστη <code>{{username}}</code> δεν είναι στις ομάδες που απαιτούνται από τον πόρο <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Η διεύθυνση IP σας <code>{{ip}}</code> δεν είναι εξουσιοδοτημένη να έχει πρόσβαση στον πόρο <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Προσπαθήστε ξανά",
|
"unauthorizedButton": "Προσπαθήστε ξανά",
|
||||||
"untrustedRedirectTitle": "Μη έμπιστη ανακατεύθυνση",
|
"untrustedRedirectTitle": "Μη έμπιστη ανακατεύθυνση",
|
||||||
"untrustedRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε σε έναν τομέα που δεν ταιριάζει με τον ρυθμισμένο τομέα σας (<Code>{{domain}}</Code>). Είστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
"untrustedRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε σε ένα domain που δεν ταιριάζει με τον ρυθμισμένο domain σας (<code>{{domain}}</code>). Είστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
||||||
"cancelTitle": "Ακύρωση",
|
"cancelTitle": "Ακύρωση",
|
||||||
"forgotPasswordTitle": "Ξεχάσατε το συνθηματικό σας;"
|
"forgotPasswordTitle": "Ξεχάσατε το συνθηματικό σας;",
|
||||||
|
"failedToFetchProvidersTitle": "Αποτυχία φόρτωσης παρόχων πιστοποίησης. Παρακαλώ ελέγξτε τις ρυθμίσεις σας.",
|
||||||
|
"errorTitle": "Παρουσιάστηκε ένα σφάλμα",
|
||||||
|
"errorSubtitle": "Παρουσιάστηκε σφάλμα κατά την προσπάθεια εκτέλεσης αυτής της ενέργειας. Ελέγξτε την κονσόλα για περισσότερες πληροφορίες."
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
@@ -49,5 +50,6 @@
|
|||||||
"forgotPasswordTitle": "Forgot your password?",
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
"errorTitle": "An error occurred",
|
"errorTitle": "An error occurred",
|
||||||
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.",
|
||||||
|
"forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable."
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
@@ -49,5 +50,6 @@
|
|||||||
"forgotPasswordTitle": "Forgot your password?",
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
"errorTitle": "An error occurred",
|
"errorTitle": "An error occurred",
|
||||||
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.",
|
||||||
|
"forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable."
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,54 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Bienvenido de vuelta, inicie sesión con",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Bienvenido de vuelta, por favor inicie sesión",
|
||||||
"loginUsername": "Username",
|
"loginDivider": "O",
|
||||||
"loginPassword": "Password",
|
"loginUsername": "Usuario",
|
||||||
"loginSubmit": "Login",
|
"loginPassword": "Contraseña",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginSubmit": "Iniciar sesión",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailTitle": "Fallo al iniciar sesión",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailSubtitle": "Por favor revise su usuario y contraseña",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginFailRateLimit": "Muchos inicios de sesión consecutivos fallidos. Por favor inténtelo más tarde",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessTitle": "Sesión iniciada",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginSuccessSubtitle": "¡Bienvenido de vuelta!",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailTitle": "Ocurrió un error",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthFailSubtitle": "Error al obtener la URL de OAuth",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessTitle": "Redireccionando",
|
||||||
"continueRedirectingTitle": "Redirecting...",
|
"loginOauthSuccessSubtitle": "Redireccionando a tu proveedor de OAuth",
|
||||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
"continueRedirectingTitle": "Redireccionando...",
|
||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueRedirectingSubtitle": "Pronto será redirigido a la aplicación",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectTitle": "Redirección inválida",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInvalidRedirectSubtitle": "La URL de redirección es inválida",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectTitle": "Redirección insegura",
|
||||||
"continueTitle": "Continue",
|
"continueInsecureRedirectSubtitle": "Está intentando redirigir desde <code>https</code> a <code>http</code> lo cual no es seguro. ¿Está seguro que desea continuar?",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueTitle": "Continuar",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
"continueSubtitle": "Haga clic en el botón para continuar hacia su aplicación.",
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
"logoutFailTitle": "Fallo al cerrar sesión",
|
||||||
"internalErrorButton": "Try again",
|
"logoutFailSubtitle": "Por favor intente nuevamente",
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutSuccessTitle": "Sesión cerrada",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutSuccessSubtitle": "Su sesión ha sido cerrada",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutTitle": "Cerrar sesión",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutUsernameSubtitle": "Actualmente está conectado como <code>{{username}}</code>. Haga clic en el botón de abajo para cerrar sesión.",
|
||||||
"logoutTitle": "Logout",
|
"logoutOauthSubtitle": "Actualmente está conectado como <code>{{username}}</code> usando {{provider}} como su proveedor de OAuth. Haga clic en el botón de abajo para cerrar sesión.",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"notFoundTitle": "Página no encontrada",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"notFoundSubtitle": "La página que está buscando no existe.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundButton": "Volver al inicio",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"totpFailTitle": "Error al verificar código",
|
||||||
"notFoundButton": "Go home",
|
"totpFailSubtitle": "Por favor compruebe su código e inténtelo de nuevo",
|
||||||
"totpFailTitle": "Failed to verify code",
|
"totpSuccessTitle": "Verificado",
|
||||||
"totpFailSubtitle": "Please check your code and try again",
|
"totpSuccessSubtitle": "Redirigiendo a su aplicación",
|
||||||
"totpSuccessTitle": "Verified",
|
"totpTitle": "Ingrese su código TOTP",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSubtitle": "Por favor introduzca el código de su aplicación de autenticación.",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"unauthorizedTitle": "No autorizado",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedResourceSubtitle": "El usuario con nombre de usuario <code>{{username}}</code> no está autorizado para acceder al recurso <code>{{resource}}</code>.",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedLoginSubtitle": "El usuario con nombre de usuario <code>{{username}}</code> no está autorizado a iniciar sesión.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedGroupsSubtitle": "El usuario con nombre de usuario <code>{{username}}</code> no está en los grupos requeridos por el recurso <code>{{resource}}</code>.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Inténtelo de nuevo",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Redirección no confiable",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "Está intentando redirigir a un dominio que no coincide con su dominio configurado (<code>{{domain}}</code>). ¿Está seguro que desea continuar?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancelar",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "¿Olvidó su contraseña?",
|
||||||
|
"failedToFetchProvidersTitle": "Error al cargar los proveedores de autenticación. Por favor revise su configuración.",
|
||||||
|
"errorTitle": "Ha ocurrido un error",
|
||||||
|
"errorSubtitle": "Ocurrió un error mientras se trataba de realizar esta acción. Por favor, revise la consola para más información."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Bienvenue, connectez-vous avec",
|
"loginTitle": "Bienvenue, connectez-vous avec",
|
||||||
"loginDivider": "Ou continuez avec le mot de passe",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Nom d'utilisateur",
|
"loginUsername": "Nom d'utilisateur",
|
||||||
"loginPassword": "Mot de passe",
|
"loginPassword": "Mot de passe",
|
||||||
"loginSubmit": "Se connecter",
|
"loginSubmit": "Se connecter",
|
||||||
"loginFailTitle": "Échec de la connexion",
|
"loginFailTitle": "Échec de la connexion",
|
||||||
"loginFailSubtitle": "Veuillez vérifier votre nom d'utilisateur et votre mot de passe",
|
"loginFailSubtitle": "Veuillez vérifier votre nom d'utilisateur et votre mot de passe",
|
||||||
"loginFailRateLimit": "Vous n'avez pas pu vous connecter trop de fois, veuillez réessayer plus tard",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Connecté",
|
"loginSuccessTitle": "Connecté",
|
||||||
"loginSuccessSubtitle": "Bienvenue!",
|
"loginSuccessSubtitle": "Bienvenue!",
|
||||||
"loginOauthFailTitle": "Erreur interne",
|
"loginOauthFailTitle": "Une erreur s'est produite",
|
||||||
"loginOauthFailSubtitle": "Impossible d'obtenir l'URL OAuth",
|
"loginOauthFailSubtitle": "Impossible d'obtenir l'URL OAuth",
|
||||||
"loginOauthSuccessTitle": "Redirection",
|
"loginOauthSuccessTitle": "Redirection",
|
||||||
"loginOauthSuccessSubtitle": "Redirection vers votre fournisseur OAuth",
|
"loginOauthSuccessSubtitle": "Redirection vers votre fournisseur OAuth",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Redirection invalide",
|
"continueInvalidRedirectTitle": "Redirection invalide",
|
||||||
"continueInvalidRedirectSubtitle": "L'URL de redirection est invalide",
|
"continueInvalidRedirectSubtitle": "L'URL de redirection est invalide",
|
||||||
"continueInsecureRedirectTitle": "Redirection non sécurisée",
|
"continueInsecureRedirectTitle": "Redirection non sécurisée",
|
||||||
"continueInsecureRedirectSubtitle": "Vous essayez de rediriger de <Code>https</Code> vers <Code>http</Code>, êtes-vous sûr de vouloir continuer ?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continuer",
|
"continueTitle": "Continuer",
|
||||||
"continueSubtitle": "Cliquez sur le bouton pour continuer vers votre application.",
|
"continueSubtitle": "Cliquez sur le bouton pour continuer vers votre application.",
|
||||||
"internalErrorTitle": "Erreur interne du serveur",
|
|
||||||
"internalErrorSubtitle": "Une erreur s'est produite sur le serveur et il ne peut actuellement pas répondre à votre demande.",
|
|
||||||
"internalErrorButton": "Réessayer",
|
|
||||||
"logoutFailTitle": "Échec de la déconnexion",
|
"logoutFailTitle": "Échec de la déconnexion",
|
||||||
"logoutFailSubtitle": "Veuillez réessayer",
|
"logoutFailSubtitle": "Veuillez réessayer",
|
||||||
"logoutSuccessTitle": "Déconnecté",
|
"logoutSuccessTitle": "Déconnecté",
|
||||||
"logoutSuccessSubtitle": "Vous avez été déconnecté",
|
"logoutSuccessSubtitle": "Vous avez été déconnecté",
|
||||||
"logoutTitle": "Déconnexion",
|
"logoutTitle": "Déconnexion",
|
||||||
"logoutUsernameSubtitle": "Vous êtes actuellement connecté en tant que <Code>{{username}}</Code>, cliquez sur le bouton ci-dessous pour vous déconnecter.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "Vous êtes actuellement connecté en tant que <Code>{{username}}</Code> en utilisant le fournisseur OAuth {{provider}} , cliquez sur le bouton ci-dessous pour vous déconnecter.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page introuvable",
|
"notFoundTitle": "Page introuvable",
|
||||||
"notFoundSubtitle": "La page recherchée n'existe pas.",
|
"notFoundSubtitle": "La page recherchée n'existe pas.",
|
||||||
"notFoundButton": "Retour à la page d'accueil",
|
"notFoundButton": "Retour à la page d'accueil",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Vérifié",
|
"totpSuccessTitle": "Vérifié",
|
||||||
"totpSuccessSubtitle": "Redirection vers votre application",
|
"totpSuccessSubtitle": "Redirection vers votre application",
|
||||||
"totpTitle": "Saisissez votre code TOTP",
|
"totpTitle": "Saisissez votre code TOTP",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Non autorisé",
|
"unauthorizedTitle": "Non autorisé",
|
||||||
"unauthorizedResourceSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}</Code> n'est pas autorisé à accéder à la ressource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Réessayer",
|
"unauthorizedButton": "Réessayer",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welkom terug, log in met",
|
"loginTitle": "Welkom terug, log in met",
|
||||||
"loginDivider": "Of ga door met wachtwoord",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Gebruikersnaam",
|
"loginUsername": "Gebruikersnaam",
|
||||||
"loginPassword": "Wachtwoord",
|
"loginPassword": "Wachtwoord",
|
||||||
"loginSubmit": "Log in",
|
"loginSubmit": "Log in",
|
||||||
"loginFailTitle": "Mislukt om in te loggen",
|
"loginFailTitle": "Mislukt om in te loggen",
|
||||||
"loginFailSubtitle": "Controleer je gebruikersnaam en wachtwoord",
|
"loginFailSubtitle": "Controleer je gebruikersnaam en wachtwoord",
|
||||||
"loginFailRateLimit": "Inloggen te vaak mislukt, probeer het later opnieuw",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Ingelogd",
|
"loginSuccessTitle": "Ingelogd",
|
||||||
"loginSuccessSubtitle": "Welkom terug!",
|
"loginSuccessSubtitle": "Welkom terug!",
|
||||||
"loginOauthFailTitle": "Interne fout",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Fout bij het ophalen van OAuth URL",
|
"loginOauthFailSubtitle": "Fout bij het ophalen van OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Omleiden",
|
"loginOauthSuccessTitle": "Omleiden",
|
||||||
"loginOauthSuccessSubtitle": "Omleiden naar je OAuth provider",
|
"loginOauthSuccessSubtitle": "Omleiden naar je OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Ongeldige omleiding",
|
"continueInvalidRedirectTitle": "Ongeldige omleiding",
|
||||||
"continueInvalidRedirectSubtitle": "De omleidings-URL is ongeldig",
|
"continueInvalidRedirectSubtitle": "De omleidings-URL is ongeldig",
|
||||||
"continueInsecureRedirectTitle": "Onveilige doorverwijzing",
|
"continueInsecureRedirectTitle": "Onveilige doorverwijzing",
|
||||||
"continueInsecureRedirectSubtitle": "Je probeert door te verwijzen van <Code>https</Code> naar <Code>http</Code>, weet je zeker dat je wilt doorgaan?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Ga verder",
|
"continueTitle": "Ga verder",
|
||||||
"continueSubtitle": "Klik op de knop om door te gaan naar de app.",
|
"continueSubtitle": "Klik op de knop om door te gaan naar de app.",
|
||||||
"internalErrorTitle": "Interne server fout",
|
|
||||||
"internalErrorSubtitle": "Er is een fout opgetreden op de server en het kan momenteel niet voldoen aan je verzoek.",
|
|
||||||
"internalErrorButton": "Opnieuw proberen",
|
|
||||||
"logoutFailTitle": "Afmelden mislukt",
|
"logoutFailTitle": "Afmelden mislukt",
|
||||||
"logoutFailSubtitle": "Probeer het opnieuw",
|
"logoutFailSubtitle": "Probeer het opnieuw",
|
||||||
"logoutSuccessTitle": "Afgemeld",
|
"logoutSuccessTitle": "Afgemeld",
|
||||||
"logoutSuccessSubtitle": "Je bent afgemeld",
|
"logoutSuccessSubtitle": "Je bent afgemeld",
|
||||||
"logoutTitle": "Afmelden",
|
"logoutTitle": "Afmelden",
|
||||||
"logoutUsernameSubtitle": "Je bent momenteel ingelogd als <Code>{{username}}</Code>, klik op de knop hieronder om uit te loggen.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "Je bent momenteel ingelogd als <Code>{{username}}</Code> met behulp van de {{provider}} OAuth provider, klik op de knop hieronder om uit te loggen.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Pagina niet gevonden",
|
"notFoundTitle": "Pagina niet gevonden",
|
||||||
"notFoundSubtitle": "De pagina die je zoekt bestaat niet.",
|
"notFoundSubtitle": "De pagina die je zoekt bestaat niet.",
|
||||||
"notFoundButton": "Naar startpagina",
|
"notFoundButton": "Naar startpagina",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Geverifiëerd",
|
"totpSuccessTitle": "Geverifiëerd",
|
||||||
"totpSuccessSubtitle": "Omleiden naar je app",
|
"totpSuccessSubtitle": "Omleiden naar je app",
|
||||||
"totpTitle": "Voer je TOTP-code in",
|
"totpTitle": "Voer je TOTP-code in",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Ongeautoriseerd",
|
"unauthorizedTitle": "Ongeautoriseerd",
|
||||||
"unauthorizedResourceSubtitle": "De gebruiker met gebruikersnaam <Code>{{username}}</Code> heeft geen toegang tot de bron <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Opnieuw proberen",
|
"unauthorizedButton": "Opnieuw proberen",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Witaj ponownie, zaloguj się przez",
|
"loginTitle": "Witaj ponownie, zaloguj się przez",
|
||||||
"loginDivider": "Lub kontynuuj z hasłem",
|
"loginTitleSimple": "Witaj ponownie, zaloguj się",
|
||||||
|
"loginDivider": "Lub",
|
||||||
"loginUsername": "Nazwa użytkownika",
|
"loginUsername": "Nazwa użytkownika",
|
||||||
"loginPassword": "Hasło",
|
"loginPassword": "Hasło",
|
||||||
"loginSubmit": "Zaloguj się",
|
"loginSubmit": "Zaloguj się",
|
||||||
"loginFailTitle": "Nie udało się zalogować",
|
"loginFailTitle": "Nie udało się zalogować",
|
||||||
"loginFailSubtitle": "Sprawdź swoją nazwę użytkownika i hasło",
|
"loginFailSubtitle": "Sprawdź swoją nazwę użytkownika i hasło",
|
||||||
"loginFailRateLimit": "Nie udało się zalogować zbyt wiele razy, spróbuj ponownie później",
|
"loginFailRateLimit": "Zbyt wiele razy nie udało Ci się zalogować. Spróbuj ponownie później",
|
||||||
"loginSuccessTitle": "Zalogowano",
|
"loginSuccessTitle": "Zalogowano",
|
||||||
"loginSuccessSubtitle": "Witaj ponownie!",
|
"loginSuccessSubtitle": "Witaj ponownie!",
|
||||||
"loginOauthFailTitle": "Wewnętrzny błąd",
|
"loginOauthFailTitle": "Wystąpił błąd",
|
||||||
"loginOauthFailSubtitle": "Nie udało się uzyskać adresu URL OAuth",
|
"loginOauthFailSubtitle": "Nie udało się uzyskać adresu URL OAuth",
|
||||||
"loginOauthSuccessTitle": "Przekierowywanie",
|
"loginOauthSuccessTitle": "Przekierowywanie",
|
||||||
"loginOauthSuccessSubtitle": "Przekierowywanie do Twojego dostawcy OAuth",
|
"loginOauthSuccessSubtitle": "Przekierowywanie do Twojego dostawcy OAuth",
|
||||||
@@ -18,34 +19,36 @@
|
|||||||
"continueInvalidRedirectTitle": "Nieprawidłowe przekierowanie",
|
"continueInvalidRedirectTitle": "Nieprawidłowe przekierowanie",
|
||||||
"continueInvalidRedirectSubtitle": "Adres przekierowania jest nieprawidłowy",
|
"continueInvalidRedirectSubtitle": "Adres przekierowania jest nieprawidłowy",
|
||||||
"continueInsecureRedirectTitle": "Niezabezpieczone przekierowanie",
|
"continueInsecureRedirectTitle": "Niezabezpieczone przekierowanie",
|
||||||
"continueInsecureRedirectSubtitle": "Próbujesz przekierować z <Code>https</Code> do <Code>http</Code>, czy na pewno chcesz kontynuować?",
|
"continueInsecureRedirectSubtitle": "Próbujesz przekierować z <code>https</code> do <code>http</code>, co nie jest bezpieczne. Czy na pewno chcesz kontynuować?",
|
||||||
"continueTitle": "Kontynuuj",
|
"continueTitle": "Kontynuuj",
|
||||||
"continueSubtitle": "Kliknij przycisk, aby przejść do aplikacji.",
|
"continueSubtitle": "Kliknij przycisk, aby przejść do aplikacji.",
|
||||||
"internalErrorTitle": "Wewnętrzny błąd serwera",
|
|
||||||
"internalErrorSubtitle": "Wystąpił błąd na serwerze i obecnie nie można obsłużyć tego żądania.",
|
|
||||||
"internalErrorButton": "Spróbuj ponownie",
|
|
||||||
"logoutFailTitle": "Nie udało się wylogować",
|
"logoutFailTitle": "Nie udało się wylogować",
|
||||||
"logoutFailSubtitle": "Spróbuj ponownie",
|
"logoutFailSubtitle": "Spróbuj ponownie",
|
||||||
"logoutSuccessTitle": "Wylogowano",
|
"logoutSuccessTitle": "Wylogowano",
|
||||||
"logoutSuccessSubtitle": "Zostałeś wylogowany",
|
"logoutSuccessSubtitle": "Zostałeś wylogowany",
|
||||||
"logoutTitle": "Wylogowanie",
|
"logoutTitle": "Wylogowanie",
|
||||||
"logoutUsernameSubtitle": "Jesteś aktualnie zalogowany jako <Code>{{username}}</Code>, kliknij przycisk poniżej, aby się wylogować.",
|
"logoutUsernameSubtitle": "Jesteś obecnie zalogowany jako <code>{{username}}</code>. Kliknij poniższy przycisk, aby się wylogować.",
|
||||||
"logoutOauthSubtitle": "Jesteś obecnie zalogowany jako <Code>{{username}}</Code> przy użyciu providera OAuth {{provider}}, kliknij przycisk poniżej, aby się wylogować.",
|
"logoutOauthSubtitle": "Obecnie jesteś zalogowany jako <code>{{username}}</code> przy użyciu dostawcy {{provider}} OAuth. Kliknij poniższy przycisk, aby się wylogować.",
|
||||||
"notFoundTitle": "Strona nie znaleziona",
|
"notFoundTitle": "Nie znaleziono strony",
|
||||||
"notFoundSubtitle": "Strona, której szukasz nie istnieje.",
|
"notFoundSubtitle": "Strona, której szukasz nie istnieje.",
|
||||||
"notFoundButton": "Wróć do strony głównej",
|
"notFoundButton": "Wróć do strony głównej",
|
||||||
"totpFailTitle": "Nie udało się zweryfikować kodu",
|
"totpFailTitle": "Nie udało się zweryfikować kodu",
|
||||||
"totpFailSubtitle": "Sprawdź swój kod i spróbuj ponownie",
|
"totpFailSubtitle": "Sprawdź swój kod i spróbuj ponownie",
|
||||||
"totpSuccessTitle": "Zweryfikowano",
|
"totpSuccessTitle": "Zweryfikowano",
|
||||||
"totpSuccessSubtitle": "Przekierowywanie do aplikacji",
|
"totpSuccessSubtitle": "Przekierowywanie do aplikacji",
|
||||||
"totpTitle": "Wprowadź kod TOTP",
|
"totpTitle": "Wprowadź kod TOTP",
|
||||||
|
"totpSubtitle": "Wpisz kod z aplikacji uwierzytelniającej.",
|
||||||
"unauthorizedTitle": "Nieautoryzowany",
|
"unauthorizedTitle": "Nieautoryzowany",
|
||||||
"unauthorizedResourceSubtitle": "Użytkownik o nazwie <Code>{{username}}</Code> nie jest upoważniony do uzyskania dostępu do zasobu <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "Użytkownik o nazwie użytkownika <code>{{username}}</code> nie ma uprawnień dostępu do zasobu <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "Użytkownik o nazwie <code>{{username}}</code> nie jest upoważniony do zalogowania się.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "Użytkownik o nazwie <code>{{username}}</code> nie należy do grup wymaganych przez zasób <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Twój adres IP <code>{{ip}}</code> nie ma autoryzacji do dostępu do zasobu <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Spróbuj ponownie",
|
"unauthorizedButton": "Spróbuj ponownie",
|
||||||
"untrustedRedirectTitle": "Niezaufane przekierowanie",
|
"untrustedRedirectTitle": "Niezaufane przekierowanie",
|
||||||
"untrustedRedirectSubtitle": "Próbujesz przekierować do domeny, która nie pasuje do skonfigurowanej przez Ciebie domeny (<Code>{{domain}}</Code>). Czy na pewno chcesz kontynuować?",
|
"untrustedRedirectSubtitle": "Próbujesz przekierować do domeny, która nie pasuje do Twojej skonfigurowanej domeny (<code>{{domain}}</code>). Czy na pewno chcesz kontynuować?",
|
||||||
"cancelTitle": "Anuluj",
|
"cancelTitle": "Anuluj",
|
||||||
"forgotPasswordTitle": "Nie pamiętasz hasła?"
|
"forgotPasswordTitle": "Nie pamiętasz hasła?",
|
||||||
|
"failedToFetchProvidersTitle": "Nie udało się załadować dostawców uwierzytelniania. Sprawdź swoją konfigurację.",
|
||||||
|
"errorTitle": "Wystąpił błąd",
|
||||||
|
"errorSubtitle": "Wystąpił błąd podczas próby wykonania tej czynności. Sprawdź konsolę, aby uzyskać więcej informacji."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Bem-vindo de volta, acesse com",
|
"loginTitle": "Bem-vindo de volta, acesse com",
|
||||||
"loginDivider": "Ou continuar com uma senha",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Nome de usuário",
|
"loginUsername": "Nome de usuário",
|
||||||
"loginPassword": "Senha",
|
"loginPassword": "Senha",
|
||||||
"loginSubmit": "Entrar",
|
"loginSubmit": "Entrar",
|
||||||
"loginFailTitle": "Falha ao iniciar sessão",
|
"loginFailTitle": "Falha ao iniciar sessão",
|
||||||
"loginFailSubtitle": "Por favor, verifique seu usuário e senha",
|
"loginFailSubtitle": "Por favor, verifique seu usuário e senha",
|
||||||
"loginFailRateLimit": "Você falhou em iniciar sessão muitas vezes, por favor tente novamente mais tarde",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Sessão Iniciada",
|
"loginSuccessTitle": "Sessão Iniciada",
|
||||||
"loginSuccessSubtitle": "Bem-vindo de volta!",
|
"loginSuccessSubtitle": "Bem-vindo de volta!",
|
||||||
"loginOauthFailTitle": "Erro interno",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Falha ao obter URL de OAuth",
|
"loginOauthFailSubtitle": "Falha ao obter URL de OAuth",
|
||||||
"loginOauthSuccessTitle": "Redirecionando",
|
"loginOauthSuccessTitle": "Redirecionando",
|
||||||
"loginOauthSuccessSubtitle": "Redirecionando para seu provedor OAuth",
|
"loginOauthSuccessSubtitle": "Redirecionando para seu provedor OAuth",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Redirecionamento inválido",
|
"continueInvalidRedirectTitle": "Redirecionamento inválido",
|
||||||
"continueInvalidRedirectSubtitle": "O endereço de redirecionamento é inválido",
|
"continueInvalidRedirectSubtitle": "O endereço de redirecionamento é inválido",
|
||||||
"continueInsecureRedirectTitle": "Redirecionamento inseguro",
|
"continueInsecureRedirectTitle": "Redirecionamento inseguro",
|
||||||
"continueInsecureRedirectSubtitle": "Você está tentando redirecionar de <Code>https</Code> para <Code>http</Code>, você tem certeza que deseja continuar?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continuar",
|
"continueTitle": "Continuar",
|
||||||
"continueSubtitle": "Clique no botão para continuar para o seu aplicativo.",
|
"continueSubtitle": "Clique no botão para continuar para o seu aplicativo.",
|
||||||
"internalErrorTitle": "Erro interno do servidor",
|
|
||||||
"internalErrorSubtitle": "Ocorreu um erro no servidor e atualmente não pode servir sua solicitação.",
|
|
||||||
"internalErrorButton": "Tentar novamente",
|
|
||||||
"logoutFailTitle": "Falha ao encerrar sessão",
|
"logoutFailTitle": "Falha ao encerrar sessão",
|
||||||
"logoutFailSubtitle": "Por favor, tente novamente",
|
"logoutFailSubtitle": "Por favor, tente novamente",
|
||||||
"logoutSuccessTitle": "Sessão encerrada",
|
"logoutSuccessTitle": "Sessão encerrada",
|
||||||
"logoutSuccessSubtitle": "Você foi desconectado",
|
"logoutSuccessSubtitle": "Você foi desconectado",
|
||||||
"logoutTitle": "Sair",
|
"logoutTitle": "Sair",
|
||||||
"logoutUsernameSubtitle": "Você está atualmente logado como <Code>{{username}}</Code>, clique no botão abaixo para sair.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "Você está atualmente logado como <Code>{{username}}</Code> usando o provedor {{provider}} OAuth, clique no botão abaixo para sair.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Página não encontrada",
|
"notFoundTitle": "Página não encontrada",
|
||||||
"notFoundSubtitle": "A página que você está procurando não existe.",
|
"notFoundSubtitle": "A página que você está procurando não existe.",
|
||||||
"notFoundButton": "Voltar para a tela inicial",
|
"notFoundButton": "Voltar para a tela inicial",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verificado",
|
"totpSuccessTitle": "Verificado",
|
||||||
"totpSuccessSubtitle": "Redirecionando para o seu aplicativo",
|
"totpSuccessSubtitle": "Redirecionando para o seu aplicativo",
|
||||||
"totpTitle": "Insira o seu código TOTP",
|
"totpTitle": "Insira o seu código TOTP",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Não autorizado",
|
"unauthorizedTitle": "Não autorizado",
|
||||||
"unauthorizedResourceSubtitle": "O usuário com nome de usuário <Code>{{username}}</Code> não está autorizado a acessar o recurso <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Tentar novamente",
|
"unauthorizedButton": "Tentar novamente",
|
||||||
"untrustedRedirectTitle": "Redirecionamento não confiável",
|
"untrustedRedirectTitle": "Redirecionamento não confiável",
|
||||||
"untrustedRedirectSubtitle": "Você está tentando redirecionar para um domínio que não corresponde ao seu domínio configurado (<Code>{{domain}}</Code>). Tem certeza que deseja continuar?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancelar",
|
"cancelTitle": "Cancelar",
|
||||||
"forgotPasswordTitle": "Esqueceu sua senha?"
|
"forgotPasswordTitle": "Esqueceu sua senha?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,54 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "С возвращением, войти с",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Вход",
|
||||||
"loginUsername": "Username",
|
"loginDivider": "Или",
|
||||||
"loginPassword": "Password",
|
"loginUsername": "Имя пользователя",
|
||||||
"loginSubmit": "Login",
|
"loginPassword": "Пароль",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginSubmit": "Войти",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailTitle": "Вход не удался",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailSubtitle": "Проверьте имя пользователя и пароль",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginFailRateLimit": "Слишком много ошибок входа. Попробуйте позже",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessTitle": "Вы вошли",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginSuccessSubtitle": "С возвращением!",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailTitle": "Произошла ошибка",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthFailSubtitle": "Не удалось получить OAuth URL",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessTitle": "Перенаправление",
|
||||||
"continueRedirectingTitle": "Redirecting...",
|
"loginOauthSuccessSubtitle": "Перенаправление к поставщику OAuth",
|
||||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
"continueRedirectingTitle": "Перенаправление...",
|
||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueRedirectingSubtitle": "Скоро вы будете перенаправлены в приложение",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectTitle": "Неверное перенаправление",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInvalidRedirectSubtitle": "URL перенаправления недействителен",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectTitle": "Небезопасное перенаправление",
|
||||||
"continueTitle": "Continue",
|
"continueInsecureRedirectSubtitle": "Попытка перенаправления с <code>https</code> на <code>http</code>, уверены, что хотите продолжить?",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueTitle": "Продолжить",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
"continueSubtitle": "Нажмите на кнопку, чтобы перейти к приложению.",
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
"logoutFailTitle": "Не удалось выйти",
|
||||||
"internalErrorButton": "Try again",
|
"logoutFailSubtitle": "Попробуйте ещё раз",
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutSuccessTitle": "Выход",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutSuccessSubtitle": "Вы вышли из системы",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutTitle": "Выйти",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutUsernameSubtitle": "Вход выполнен как <code>{{username}}</code>, нажмите на кнопку ниже, чтобы выйти.",
|
||||||
"logoutTitle": "Logout",
|
"logoutOauthSubtitle": "Вход выполнен как <code>{{username}}</code> с использованием {{provider}} OAuth, нажмите кнопку ниже, чтобы выйти.",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"notFoundTitle": "Страница не найдена",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"notFoundSubtitle": "Эта страница не существует.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundButton": "На главную",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"totpFailTitle": "Не удалось проверить код",
|
||||||
"notFoundButton": "Go home",
|
"totpFailSubtitle": "Пожалуйста, проверьте свой код и повторите попытку",
|
||||||
"totpFailTitle": "Failed to verify code",
|
"totpSuccessTitle": "Подтверждён",
|
||||||
"totpFailSubtitle": "Please check your code and try again",
|
"totpSuccessSubtitle": "Перенаправление в приложение",
|
||||||
"totpSuccessTitle": "Verified",
|
"totpTitle": "Введите код TOTP",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSubtitle": "Пожалуйста, введите код из вашего приложения — аутентификатора.",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"unauthorizedTitle": "Доступ запрещен",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedResourceSubtitle": "Пользователю <code>{{username}}</code> не разрешен доступ к <code>{{resource}}</code>.",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedLoginSubtitle": "Пользователю <code>{{username}}</code> не разрешен вход.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedGroupsSubtitle": "Пользователь <code>{{username}}</code> не состоит в группах, которым разрешен доступ к <code>{{resource}}</code>.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedIpSubtitle": "Ваш IP адрес <code>{{ip}}</code> не авторизован для доступа к ресурсу <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Повторить",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Ненадежное перенаправление",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "Попытка перенаправить на домен, который не соответствует вашему заданному домену (<code>{{domain}}</code>). Уверены, что хотите продолжить?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Отмена",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Забыли пароль?",
|
||||||
|
"failedToFetchProvidersTitle": "Не удалось загрузить провайдеров аутентификации. Пожалуйста, проверьте конфигурацию.",
|
||||||
|
"errorTitle": "Произошла ошибка",
|
||||||
|
"errorSubtitle": "Произошла ошибка при попытке выполнить это действие. Проверьте консоль для дополнительной информации."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Ya da şifre ile devam edin",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Kullanıcı Adı",
|
"loginUsername": "Kullanıcı Adı",
|
||||||
"loginPassword": "Şifre",
|
"loginPassword": "Şifre",
|
||||||
"loginSubmit": "Giriş Yap",
|
"loginSubmit": "Giriş Yap",
|
||||||
"loginFailTitle": "Giriş yapılamadı",
|
"loginFailTitle": "Giriş yapılamadı",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Giriş yapıldı",
|
"loginSuccessTitle": "Giriş yapıldı",
|
||||||
"loginSuccessSubtitle": "Tekrar hoş geldiniz!",
|
"loginSuccessSubtitle": "Tekrar hoş geldiniz!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Yönlendiriliyor",
|
"loginOauthSuccessTitle": "Yönlendiriliyor",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Devam et",
|
"continueTitle": "Devam et",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "İç Sunucu Hatası",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Tekrar deneyin",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Lütfen tekrar deneyin",
|
"logoutFailSubtitle": "Lütfen tekrar deneyin",
|
||||||
"logoutSuccessTitle": "Çıkış yapıldı",
|
"logoutSuccessTitle": "Çıkış yapıldı",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Sayfa bulunamadı",
|
"notFoundTitle": "Sayfa bulunamadı",
|
||||||
"notFoundSubtitle": "Aradığınız sayfa mevcut değil.",
|
"notFoundSubtitle": "Aradığınız sayfa mevcut değil.",
|
||||||
"notFoundButton": "Ana sayfaya git",
|
"notFoundButton": "Ana sayfaya git",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Doğrulandı",
|
"totpSuccessTitle": "Doğrulandı",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "İptal",
|
"cancelTitle": "İptal",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "Welcome back, login with",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "Welcome back, please login",
|
||||||
|
"loginDivider": "Or",
|
||||||
"loginUsername": "Username",
|
"loginUsername": "Username",
|
||||||
"loginPassword": "Password",
|
"loginPassword": "Password",
|
||||||
"loginSubmit": "Login",
|
"loginSubmit": "Login",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginFailTitle": "Failed to log in",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailSubtitle": "Please check your username and password",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailRateLimit": "You failed to login too many times. Please try again later",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginSuccessTitle": "Logged in",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessSubtitle": "Welcome back!",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginOauthFailTitle": "An error occurred",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueSubtitle": "Click the button to continue to your app.",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
|
||||||
"internalErrorButton": "Try again",
|
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutFailTitle": "Failed to log out",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutFailSubtitle": "Please try again",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutSuccessTitle": "Logged out",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutSuccessSubtitle": "You have been logged out",
|
||||||
"logoutTitle": "Logout",
|
"logoutTitle": "Logout",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundTitle": "Page not found",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||||
"notFoundButton": "Go home",
|
"notFoundButton": "Go home",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "Verified",
|
"totpSuccessTitle": "Verified",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSuccessSubtitle": "Redirecting to your app",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"totpTitle": "Enter your TOTP code",
|
||||||
|
"totpSubtitle": "Please enter the code from your authenticator app.",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedTitle": "Unauthorized",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "The user with username <code>{{username}}</code> is not authorized to login.",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "The user with username <code>{{username}}</code> is not in the groups required by the resource <code>{{resource}}</code>.",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "Try again",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "Untrusted redirect",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<code>{{domain}}</code>). Are you sure you want to continue?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "Cancel",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "Forgot your password?",
|
||||||
|
"failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.",
|
||||||
|
"errorTitle": "An error occurred",
|
||||||
|
"errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information."
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "欢迎回来,请登录",
|
"loginTitle": "欢迎回来,请使用以下方式登录",
|
||||||
"loginDivider": "或者继续使用密码",
|
"loginTitleSimple": "欢迎回来,请登录",
|
||||||
|
"loginDivider": "或",
|
||||||
"loginUsername": "用户名",
|
"loginUsername": "用户名",
|
||||||
"loginPassword": "密码",
|
"loginPassword": "密码",
|
||||||
"loginSubmit": "登录",
|
"loginSubmit": "登录",
|
||||||
"loginFailTitle": "登录失败",
|
"loginFailTitle": "登录失败",
|
||||||
"loginFailSubtitle": "请检查您的用户名和密码",
|
"loginFailSubtitle": "请检查您的用户名和密码",
|
||||||
"loginFailRateLimit": "您登录次数过多,请稍后再试",
|
"loginFailRateLimit": "您登录失败次数过多。请稍后再试",
|
||||||
"loginSuccessTitle": "已登录",
|
"loginSuccessTitle": "已登录",
|
||||||
"loginSuccessSubtitle": "欢迎回来!",
|
"loginSuccessSubtitle": "欢迎回来!",
|
||||||
"loginOauthFailTitle": "内部错误",
|
"loginOauthFailTitle": "发生错误",
|
||||||
"loginOauthFailSubtitle": "获取 OAuth URL 失败",
|
"loginOauthFailSubtitle": "获取 OAuth URL 失败",
|
||||||
"loginOauthSuccessTitle": "重定向中",
|
"loginOauthSuccessTitle": "重定向中",
|
||||||
"loginOauthSuccessSubtitle": "重定向到您的 OAuth 提供商",
|
"loginOauthSuccessSubtitle": "重定向到您的 OAuth 提供商",
|
||||||
@@ -18,19 +19,16 @@
|
|||||||
"continueInvalidRedirectTitle": "无效的重定向",
|
"continueInvalidRedirectTitle": "无效的重定向",
|
||||||
"continueInvalidRedirectSubtitle": "重定向URL无效",
|
"continueInvalidRedirectSubtitle": "重定向URL无效",
|
||||||
"continueInsecureRedirectTitle": "不安全的重定向",
|
"continueInsecureRedirectTitle": "不安全的重定向",
|
||||||
"continueInsecureRedirectSubtitle": "您正在尝试将 <Code>https</Code> 重定向到 <Code>http</Code>,您确定要继续吗?",
|
"continueInsecureRedirectSubtitle": "您正在尝试从<code>https</code>重定向到<code>http</code>可能存在风险。您确定要继续吗?",
|
||||||
"continueTitle": "继续",
|
"continueTitle": "继续",
|
||||||
"continueSubtitle": "点击按钮以继续您的应用。",
|
"continueSubtitle": "点击按钮以继续您的应用。",
|
||||||
"internalErrorTitle": "服务器内部错误",
|
|
||||||
"internalErrorSubtitle": "服务器上发生错误,当前无法满足您的请求。",
|
|
||||||
"internalErrorButton": "重试",
|
|
||||||
"logoutFailTitle": "注销失败",
|
"logoutFailTitle": "注销失败",
|
||||||
"logoutFailSubtitle": "请重试",
|
"logoutFailSubtitle": "请重试",
|
||||||
"logoutSuccessTitle": "已登出",
|
"logoutSuccessTitle": "已登出",
|
||||||
"logoutSuccessSubtitle": "您已登出",
|
"logoutSuccessSubtitle": "您已登出",
|
||||||
"logoutTitle": "登出",
|
"logoutTitle": "登出",
|
||||||
"logoutUsernameSubtitle": "您当前以 <Code>{{username}}</Code> 的身份登录,点击下方按钮退出登录。",
|
"logoutUsernameSubtitle": "您当前登录用户为<code>{{username}}</code>。点击下方按钮注销。",
|
||||||
"logoutOauthSubtitle": "您当前以 <Code>{{username}}</Code> 的身份登录,使用的是 {{provider}} OAuth 提供商,点击下方按钮退出登录。",
|
"logoutOauthSubtitle": "您当前以<code>{{username}}</code>登录,使用的是{{provider}} OAuth 提供商。点击下方按钮注销。",
|
||||||
"notFoundTitle": "无法找到页面",
|
"notFoundTitle": "无法找到页面",
|
||||||
"notFoundSubtitle": "您正在查找的页面不存在。",
|
"notFoundSubtitle": "您正在查找的页面不存在。",
|
||||||
"notFoundButton": "回到主页",
|
"notFoundButton": "回到主页",
|
||||||
@@ -39,13 +37,18 @@
|
|||||||
"totpSuccessTitle": "已验证",
|
"totpSuccessTitle": "已验证",
|
||||||
"totpSuccessSubtitle": "重定向到您的应用",
|
"totpSuccessSubtitle": "重定向到您的应用",
|
||||||
"totpTitle": "输入您的 TOTP 代码",
|
"totpTitle": "输入您的 TOTP 代码",
|
||||||
|
"totpSubtitle": "请输入您身份验证器应用中的代码。",
|
||||||
"unauthorizedTitle": "未授权",
|
"unauthorizedTitle": "未授权",
|
||||||
"unauthorizedResourceSubtitle": "用户 <Code>{{username}}</Code> 无权访问资源 <Code>{{resource}}</Code>。",
|
"unauthorizedResourceSubtitle": "用户名为<code>{{username}}</code>的用户无权访问资源<code>{{resource}}</code>。",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedLoginSubtitle": "用户名为<code>{{username}}</code>的用户无权登录。",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedGroupsSubtitle": "用户名为<code>{{username}}</code>的用户不在资源<code>{{resource}}</code>所需的组中。",
|
||||||
|
"unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access the resource <code>{{resource}}</code>.",
|
||||||
"unauthorizedButton": "重试",
|
"unauthorizedButton": "重试",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "不可信的重定向",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "您正在尝试重定向到一个与您已配置的域名 (<code>{{domain}}</code>) 不匹配的域名。您确定要继续吗?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "取消",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "忘记密码?",
|
||||||
|
"failedToFetchProvidersTitle": "加载身份验证提供程序失败,请检查您的配置。",
|
||||||
|
"errorTitle": "发生了错误",
|
||||||
|
"errorSubtitle": "执行此操作时发生错误,请检查控制台以获取更多信息。"
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,54 @@
|
|||||||
{
|
{
|
||||||
"loginTitle": "Welcome back, login with",
|
"loginTitle": "歡迎回來,請用以下方式登入",
|
||||||
"loginDivider": "Or continue with password",
|
"loginTitleSimple": "歡迎回來,請登入",
|
||||||
"loginUsername": "Username",
|
"loginDivider": "或",
|
||||||
"loginPassword": "Password",
|
"loginUsername": "帳號",
|
||||||
"loginSubmit": "Login",
|
"loginPassword": "密碼",
|
||||||
"loginFailTitle": "Failed to log in",
|
"loginSubmit": "登入",
|
||||||
"loginFailSubtitle": "Please check your username and password",
|
"loginFailTitle": "登入失敗",
|
||||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
"loginFailSubtitle": "請檢查您的帳號與密碼",
|
||||||
"loginSuccessTitle": "Logged in",
|
"loginFailRateLimit": "登入失敗次數過多,請稍後再試",
|
||||||
"loginSuccessSubtitle": "Welcome back!",
|
"loginSuccessTitle": "登入成功",
|
||||||
"loginOauthFailTitle": "Internal error",
|
"loginSuccessSubtitle": "歡迎回來!",
|
||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailTitle": "發生錯誤",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthFailSubtitle": "無法取得 OAuth 網址",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessTitle": "重新導向中",
|
||||||
"continueRedirectingTitle": "Redirecting...",
|
"loginOauthSuccessSubtitle": "正在將您重新導向至 OAuth 供應商",
|
||||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
"continueRedirectingTitle": "重新導向中...",
|
||||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
"continueRedirectingSubtitle": "您即將被重新導向至應用程式",
|
||||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
"continueInvalidRedirectTitle": "無效的重新導向",
|
||||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
"continueInvalidRedirectSubtitle": "重新導向的網址無效",
|
||||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
"continueInsecureRedirectTitle": "不安全的重新導向",
|
||||||
"continueTitle": "Continue",
|
"continueInsecureRedirectSubtitle": "您正嘗試從安全的 <code>https</code> 重新導向至不安全的 <code>http</code>。您確定要繼續嗎?",
|
||||||
"continueSubtitle": "Click the button to continue to your app.",
|
"continueTitle": "繼續",
|
||||||
"internalErrorTitle": "Internal Server Error",
|
"continueSubtitle": "點擊按鈕以繼續前往您的應用程式。",
|
||||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
"logoutFailTitle": "登出失敗",
|
||||||
"internalErrorButton": "Try again",
|
"logoutFailSubtitle": "請再試一次",
|
||||||
"logoutFailTitle": "Failed to log out",
|
"logoutSuccessTitle": "登出成功",
|
||||||
"logoutFailSubtitle": "Please try again",
|
"logoutSuccessSubtitle": "您已成功登出",
|
||||||
"logoutSuccessTitle": "Logged out",
|
"logoutTitle": "登出",
|
||||||
"logoutSuccessSubtitle": "You have been logged out",
|
"logoutUsernameSubtitle": "您目前以 <code>{{username}}</code> 的身分登入。點擊下方按鈕以登出。",
|
||||||
"logoutTitle": "Logout",
|
"logoutOauthSubtitle": "您目前使用 {{provider}} OAuth 供應商並以 <code>{{username}}</code> 的身分登入。點擊下方按鈕以登出。",
|
||||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
"notFoundTitle": "找不到頁面",
|
||||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
"notFoundSubtitle": "您要尋找的頁面不存在。",
|
||||||
"notFoundTitle": "Page not found",
|
"notFoundButton": "回到首頁",
|
||||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
"totpFailTitle": "驗證失敗",
|
||||||
"notFoundButton": "Go home",
|
"totpFailSubtitle": "請檢查您的驗證碼並再試一次",
|
||||||
"totpFailTitle": "Failed to verify code",
|
"totpSuccessTitle": "驗證成功",
|
||||||
"totpFailSubtitle": "Please check your code and try again",
|
"totpSuccessSubtitle": "正在重新導向至您的應用程式",
|
||||||
"totpSuccessTitle": "Verified",
|
"totpTitle": "輸入您的 TOTP 驗證碼",
|
||||||
"totpSuccessSubtitle": "Redirecting to your app",
|
"totpSubtitle": "請輸入您驗證器應用程式中的代碼。",
|
||||||
"totpTitle": "Enter your TOTP code",
|
"unauthorizedTitle": "未經授權",
|
||||||
"unauthorizedTitle": "Unauthorized",
|
"unauthorizedResourceSubtitle": "使用者 <code>{{username}}</code> 未被授權存取資源 <code>{{resource}}</code>。",
|
||||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
"unauthorizedLoginSubtitle": "使用者 <code>{{username}}</code> 未被授權登入。",
|
||||||
"unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
|
"unauthorizedGroupsSubtitle": "使用者 <code>{{username}}</code> 不在存取資源 <code>{{resource}}</code> 所需的群組中。",
|
||||||
"unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.",
|
"unauthorizedIpSubtitle": "您的 IP 位址 <code>{{ip}}</code> 未被授權存取資源 <code>{{resource}}</code>。",
|
||||||
"unauthorizedButton": "Try again",
|
"unauthorizedButton": "再試一次",
|
||||||
"untrustedRedirectTitle": "Untrusted redirect",
|
"untrustedRedirectTitle": "不受信任的重新導向",
|
||||||
"untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?",
|
"untrustedRedirectSubtitle": "您正嘗試重新導向至的網域與您設定的網域 (<code>{{domain}}</code>) 不符。您確定要繼續嗎?",
|
||||||
"cancelTitle": "Cancel",
|
"cancelTitle": "取消",
|
||||||
"forgotPasswordTitle": "Forgot your password?"
|
"forgotPasswordTitle": "忘記密碼?",
|
||||||
|
"failedToFetchProvidersTitle": "載入驗證供應商失敗。請檢查您的設定。",
|
||||||
|
"errorTitle": "發生錯誤",
|
||||||
|
"errorSubtitle": "執行此操作時發生錯誤。請檢查主控台以獲取更多資訊。"
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import { StrictMode } from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { Layout } from "./components/layout/layout.tsx";
|
import { Layout } from "./components/layout/layout.tsx";
|
||||||
import { createBrowserRouter, RouterProvider } from "react-router";
|
import { BrowserRouter, Route, Routes } from "react-router";
|
||||||
import { LoginPage } from "./pages/login-page.tsx";
|
import { LoginPage } from "./pages/login-page.tsx";
|
||||||
import { App } from "./App.tsx";
|
import { App } from "./App.tsx";
|
||||||
import { ErrorPage } from "./pages/error-page.tsx";
|
import { ErrorPage } from "./pages/error-page.tsx";
|
||||||
@@ -17,54 +17,6 @@ import { AppContextProvider } from "./context/app-context.tsx";
|
|||||||
import { UserContextProvider } from "./context/user-context.tsx";
|
import { UserContextProvider } from "./context/user-context.tsx";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
element: <App />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/login",
|
|
||||||
element: <LoginPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/logout",
|
|
||||||
element: <LogoutPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/continue",
|
|
||||||
element: <ContinuePage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/totp",
|
|
||||||
element: <TotpPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/forgot-password",
|
|
||||||
element: <ForgotPasswordPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/unauthorized",
|
|
||||||
element: <UnauthorizedPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/error",
|
|
||||||
element: <ErrorPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "*",
|
|
||||||
element: <NotFoundPage />,
|
|
||||||
errorElement: <ErrorPage />,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
@@ -72,10 +24,25 @@ createRoot(document.getElementById("root")!).render(
|
|||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AppContextProvider>
|
<AppContextProvider>
|
||||||
<UserContextProvider>
|
<UserContextProvider>
|
||||||
<Layout>
|
<BrowserRouter>
|
||||||
<RouterProvider router={router} />
|
<Routes>
|
||||||
<Toaster />
|
<Route element={<Layout />} errorElement={<ErrorPage />}>
|
||||||
</Layout>
|
<Route path="/" element={<App />} />
|
||||||
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
<Route path="/logout" element={<LogoutPage />} />
|
||||||
|
<Route path="/continue" element={<ContinuePage />} />
|
||||||
|
<Route path="/totp" element={<TotpPage />} />
|
||||||
|
<Route
|
||||||
|
path="/forgot-password"
|
||||||
|
element={<ForgotPasswordPage />}
|
||||||
|
/>
|
||||||
|
<Route path="/unauthorized" element={<UnauthorizedPage />} />
|
||||||
|
<Route path="/error" element={<ErrorPage />} />
|
||||||
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
<Toaster />
|
||||||
</UserContextProvider>
|
</UserContextProvider>
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { isValidUrl } from "@/lib/utils";
|
|||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { Navigate, useLocation, useNavigate } from "react-router";
|
import { Navigate, useLocation, useNavigate } from "react-router";
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export const ContinuePage = () => {
|
export const ContinuePage = () => {
|
||||||
const { isLoggedIn } = useUserContext();
|
const { isLoggedIn } = useUserContext();
|
||||||
@@ -22,6 +23,7 @@ export const ContinuePage = () => {
|
|||||||
|
|
||||||
const { domain, disableContinue } = useAppContext();
|
const { domain, disableContinue } = useAppContext();
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(search);
|
const searchParams = new URLSearchParams(search);
|
||||||
const redirectURI = searchParams.get("redirect_uri");
|
const redirectURI = searchParams.get("redirect_uri");
|
||||||
@@ -34,10 +36,15 @@ export const ContinuePage = () => {
|
|||||||
return <Navigate to="/logout" />;
|
return <Navigate to="/logout" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disableContinue) {
|
const handleRedirect = () => {
|
||||||
|
setLoading(true);
|
||||||
window.location.href = DOMPurify.sanitize(redirectURI);
|
window.location.href = DOMPurify.sanitize(redirectURI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disableContinue) {
|
||||||
|
handleRedirect();
|
||||||
|
}
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@@ -63,14 +70,13 @@ export const ContinuePage = () => {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter className="flex flex-col items-stretch gap-2">
|
<CardFooter className="flex flex-col items-stretch gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={handleRedirect}
|
||||||
(window.location.href = DOMPurify.sanitize(redirectURI))
|
loading={loading}
|
||||||
}
|
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
>
|
>
|
||||||
{t("continueTitle")}
|
{t("continueTitle")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => navigate("/logout")} variant="outline">
|
<Button onClick={() => navigate("/logout")} variant="outline" disabled={loading}>
|
||||||
{t("cancelTitle")}
|
{t("cancelTitle")}
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
@@ -97,14 +103,13 @@ export const ContinuePage = () => {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter className="flex flex-col items-stretch gap-2">
|
<CardFooter className="flex flex-col items-stretch gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={handleRedirect}
|
||||||
(window.location.href = DOMPurify.sanitize(redirectURI))
|
loading={loading}
|
||||||
}
|
|
||||||
variant="warning"
|
variant="warning"
|
||||||
>
|
>
|
||||||
{t("continueTitle")}
|
{t("continueTitle")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => navigate("/logout")} variant="outline">
|
<Button onClick={() => navigate("/logout")} variant="outline" disabled={loading}>
|
||||||
{t("cancelTitle")}
|
{t("cancelTitle")}
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
@@ -120,9 +125,8 @@ export const ContinuePage = () => {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter className="flex flex-col items-stretch">
|
<CardFooter className="flex flex-col items-stretch">
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={handleRedirect}
|
||||||
(window.location.href = DOMPurify.sanitize(redirectURI))
|
loading={loading}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{t("continueTitle")}
|
{t("continueTitle")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const ForgotPasswordPage = () => {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-3xl">{t("forgotPasswordTitle")}</CardTitle>
|
<CardTitle className="text-3xl">{t("forgotPasswordTitle")}</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
<Markdown>{forgotPasswordMessage}</Markdown>
|
<Markdown>{forgotPasswordMessage !== "" ? forgotPasswordMessage : t('forgotPasswordMessage')}</Markdown>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const LoginPage = () => {
|
|||||||
return <Navigate to="/logout" />;
|
return <Navigate to="/logout" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { configuredProviders, title, oauthAutoRedirect } = useAppContext();
|
const { configuredProviders, title, oauthAutoRedirect, genericName } = useAppContext();
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isMounted = useIsMounted();
|
const isMounted = useIsMounted();
|
||||||
@@ -126,6 +126,8 @@ export const LoginPage = () => {
|
|||||||
icon={<GoogleIcon />}
|
icon={<GoogleIcon />}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => oauthMutation.mutate("google")}
|
onClick={() => oauthMutation.mutate("google")}
|
||||||
|
loading={oauthMutation.isPending && oauthMutation.variables === "google"}
|
||||||
|
disabled={oauthMutation.isPending || loginMutation.isPending}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{configuredProviders.includes("github") && (
|
{configuredProviders.includes("github") && (
|
||||||
@@ -134,14 +136,18 @@ export const LoginPage = () => {
|
|||||||
icon={<GithubIcon />}
|
icon={<GithubIcon />}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => oauthMutation.mutate("github")}
|
onClick={() => oauthMutation.mutate("github")}
|
||||||
|
loading={oauthMutation.isPending && oauthMutation.variables === "github"}
|
||||||
|
disabled={oauthMutation.isPending || loginMutation.isPending}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{configuredProviders.includes("generic") && (
|
{configuredProviders.includes("generic") && (
|
||||||
<OAuthButton
|
<OAuthButton
|
||||||
title="Generic"
|
title={genericName}
|
||||||
icon={<GenericIcon />}
|
icon={<GenericIcon />}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => oauthMutation.mutate("generic")}
|
onClick={() => oauthMutation.mutate("generic")}
|
||||||
|
loading={oauthMutation.isPending && oauthMutation.variables === "generic"}
|
||||||
|
disabled={oauthMutation.isPending || loginMutation.isPending}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -152,13 +158,13 @@ export const LoginPage = () => {
|
|||||||
{userAuthConfigured && (
|
{userAuthConfigured && (
|
||||||
<LoginForm
|
<LoginForm
|
||||||
onSubmit={(values) => loginMutation.mutate(values)}
|
onSubmit={(values) => loginMutation.mutate(values)}
|
||||||
loading={loginMutation.isPending}
|
loading={loginMutation.isPending || oauthMutation.isPending}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{configuredProviders.length == 0 && (
|
{configuredProviders.length == 0 && (
|
||||||
<h3 className="text-center text-xl text-red-600">
|
<p className="text-center text-red-600 max-w-sm">
|
||||||
{t("failedToFetchProvidersTitle")}
|
{t("failedToFetchProvidersTitle")}
|
||||||
</h3>
|
</p>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -6,12 +6,19 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
export const NotFoundPage = () => {
|
export const NotFoundPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleRedirect = () => {
|
||||||
|
setLoading(true);
|
||||||
|
navigate("/");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="min-w-xs sm:min-w-sm">
|
<Card className="min-w-xs sm:min-w-sm">
|
||||||
@@ -20,7 +27,7 @@ export const NotFoundPage = () => {
|
|||||||
<CardDescription>{t("notFoundSubtitle")}</CardDescription>
|
<CardDescription>{t("notFoundSubtitle")}</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter className="flex flex-col items-stretch">
|
<CardFooter className="flex flex-col items-stretch">
|
||||||
<Button onClick={() => navigate("/")}>{t("notFoundButton")}</Button>
|
<Button onClick={handleRedirect} loading={loading}>{t("notFoundButton")}</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,18 +8,24 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
|
import { useUserContext } from "@/context/user-context";
|
||||||
import { TotpSchema } from "@/schemas/totp-schema";
|
import { TotpSchema } from "@/schemas/totp-schema";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useId } from "react";
|
import { useId } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocation, useNavigate } from "react-router";
|
import { Navigate, useLocation } from "react-router";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export const TotpPage = () => {
|
export const TotpPage = () => {
|
||||||
|
const { totpPending } = useUserContext();
|
||||||
|
|
||||||
|
if (!totpPending) {
|
||||||
|
return <Navigate to="/" />;
|
||||||
|
}
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const navigate = useNavigate();
|
|
||||||
const formId = useId();
|
const formId = useId();
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(search);
|
const searchParams = new URLSearchParams(search);
|
||||||
@@ -34,7 +40,7 @@ export const TotpPage = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigate(
|
window.location.replace(
|
||||||
`/continue?redirect_uri=${encodeURIComponent(redirectUri ?? "")}`,
|
`/continue?redirect_uri=${encodeURIComponent(redirectUri ?? "")}`,
|
||||||
);
|
);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
|
import { useState } from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { Navigate, useLocation, useNavigate } from "react-router";
|
import { Navigate, useLocation, useNavigate } from "react-router";
|
||||||
|
|
||||||
@@ -16,13 +17,20 @@ export const UnauthorizedPage = () => {
|
|||||||
const username = searchParams.get("username");
|
const username = searchParams.get("username");
|
||||||
const resource = searchParams.get("resource");
|
const resource = searchParams.get("resource");
|
||||||
const groupErr = searchParams.get("groupErr");
|
const groupErr = searchParams.get("groupErr");
|
||||||
|
const ip = searchParams.get("ip");
|
||||||
|
|
||||||
if (!username) {
|
if (!username && !ip) {
|
||||||
return <Navigate to="/" />;
|
return <Navigate to="/" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleRedirect = () => {
|
||||||
|
setLoading(true);
|
||||||
|
navigate("/login");
|
||||||
|
};
|
||||||
|
|
||||||
let i18nKey = "unauthorizedLoginSubtitle";
|
let i18nKey = "unauthorizedLoginSubtitle";
|
||||||
|
|
||||||
@@ -34,6 +42,10 @@ export const UnauthorizedPage = () => {
|
|||||||
i18nKey = "unauthorizedGroupsSubtitle";
|
i18nKey = "unauthorizedGroupsSubtitle";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ip) {
|
||||||
|
i18nKey = "unauthorizedIpSubtitle";
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="min-w-xs sm:min-w-sm">
|
<Card className="min-w-xs sm:min-w-sm">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -48,12 +60,13 @@ export const UnauthorizedPage = () => {
|
|||||||
values={{
|
values={{
|
||||||
username,
|
username,
|
||||||
resource,
|
resource,
|
||||||
|
ip,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter className="flex flex-col items-stretch">
|
<CardFooter className="flex flex-col items-stretch">
|
||||||
<Button onClick={() => navigate("/login")}>
|
<Button onClick={handleRedirect} loading={loading}>
|
||||||
{t("unauthorizedButton")}
|
{t("unauthorizedButton")}
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
|
|||||||
26
go.mod
26
go.mod
@@ -3,22 +3,27 @@ module tinyauth
|
|||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.1
|
||||||
github.com/go-playground/validator/v10 v10.26.0
|
github.com/go-playground/validator/v10 v10.27.0
|
||||||
github.com/google/go-querystring v1.1.0
|
github.com/google/go-querystring v1.1.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/mdp/qrterminal/v3 v3.2.1
|
github.com/mdp/qrterminal/v3 v3.2.1
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/spf13/cobra v1.9.1
|
github.com/spf13/cobra v1.9.1
|
||||||
github.com/spf13/viper v1.20.1
|
github.com/spf13/viper v1.20.1
|
||||||
golang.org/x/crypto v0.38.0
|
github.com/traefik/paerser v0.2.2
|
||||||
|
golang.org/x/crypto v0.40.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||||
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||||
github.com/moby/term v0.5.2 // indirect
|
github.com/moby/term v0.5.2 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
@@ -26,7 +31,7 @@ require (
|
|||||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||||
golang.org/x/term v0.32.0 // indirect
|
golang.org/x/term v0.33.0 // indirect
|
||||||
gotest.tools/v3 v3.5.2 // indirect
|
gotest.tools/v3 v3.5.2 // indirect
|
||||||
rsc.io/qr v0.2.0 // indirect
|
rsc.io/qr v0.2.0 // indirect
|
||||||
)
|
)
|
||||||
@@ -48,7 +53,7 @@ require (
|
|||||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/docker v28.1.1+incompatible
|
github.com/docker/docker v28.3.2+incompatible
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
@@ -57,6 +62,7 @@ require (
|
|||||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
@@ -103,11 +109,11 @@ require (
|
|||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.13.0 // indirect
|
golang.org/x/arch v0.13.0 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
golang.org/x/net v0.41.0 // indirect
|
||||||
golang.org/x/oauth2 v0.30.0
|
golang.org/x/oauth2 v0.30.0
|
||||||
golang.org/x/sync v0.14.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.25.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.3 // indirect
|
google.golang.org/protobuf v1.36.3 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
68
go.sum
68
go.sum
@@ -1,9 +1,13 @@
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||||
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
@@ -53,6 +57,10 @@ github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJn
|
|||||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
|
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||||
|
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
@@ -64,8 +72,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
|
github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA=
|
||||||
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
@@ -84,8 +92,12 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
@@ -97,10 +109,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||||
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
@@ -122,8 +134,22 @@ github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzq
|
|||||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
|
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||||
|
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||||
|
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||||
|
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||||
|
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||||
|
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
|
||||||
|
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||||
|
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||||
|
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
@@ -234,6 +260,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
github.com/traefik/paerser v0.2.2 h1:cpzW/ZrQrBh3mdwD/jnp6aXASiUFKOVr6ldP+keJTcQ=
|
||||||
|
github.com/traefik/paerser v0.2.2/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
@@ -269,8 +297,8 @@ golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@@ -279,15 +307,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -297,14 +325,14 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|||||||
@@ -1,322 +0,0 @@
|
|||||||
package api_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"tinyauth/internal/api"
|
|
||||||
"tinyauth/internal/auth"
|
|
||||||
"tinyauth/internal/docker"
|
|
||||||
"tinyauth/internal/handlers"
|
|
||||||
"tinyauth/internal/hooks"
|
|
||||||
"tinyauth/internal/providers"
|
|
||||||
"tinyauth/internal/types"
|
|
||||||
|
|
||||||
"github.com/magiconair/properties/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Simple API config for tests
|
|
||||||
var apiConfig = types.APIConfig{
|
|
||||||
Port: 8080,
|
|
||||||
Address: "0.0.0.0",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple handlers config for tests
|
|
||||||
var handlersConfig = types.HandlersConfig{
|
|
||||||
AppURL: "http://localhost:8080",
|
|
||||||
Domain: "localhost",
|
|
||||||
DisableContinue: false,
|
|
||||||
CookieSecure: false,
|
|
||||||
Title: "Tinyauth",
|
|
||||||
GenericName: "Generic",
|
|
||||||
ForgotPasswordMessage: "Some message",
|
|
||||||
CsrfCookieName: "tinyauth-csrf",
|
|
||||||
RedirectCookieName: "tinyauth-redirect",
|
|
||||||
BackgroundImage: "https://example.com/image.png",
|
|
||||||
OAuthAutoRedirect: "none",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple auth config for tests
|
|
||||||
var authConfig = types.AuthConfig{
|
|
||||||
Users: types.Users{},
|
|
||||||
OauthWhitelist: "",
|
|
||||||
Secret: "super-secret-api-thing-for-tests", // It is 32 chars long
|
|
||||||
CookieSecure: false,
|
|
||||||
SessionExpiry: 3600,
|
|
||||||
LoginTimeout: 0,
|
|
||||||
LoginMaxRetries: 0,
|
|
||||||
SessionCookieName: "tinyauth-session",
|
|
||||||
Domain: "localhost",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple hooks config for tests
|
|
||||||
var hooksConfig = types.HooksConfig{
|
|
||||||
Domain: "localhost",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cookie
|
|
||||||
var cookie string
|
|
||||||
|
|
||||||
// User
|
|
||||||
var user = types.User{
|
|
||||||
Username: "user",
|
|
||||||
Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need all this to be able to test the API
|
|
||||||
func getAPI(t *testing.T) *api.API {
|
|
||||||
// Create docker service
|
|
||||||
docker := docker.NewDocker()
|
|
||||||
|
|
||||||
// Initialize docker
|
|
||||||
err := docker.Init()
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to initialize docker: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create auth service
|
|
||||||
authConfig.Users = types.Users{
|
|
||||||
{
|
|
||||||
Username: user.Username,
|
|
||||||
Password: user.Password,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
auth := auth.NewAuth(authConfig, docker)
|
|
||||||
|
|
||||||
// Create providers service
|
|
||||||
providers := providers.NewProviders(types.OAuthConfig{})
|
|
||||||
|
|
||||||
// Initialize providers
|
|
||||||
providers.Init()
|
|
||||||
|
|
||||||
// Create hooks service
|
|
||||||
hooks := hooks.NewHooks(hooksConfig, auth, providers)
|
|
||||||
|
|
||||||
// Create handlers service
|
|
||||||
handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
|
|
||||||
|
|
||||||
// Create API
|
|
||||||
api := api.NewAPI(apiConfig, handlers)
|
|
||||||
|
|
||||||
// Setup routes
|
|
||||||
api.Init()
|
|
||||||
api.SetupRoutes()
|
|
||||||
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test login (we will need this for the other tests)
|
|
||||||
func TestLogin(t *testing.T) {
|
|
||||||
t.Log("Testing login")
|
|
||||||
|
|
||||||
// Get API
|
|
||||||
api := getAPI(t)
|
|
||||||
|
|
||||||
// Create recorder
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
user := types.LoginRequest{
|
|
||||||
Username: "user",
|
|
||||||
Password: "pass",
|
|
||||||
}
|
|
||||||
|
|
||||||
json, err := json.Marshal(user)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error marshalling json: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
req, err := http.NewRequest("POST", "/api/login", strings.NewReader(string(json)))
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve the request
|
|
||||||
api.Router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert.Equal(t, recorder.Code, http.StatusOK)
|
|
||||||
|
|
||||||
// Get the cookie
|
|
||||||
cookie = recorder.Result().Cookies()[0].Value
|
|
||||||
|
|
||||||
// Check if the cookie is set
|
|
||||||
if cookie == "" {
|
|
||||||
t.Fatalf("Cookie not set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test app context
|
|
||||||
func TestAppContext(t *testing.T) {
|
|
||||||
t.Log("Testing app context")
|
|
||||||
|
|
||||||
// Get API
|
|
||||||
api := getAPI(t)
|
|
||||||
|
|
||||||
// Create recorder
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
req, err := http.NewRequest("GET", "/api/app", nil)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cookie
|
|
||||||
req.AddCookie(&http.Cookie{
|
|
||||||
Name: "tinyauth",
|
|
||||||
Value: cookie,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Serve the request
|
|
||||||
api.Router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert.Equal(t, recorder.Code, http.StatusOK)
|
|
||||||
|
|
||||||
// Read the body of the response
|
|
||||||
body, err := io.ReadAll(recorder.Body)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error getting body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal the body into the user struct
|
|
||||||
var app types.AppContext
|
|
||||||
|
|
||||||
err = json.Unmarshal(body, &app)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error unmarshalling body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create tests values
|
|
||||||
expected := types.AppContext{
|
|
||||||
Status: 200,
|
|
||||||
Message: "OK",
|
|
||||||
ConfiguredProviders: []string{"username"},
|
|
||||||
DisableContinue: false,
|
|
||||||
Title: "Tinyauth",
|
|
||||||
GenericName: "Generic",
|
|
||||||
ForgotPasswordMessage: "Some message",
|
|
||||||
BackgroundImage: "https://example.com/image.png",
|
|
||||||
OAuthAutoRedirect: "none",
|
|
||||||
Domain: "localhost",
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should get the username back
|
|
||||||
if !reflect.DeepEqual(app, expected) {
|
|
||||||
t.Fatalf("Expected %v, got %v", expected, app)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test user context
|
|
||||||
func TestUserContext(t *testing.T) {
|
|
||||||
t.Log("Testing user context")
|
|
||||||
|
|
||||||
// Get API
|
|
||||||
api := getAPI(t)
|
|
||||||
|
|
||||||
// Create recorder
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
req, err := http.NewRequest("GET", "/api/user", nil)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cookie
|
|
||||||
req.AddCookie(&http.Cookie{
|
|
||||||
Name: "tinyauth-session",
|
|
||||||
Value: cookie,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Serve the request
|
|
||||||
api.Router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert.Equal(t, recorder.Code, http.StatusOK)
|
|
||||||
|
|
||||||
// Read the body of the response
|
|
||||||
body, err := io.ReadAll(recorder.Body)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error getting body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal the body into the user struct
|
|
||||||
type User struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var user User
|
|
||||||
|
|
||||||
err = json.Unmarshal(body, &user)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error unmarshalling body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should get the username back
|
|
||||||
if user.Username != "user" {
|
|
||||||
t.Fatalf("Expected user, got %s", user.Username)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test logout
|
|
||||||
func TestLogout(t *testing.T) {
|
|
||||||
t.Log("Testing logout")
|
|
||||||
|
|
||||||
// Get API
|
|
||||||
api := getAPI(t)
|
|
||||||
|
|
||||||
// Create recorder
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
req, err := http.NewRequest("POST", "/api/logout", nil)
|
|
||||||
|
|
||||||
// Check if there was an error
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error creating request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cookie
|
|
||||||
req.AddCookie(&http.Cookie{
|
|
||||||
Name: "tinyauth",
|
|
||||||
Value: cookie,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Serve the request
|
|
||||||
api.Router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assert.Equal(t, recorder.Code, http.StatusOK)
|
|
||||||
|
|
||||||
// Check if the cookie is different (means go sessions flushed it)
|
|
||||||
if recorder.Result().Cookies()[0].Value == cookie {
|
|
||||||
t.Fatalf("Cookie not flushed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Testing for the oauth stuff
|
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"tinyauth/internal/docker"
|
"tinyauth/internal/docker"
|
||||||
|
"tinyauth/internal/ldap"
|
||||||
"tinyauth/internal/types"
|
"tinyauth/internal/types"
|
||||||
"tinyauth/internal/utils"
|
"tinyauth/internal/utils"
|
||||||
|
|
||||||
@@ -16,52 +17,151 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAuth(config types.AuthConfig, docker *docker.Docker) *Auth {
|
|
||||||
return &Auth{
|
|
||||||
Config: config,
|
|
||||||
Docker: docker,
|
|
||||||
LoginAttempts: make(map[string]*types.LoginAttempt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
Config types.AuthConfig
|
Config types.AuthConfig
|
||||||
Docker *docker.Docker
|
Docker *docker.Docker
|
||||||
LoginAttempts map[string]*types.LoginAttempt
|
LoginAttempts map[string]*types.LoginAttempt
|
||||||
LoginMutex sync.RWMutex
|
LoginMutex sync.RWMutex
|
||||||
|
Store *sessions.CookieStore
|
||||||
|
LDAP *ldap.LDAP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
|
func NewAuth(config types.AuthConfig, docker *docker.Docker, ldap *ldap.LDAP) *Auth {
|
||||||
// Create cookie store
|
// Create cookie store
|
||||||
store := sessions.NewCookieStore([]byte(auth.Config.Secret))
|
store := sessions.NewCookieStore([]byte(config.HMACSecret), []byte(config.EncryptionSecret))
|
||||||
|
|
||||||
// Configure cookie store
|
// Configure cookie store
|
||||||
store.Options = &sessions.Options{
|
store.Options = &sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: auth.Config.SessionExpiry,
|
MaxAge: config.SessionExpiry,
|
||||||
Secure: auth.Config.CookieSecure,
|
Secure: config.CookieSecure,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Domain: fmt.Sprintf(".%s", auth.Config.Domain),
|
Domain: fmt.Sprintf(".%s", config.Domain),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &Auth{
|
||||||
|
Config: config,
|
||||||
|
Docker: docker,
|
||||||
|
LoginAttempts: make(map[string]*types.LoginAttempt),
|
||||||
|
Store: store,
|
||||||
|
LDAP: ldap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
|
||||||
// Get session
|
// Get session
|
||||||
session, err := store.Get(c.Request, auth.Config.SessionCookieName)
|
session, err := auth.Store.Get(c.Request, auth.Config.SessionCookieName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to get session")
|
log.Warn().Err(err).Msg("Invalid session, clearing cookie and retrying")
|
||||||
return nil, err
|
|
||||||
|
// Delete the session cookie if there is an error
|
||||||
|
c.SetCookie(auth.Config.SessionCookieName, "", -1, "/", fmt.Sprintf(".%s", auth.Config.Domain), auth.Config.CookieSecure, true)
|
||||||
|
|
||||||
|
// Try to get the session again
|
||||||
|
session, err = auth.Store.Get(c.Request, auth.Config.SessionCookieName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// If we still can't get the session, log the error and return nil
|
||||||
|
log.Error().Err(err).Msg("Failed to get session")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) GetUser(username string) *types.User {
|
func (auth *Auth) SearchUser(username string) types.UserSearch {
|
||||||
// Loop through users and return the user if the username matches
|
// Loop through users and return the user if the username matches
|
||||||
for _, user := range auth.Config.Users {
|
log.Debug().Str("username", username).Msg("Searching for user")
|
||||||
if user.Username == username {
|
|
||||||
return &user
|
if auth.GetLocalUser(username).Username != "" {
|
||||||
|
log.Debug().Str("username", username).Msg("Found local user")
|
||||||
|
|
||||||
|
// If user found, return a user with the username and type "local"
|
||||||
|
return types.UserSearch{
|
||||||
|
Username: username,
|
||||||
|
Type: "local",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
// If no user found, check LDAP
|
||||||
|
if auth.LDAP != nil {
|
||||||
|
log.Debug().Str("username", username).Msg("Checking LDAP for user")
|
||||||
|
|
||||||
|
userDN, err := auth.LDAP.Search(username)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("username", username).Msg("Failed to find user in LDAP")
|
||||||
|
return types.UserSearch{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user found in LDAP, return a user with the DN as username
|
||||||
|
return types.UserSearch{
|
||||||
|
Username: userDN,
|
||||||
|
Type: "ldap",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.UserSearch{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *Auth) VerifyUser(search types.UserSearch, password string) bool {
|
||||||
|
// Authenticate the user based on the type
|
||||||
|
switch search.Type {
|
||||||
|
case "local":
|
||||||
|
// Get local user
|
||||||
|
user := auth.GetLocalUser(search.Username)
|
||||||
|
|
||||||
|
// Check if password is correct
|
||||||
|
return auth.CheckPassword(user, password)
|
||||||
|
case "ldap":
|
||||||
|
// If LDAP is configured, bind to the LDAP server with the user DN and password
|
||||||
|
if auth.LDAP != nil {
|
||||||
|
log.Debug().Str("username", search.Username).Msg("Binding to LDAP for user authentication")
|
||||||
|
|
||||||
|
// Bind to the LDAP server
|
||||||
|
err := auth.LDAP.Bind(search.Username, password)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("username", search.Username).Msg("Failed to bind to LDAP")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If bind is successful, rebind with the LDAP bind user
|
||||||
|
err = auth.LDAP.Bind(auth.LDAP.Config.BindDN, auth.LDAP.Config.BindPassword)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to rebind with service account after user authentication")
|
||||||
|
// Consider closing the connection or creating a new one
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("username", search.Username).Msg("LDAP authentication successful")
|
||||||
|
|
||||||
|
// Return true if the bind was successful
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Warn().Str("type", search.Type).Msg("Unknown user type for authentication")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no user found or authentication failed, return false
|
||||||
|
log.Warn().Str("username", search.Username).Msg("User authentication failed")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *Auth) GetLocalUser(username string) types.User {
|
||||||
|
// Loop through users and return the user if the username matches
|
||||||
|
log.Debug().Str("username", username).Msg("Searching for local user")
|
||||||
|
|
||||||
|
for _, user := range auth.Config.Users {
|
||||||
|
if user.Username == username {
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no user found, return an empty user
|
||||||
|
log.Warn().Str("username", username).Msg("Local user not found")
|
||||||
|
return types.User{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) CheckPassword(user types.User, password string) bool {
|
func (auth *Auth) CheckPassword(user types.User, password string) bool {
|
||||||
@@ -133,8 +233,8 @@ func (auth *Auth) RecordLoginAttempt(identifier string, success bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) EmailWhitelisted(emailSrc string) bool {
|
func (auth *Auth) EmailWhitelisted(email string) bool {
|
||||||
return utils.CheckWhitelist(auth.Config.OauthWhitelist, emailSrc)
|
return utils.CheckFilter(auth.Config.OauthWhitelist, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) error {
|
func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) error {
|
||||||
@@ -261,25 +361,25 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error)
|
|||||||
|
|
||||||
func (auth *Auth) UserAuthConfigured() bool {
|
func (auth *Auth) UserAuthConfigured() bool {
|
||||||
// If there are users, return true
|
// If there are users, return true
|
||||||
return len(auth.Config.Users) > 0
|
return len(auth.Config.Users) > 0 || auth.LDAP != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext, labels types.TinyauthLabels) bool {
|
func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext, labels types.Labels) bool {
|
||||||
// Check if oauth is allowed
|
// Check if oauth is allowed
|
||||||
if context.OAuth {
|
if context.OAuth {
|
||||||
log.Debug().Msg("Checking OAuth whitelist")
|
log.Debug().Msg("Checking OAuth whitelist")
|
||||||
return utils.CheckWhitelist(labels.OAuthWhitelist, context.Email)
|
return utils.CheckFilter(labels.OAuth.Whitelist, context.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check users
|
// Check users
|
||||||
log.Debug().Msg("Checking users")
|
log.Debug().Msg("Checking users")
|
||||||
|
|
||||||
return utils.CheckWhitelist(labels.Users, context.Username)
|
return utils.CheckFilter(labels.Users, context.Username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) OAuthGroup(c *gin.Context, context types.UserContext, labels types.TinyauthLabels) bool {
|
func (auth *Auth) OAuthGroup(c *gin.Context, context types.UserContext, labels types.Labels) bool {
|
||||||
// Check if groups are required
|
// Check if groups are required
|
||||||
if labels.OAuthGroups == "" {
|
if labels.OAuth.Groups == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +394,7 @@ func (auth *Auth) OAuthGroup(c *gin.Context, context types.UserContext, labels t
|
|||||||
|
|
||||||
// For every group check if it is in the required groups
|
// For every group check if it is in the required groups
|
||||||
for _, group := range oauthGroups {
|
for _, group := range oauthGroups {
|
||||||
if utils.CheckWhitelist(labels.OAuthGroups, group) {
|
if utils.CheckFilter(labels.OAuth.Groups, group) {
|
||||||
log.Debug().Str("group", group).Msg("Group is in required groups")
|
log.Debug().Str("group", group).Msg("Group is in required groups")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -307,7 +407,7 @@ func (auth *Auth) OAuthGroup(c *gin.Context, context types.UserContext, labels t
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *Auth) AuthEnabled(c *gin.Context, labels types.TinyauthLabels) (bool, error) {
|
func (auth *Auth) AuthEnabled(c *gin.Context, labels types.Labels) (bool, error) {
|
||||||
// Get headers
|
// Get headers
|
||||||
uri := c.Request.Header.Get("X-Forwarded-Uri")
|
uri := c.Request.Header.Get("X-Forwarded-Uri")
|
||||||
|
|
||||||
@@ -351,3 +451,60 @@ func (auth *Auth) GetBasicAuth(c *gin.Context) *types.User {
|
|||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (auth *Auth) CheckIP(labels types.Labels, ip string) bool {
|
||||||
|
// Check if the IP is in block list
|
||||||
|
for _, blocked := range labels.IP.Block {
|
||||||
|
res, err := utils.FilterIP(blocked, ip)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("item", blocked).Msg("Invalid IP/CIDR in block list")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if res {
|
||||||
|
log.Warn().Str("ip", ip).Str("item", blocked).Msg("IP is in blocked list, denying access")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For every IP in the allow list, check if the IP matches
|
||||||
|
for _, allowed := range labels.IP.Allow {
|
||||||
|
res, err := utils.FilterIP(allowed, ip)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("item", allowed).Msg("Invalid IP/CIDR in allow list")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if res {
|
||||||
|
log.Debug().Str("ip", ip).Str("item", allowed).Msg("IP is in allowed list, allowing access")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not in allowed range and allowed range is not empty, deny access
|
||||||
|
if len(labels.IP.Allow) > 0 {
|
||||||
|
log.Warn().Str("ip", ip).Msg("IP not in allow list, denying access")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("ip", ip).Msg("IP not in allow or block list, allowing by default")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *Auth) BypassedIP(labels types.Labels, ip string) bool {
|
||||||
|
// For every IP in the bypass list, check if the IP matches
|
||||||
|
for _, bypassed := range labels.IP.Bypass {
|
||||||
|
res, err := utils.FilterIP(bypassed, ip)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if res {
|
||||||
|
log.Debug().Str("ip", ip).Str("item", bypassed).Msg("IP is in bypass list, allowing access")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Str("ip", ip).Msg("IP not in bypass list, continuing with authentication")
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func TestLoginRateLimiting(t *testing.T) {
|
|||||||
// Initialize a new auth service with 3 max retries and 5 seconds timeout
|
// Initialize a new auth service with 3 max retries and 5 seconds timeout
|
||||||
config.LoginMaxRetries = 3
|
config.LoginMaxRetries = 3
|
||||||
config.LoginTimeout = 5
|
config.LoginTimeout = 5
|
||||||
authService := auth.NewAuth(config, &docker.Docker{})
|
authService := auth.NewAuth(config, &docker.Docker{}, nil)
|
||||||
|
|
||||||
// Test identifier
|
// Test identifier
|
||||||
identifier := "test_user"
|
identifier := "test_user"
|
||||||
@@ -62,7 +62,7 @@ func TestLoginRateLimiting(t *testing.T) {
|
|||||||
// Reinitialize auth service with a shorter timeout for testing
|
// Reinitialize auth service with a shorter timeout for testing
|
||||||
config.LoginTimeout = 1
|
config.LoginTimeout = 1
|
||||||
config.LoginMaxRetries = 3
|
config.LoginMaxRetries = 3
|
||||||
authService = auth.NewAuth(config, &docker.Docker{})
|
authService = auth.NewAuth(config, &docker.Docker{}, nil)
|
||||||
|
|
||||||
// Add enough failed attempts to lock the account
|
// Add enough failed attempts to lock the account
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
@@ -87,7 +87,7 @@ func TestLoginRateLimiting(t *testing.T) {
|
|||||||
t.Log("Testing disabled rate limiting")
|
t.Log("Testing disabled rate limiting")
|
||||||
config.LoginMaxRetries = 0
|
config.LoginMaxRetries = 0
|
||||||
config.LoginTimeout = 0
|
config.LoginTimeout = 0
|
||||||
authService = auth.NewAuth(config, &docker.Docker{})
|
authService = auth.NewAuth(config, &docker.Docker{}, nil)
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
authService.RecordLoginAttempt(identifier, false)
|
authService.RecordLoginAttempt(identifier, false)
|
||||||
@@ -103,7 +103,7 @@ func TestConcurrentLoginAttempts(t *testing.T) {
|
|||||||
// Initialize a new auth service with 2 max retries and 5 seconds timeout
|
// Initialize a new auth service with 2 max retries and 5 seconds timeout
|
||||||
config.LoginMaxRetries = 2
|
config.LoginMaxRetries = 2
|
||||||
config.LoginTimeout = 5
|
config.LoginTimeout = 5
|
||||||
authService := auth.NewAuth(config, &docker.Docker{})
|
authService := auth.NewAuth(config, &docker.Docker{}, nil)
|
||||||
|
|
||||||
// Test multiple identifiers
|
// Test multiple identifiers
|
||||||
identifiers := []string{"user1", "user2", "user3"}
|
identifiers := []string{"user1", "user2", "user3"}
|
||||||
|
|||||||
@@ -1,14 +1,5 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
// TinyauthLabels is a list of labels that can be used in a tinyauth protected container
|
|
||||||
var TinyauthLabels = []string{
|
|
||||||
"tinyauth.oauth.whitelist",
|
|
||||||
"tinyauth.users",
|
|
||||||
"tinyauth.allowed",
|
|
||||||
"tinyauth.headers",
|
|
||||||
"tinyauth.oauth.groups",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Claims are the OIDC supported claims (including preferd username for some reason)
|
// Claims are the OIDC supported claims (including preferd username for some reason)
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|||||||
@@ -11,35 +11,30 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDocker() *Docker {
|
|
||||||
return &Docker{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Docker struct {
|
type Docker struct {
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
Context context.Context
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (docker *Docker) Init() error {
|
func NewDocker() (*Docker, error) {
|
||||||
// Create a new docker client
|
// Create a new docker client
|
||||||
client, err := client.NewClientWithOpts(client.FromEnv)
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
|
|
||||||
// Check if there was an error
|
// Check if there was an error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
docker.Context = context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Negotiate API version
|
// Negotiate API version
|
||||||
client.NegotiateAPIVersion(docker.Context)
|
client.NegotiateAPIVersion(ctx)
|
||||||
|
|
||||||
// Set client
|
return &Docker{
|
||||||
docker.Client = client
|
Client: client,
|
||||||
|
Context: ctx,
|
||||||
// Done
|
}, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (docker *Docker) GetContainers() ([]container.Summary, error) {
|
func (docker *Docker) GetContainers() ([]container.Summary, error) {
|
||||||
@@ -74,56 +69,65 @@ func (docker *Docker) DockerConnected() bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (docker *Docker) GetLabels(appId string) (types.TinyauthLabels, error) {
|
func (docker *Docker) GetLabels(app string, domain string) (types.Labels, error) {
|
||||||
// Check if we have access to the Docker API
|
// Check if we have access to the Docker API
|
||||||
isConnected := docker.DockerConnected()
|
isConnected := docker.DockerConnected()
|
||||||
|
|
||||||
// If we don't have access, return an empty struct
|
// If we don't have access, return an empty struct
|
||||||
if !isConnected {
|
if !isConnected {
|
||||||
log.Debug().Msg("Docker not connected, returning empty labels")
|
log.Debug().Msg("Docker not connected, returning empty labels")
|
||||||
return types.TinyauthLabels{}, nil
|
return types.Labels{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the containers
|
// Get the containers
|
||||||
|
log.Debug().Msg("Getting containers")
|
||||||
|
|
||||||
containers, err := docker.GetContainers()
|
containers, err := docker.GetContainers()
|
||||||
|
|
||||||
// If there is an error, return false
|
// If there is an error, return false
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.TinyauthLabels{}, err
|
log.Error().Err(err).Msg("Error getting containers")
|
||||||
|
return types.Labels{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Got containers")
|
|
||||||
|
|
||||||
// Loop through the containers
|
// Loop through the containers
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
// Inspect the container
|
// Inspect the container
|
||||||
inspect, err := docker.InspectContainer(container.ID)
|
inspect, err := docker.InspectContainer(container.ID)
|
||||||
|
|
||||||
// If there is an error, return false
|
// Check if there was an error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.TinyauthLabels{}, err
|
log.Warn().Str("id", container.ID).Err(err).Msg("Error inspecting container, skipping")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the container name (for some reason it is /name)
|
// Get the labels
|
||||||
containerName := strings.TrimPrefix(inspect.Name, "/")
|
log.Debug().Str("id", inspect.ID).Msg("Getting labels for container")
|
||||||
|
|
||||||
// There is a container with the same name as the app ID
|
labels, err := utils.GetLabels(inspect.Config.Labels)
|
||||||
if containerName == appId {
|
|
||||||
log.Debug().Str("container", containerName).Msg("Found container")
|
|
||||||
|
|
||||||
// Get only the tinyauth labels in a struct
|
// Check if there was an error
|
||||||
labels := utils.GetTinyauthLabels(inspect.Config.Labels)
|
if err != nil {
|
||||||
|
log.Warn().Str("id", container.ID).Err(err).Msg("Error getting container labels, skipping")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Got labels")
|
// Check if the container matches the ID or domain
|
||||||
|
for _, lDomain := range labels.Domain {
|
||||||
|
if lDomain == domain {
|
||||||
|
log.Debug().Str("id", inspect.ID).Msg("Found matching container by domain")
|
||||||
|
return labels, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return labels
|
if strings.TrimPrefix(inspect.Name, "/") == app {
|
||||||
|
log.Debug().Str("id", inspect.ID).Msg("Found matching container by name")
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msg("No matching container found, returning empty labels")
|
log.Debug().Msg("No matching container found, returning empty labels")
|
||||||
|
|
||||||
// If no matching container is found, return empty labels
|
// If no matching container is found, return empty labels
|
||||||
return types.TinyauthLabels{}, nil
|
return types.Labels{}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Handlers struct {
|
||||||
|
Config types.HandlersConfig
|
||||||
|
Auth *auth.Auth
|
||||||
|
Hooks *hooks.Hooks
|
||||||
|
Providers *providers.Providers
|
||||||
|
Docker *docker.Docker
|
||||||
|
}
|
||||||
|
|
||||||
func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers {
|
func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers {
|
||||||
return &Handlers{
|
return &Handlers{
|
||||||
Config: config,
|
Config: config,
|
||||||
@@ -28,14 +36,6 @@ func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handlers struct {
|
|
||||||
Config types.HandlersConfig
|
|
||||||
Auth *auth.Auth
|
|
||||||
Hooks *hooks.Hooks
|
|
||||||
Providers *providers.Providers
|
|
||||||
Docker *docker.Docker
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handlers) AuthHandler(c *gin.Context) {
|
func (h *Handlers) AuthHandler(c *gin.Context) {
|
||||||
// Create struct for proxy
|
// Create struct for proxy
|
||||||
var proxy types.Proxy
|
var proxy types.Proxy
|
||||||
@@ -69,11 +69,14 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
proto := c.Request.Header.Get("X-Forwarded-Proto")
|
proto := c.Request.Header.Get("X-Forwarded-Proto")
|
||||||
host := c.Request.Header.Get("X-Forwarded-Host")
|
host := c.Request.Header.Get("X-Forwarded-Host")
|
||||||
|
|
||||||
// Get the app id
|
// Remove the port from the host if it exists
|
||||||
appId := strings.Split(host, ".")[0]
|
hostPortless := strings.Split(host, ":")[0] // *lol*
|
||||||
|
|
||||||
|
// Get the id
|
||||||
|
id := strings.Split(hostPortless, ".")[0]
|
||||||
|
|
||||||
// Get the container labels
|
// Get the container labels
|
||||||
labels, err := h.Docker.GetLabels(appId)
|
labels, err := h.Docker.GetLabels(id, hostPortless)
|
||||||
|
|
||||||
log.Debug().Interface("labels", labels).Msg("Got labels")
|
log.Debug().Interface("labels", labels).Msg("Got labels")
|
||||||
|
|
||||||
@@ -89,7 +92,57 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get client IP
|
||||||
|
ip := c.ClientIP()
|
||||||
|
|
||||||
|
// Check if the IP is in bypass list
|
||||||
|
if h.Auth.BypassedIP(labels, ip) {
|
||||||
|
headersParsed := utils.ParseHeaders(labels.Headers)
|
||||||
|
for key, value := range headersParsed {
|
||||||
|
log.Debug().Str("key", key).Msg("Setting header")
|
||||||
|
c.Header(key, value)
|
||||||
|
}
|
||||||
|
if labels.Basic.Username != "" && utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File) != "" {
|
||||||
|
log.Debug().Str("username", labels.Basic.Username).Msg("Setting basic auth headers")
|
||||||
|
c.Header("Authorization", fmt.Sprintf("Basic %s", utils.GetBasicAuth(labels.Basic.Username, utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File))))
|
||||||
|
}
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"status": 200,
|
||||||
|
"message": "Authenticated",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the IP is allowed/blocked
|
||||||
|
if !h.Auth.CheckIP(labels, ip) {
|
||||||
|
if proxy.Proxy == "nginx" || !isBrowser {
|
||||||
|
c.JSON(403, gin.H{
|
||||||
|
"status": 403,
|
||||||
|
"message": "Forbidden",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
values := types.UnauthorizedQuery{
|
||||||
|
Resource: strings.Split(host, ".")[0],
|
||||||
|
IP: ip,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build query
|
||||||
|
queries, err := query.Values(values)
|
||||||
|
|
||||||
|
// Handle error
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to build queries")
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,15 +161,20 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If auth is not enabled, return 200
|
// If auth is not enabled, return 200
|
||||||
if !authEnabled {
|
if !authEnabled {
|
||||||
for key, value := range labels.Headers {
|
headersParsed := utils.ParseHeaders(labels.Headers)
|
||||||
log.Debug().Str("key", key).Str("value", value).Msg("Setting header")
|
for key, value := range headersParsed {
|
||||||
c.Header(key, utils.SanitizeHeader(value))
|
log.Debug().Str("key", key).Msg("Setting header")
|
||||||
|
c.Header(key, value)
|
||||||
|
}
|
||||||
|
if labels.Basic.Username != "" && utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File) != "" {
|
||||||
|
log.Debug().Str("username", labels.Basic.Username).Msg("Setting basic auth headers")
|
||||||
|
c.Header("Authorization", fmt.Sprintf("Basic %s", utils.GetBasicAuth(labels.Basic.Username, utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File))))
|
||||||
}
|
}
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"status": 200,
|
"status": 200,
|
||||||
@@ -173,12 +231,12 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to build queries")
|
log.Error().Err(err).Msg("Failed to build queries")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are using caddy/traefik so redirect
|
// We are using caddy/traefik so redirect
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,12 +278,12 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to build queries")
|
log.Error().Err(err).Msg("Failed to build queries")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are using caddy/traefik so redirect
|
// We are using caddy/traefik so redirect
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,9 +294,16 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
c.Header("Remote-Groups", utils.SanitizeHeader(userContext.OAuthGroups))
|
c.Header("Remote-Groups", utils.SanitizeHeader(userContext.OAuthGroups))
|
||||||
|
|
||||||
// Set the rest of the headers
|
// Set the rest of the headers
|
||||||
for key, value := range labels.Headers {
|
parsedHeaders := utils.ParseHeaders(labels.Headers)
|
||||||
log.Debug().Str("key", key).Str("value", value).Msg("Setting header")
|
for key, value := range parsedHeaders {
|
||||||
c.Header(key, utils.SanitizeHeader(value))
|
log.Debug().Str("key", key).Msg("Setting header")
|
||||||
|
c.Header(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set basic auth headers if configured
|
||||||
|
if labels.Basic.Username != "" && utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File) != "" {
|
||||||
|
log.Debug().Str("username", labels.Basic.Username).Msg("Setting basic auth headers")
|
||||||
|
c.Header("Authorization", fmt.Sprintf("Basic %s", utils.GetBasicAuth(labels.Basic.Username, utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The user is allowed to access the app
|
// The user is allowed to access the app
|
||||||
@@ -266,7 +331,7 @@ func (h *Handlers) AuthHandler(c *gin.Context) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to build queries")
|
log.Error().Err(err).Msg("Failed to build queries")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,11 +380,13 @@ func (h *Handlers) LoginHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user based on username
|
// Search for a user based on username
|
||||||
user := h.Auth.GetUser(login.Username)
|
userSearch := h.Auth.SearchUser(login.Username)
|
||||||
|
|
||||||
|
log.Debug().Interface("userSearch", userSearch).Msg("Searching for user")
|
||||||
|
|
||||||
// User does not exist
|
// User does not exist
|
||||||
if user == nil {
|
if userSearch.Type == "" {
|
||||||
log.Debug().Str("username", login.Username).Msg("User not found")
|
log.Debug().Str("username", login.Username).Msg("User not found")
|
||||||
// Record failed login attempt
|
// Record failed login attempt
|
||||||
h.Auth.RecordLoginAttempt(rateIdentifier, false)
|
h.Auth.RecordLoginAttempt(rateIdentifier, false)
|
||||||
@@ -333,7 +400,7 @@ func (h *Handlers) LoginHandler(c *gin.Context) {
|
|||||||
log.Debug().Msg("Got user")
|
log.Debug().Msg("Got user")
|
||||||
|
|
||||||
// Check if password is correct
|
// Check if password is correct
|
||||||
if !h.Auth.CheckPassword(*user, login.Password) {
|
if !h.Auth.VerifyUser(userSearch, login.Password) {
|
||||||
log.Debug().Str("username", login.Username).Msg("Password incorrect")
|
log.Debug().Str("username", login.Username).Msg("Password incorrect")
|
||||||
// Record failed login attempt
|
// Record failed login attempt
|
||||||
h.Auth.RecordLoginAttempt(rateIdentifier, false)
|
h.Auth.RecordLoginAttempt(rateIdentifier, false)
|
||||||
@@ -349,28 +416,34 @@ func (h *Handlers) LoginHandler(c *gin.Context) {
|
|||||||
// Record successful login attempt (will reset failed attempt counter)
|
// Record successful login attempt (will reset failed attempt counter)
|
||||||
h.Auth.RecordLoginAttempt(rateIdentifier, true)
|
h.Auth.RecordLoginAttempt(rateIdentifier, true)
|
||||||
|
|
||||||
// Check if user has totp enabled
|
// Check if user is using TOTP
|
||||||
if user.TotpSecret != "" {
|
if userSearch.Type == "local" {
|
||||||
log.Debug().Msg("Totp enabled")
|
// Get local user
|
||||||
|
localUser := h.Auth.GetLocalUser(login.Username)
|
||||||
|
|
||||||
// Set totp pending cookie
|
// Check if TOTP is enabled
|
||||||
h.Auth.CreateSessionCookie(c, &types.SessionCookie{
|
if localUser.TotpSecret != "" {
|
||||||
Username: login.Username,
|
log.Debug().Msg("Totp enabled")
|
||||||
Name: utils.Capitalize(login.Username),
|
|
||||||
Email: fmt.Sprintf("%s@%s", strings.ToLower(login.Username), h.Config.Domain),
|
|
||||||
Provider: "username",
|
|
||||||
TotpPending: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Return totp required
|
// Set totp pending cookie
|
||||||
c.JSON(200, gin.H{
|
h.Auth.CreateSessionCookie(c, &types.SessionCookie{
|
||||||
"status": 200,
|
Username: login.Username,
|
||||||
"message": "Waiting for totp",
|
Name: utils.Capitalize(login.Username),
|
||||||
"totpPending": true,
|
Email: fmt.Sprintf("%s@%s", strings.ToLower(login.Username), h.Config.Domain),
|
||||||
})
|
Provider: "username",
|
||||||
|
TotpPending: true,
|
||||||
|
})
|
||||||
|
|
||||||
// Stop further processing
|
// Return totp required
|
||||||
return
|
c.JSON(200, gin.H{
|
||||||
|
"status": 200,
|
||||||
|
"message": "Waiting for totp",
|
||||||
|
"totpPending": true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Stop further processing
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create session cookie with username as provider
|
// Create session cookie with username as provider
|
||||||
@@ -422,17 +495,7 @@ func (h *Handlers) TotpHandler(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get user
|
// Get user
|
||||||
user := h.Auth.GetUser(userContext.Username)
|
user := h.Auth.GetLocalUser(userContext.Username)
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
if user == nil {
|
|
||||||
log.Debug().Msg("User not found")
|
|
||||||
c.JSON(401, gin.H{
|
|
||||||
"status": 401,
|
|
||||||
"message": "Unauthorized",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if totp is correct
|
// Check if totp is correct
|
||||||
ok := totp.Validate(totpReq.Code, user.TotpSecret)
|
ok := totp.Validate(totpReq.Code, user.TotpSecret)
|
||||||
@@ -610,7 +673,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Handle error
|
// Handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to bind URI")
|
log.Error().Err(err).Msg("Failed to bind URI")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +687,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Msg("No CSRF cookie")
|
log.Debug().Msg("No CSRF cookie")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,7 +696,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Check if CSRF cookie is valid
|
// Check if CSRF cookie is valid
|
||||||
if csrfCookie != state {
|
if csrfCookie != state {
|
||||||
log.Warn().Msg("Invalid CSRF cookie or CSRF cookie does not match with the state")
|
log.Warn().Msg("Invalid CSRF cookie or CSRF cookie does not match with the state")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,7 +715,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
|
|
||||||
// Provider does not exist
|
// Provider does not exist
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
c.Redirect(http.StatusPermanentRedirect, "/not-found")
|
c.Redirect(http.StatusTemporaryRedirect, "/not-found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,7 +727,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Handle error
|
// Handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to exchange token")
|
log.Error().Err(err).Msg("Failed to exchange token")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +737,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Handle error
|
// Handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Msg("Failed to get user")
|
log.Error().Msg("Failed to get user")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,7 +746,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Check that email is not empty
|
// Check that email is not empty
|
||||||
if user.Email == "" {
|
if user.Email == "" {
|
||||||
log.Error().Msg("Email is empty")
|
log.Error().Msg("Email is empty")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,12 +762,12 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Handle error
|
// Handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to build queries")
|
log.Error().Err(err).Msg("Failed to build queries")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to unauthorized
|
// Redirect to unauthorized
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode()))
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msg("Email whitelisted")
|
log.Debug().Msg("Email whitelisted")
|
||||||
@@ -741,7 +804,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Msg("No redirect cookie")
|
log.Debug().Msg("No redirect cookie")
|
||||||
c.Redirect(http.StatusPermanentRedirect, h.Config.AppURL)
|
c.Redirect(http.StatusTemporaryRedirect, h.Config.AppURL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,7 +820,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
// Handle error
|
// Handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to build queries")
|
log.Error().Err(err).Msg("Failed to build queries")
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", h.Config.AppURL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,7 +828,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
|
|||||||
c.SetCookie(h.Config.RedirectCookieName, "", -1, "/", "", h.Config.CookieSecure, true)
|
c.SetCookie(h.Config.RedirectCookieName, "", -1, "/", "", h.Config.CookieSecure, true)
|
||||||
|
|
||||||
// Redirect to continue with the redirect URI
|
// Redirect to continue with the redirect URI
|
||||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/continue?%s", h.Config.AppURL, queries.Encode()))
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/continue?%s", h.Config.AppURL, queries.Encode()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handlers) HealthcheckHandler(c *gin.Context) {
|
func (h *Handlers) HealthcheckHandler(c *gin.Context) {
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Hooks struct {
|
||||||
|
Config types.HooksConfig
|
||||||
|
Auth *auth.Auth
|
||||||
|
Providers *providers.Providers
|
||||||
|
}
|
||||||
|
|
||||||
func NewHooks(config types.HooksConfig, auth *auth.Auth, providers *providers.Providers) *Hooks {
|
func NewHooks(config types.HooksConfig, auth *auth.Auth, providers *providers.Providers) *Hooks {
|
||||||
return &Hooks{
|
return &Hooks{
|
||||||
Config: config,
|
Config: config,
|
||||||
@@ -20,12 +26,6 @@ func NewHooks(config types.HooksConfig, auth *auth.Auth, providers *providers.Pr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hooks struct {
|
|
||||||
Config types.HooksConfig
|
|
||||||
Auth *auth.Auth
|
|
||||||
Providers *providers.Providers
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
|
func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
|
||||||
// Get session cookie and basic auth
|
// Get session cookie and basic auth
|
||||||
cookie, err := hooks.Auth.GetSessionCookie(c)
|
cookie, err := hooks.Auth.GetSessionCookie(c)
|
||||||
@@ -35,30 +35,49 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
|
|||||||
if basic != nil {
|
if basic != nil {
|
||||||
log.Debug().Msg("Got basic auth")
|
log.Debug().Msg("Got basic auth")
|
||||||
|
|
||||||
// Get user
|
// Search for a user based on username
|
||||||
user := hooks.Auth.GetUser(basic.Username)
|
userSearch := hooks.Auth.SearchUser(basic.Username)
|
||||||
|
|
||||||
// Check we have a user
|
if userSearch.Type == "" {
|
||||||
if user == nil {
|
|
||||||
log.Error().Str("username", basic.Username).Msg("User does not exist")
|
log.Error().Str("username", basic.Username).Msg("User does not exist")
|
||||||
|
|
||||||
// Return empty context
|
// Return empty context
|
||||||
return types.UserContext{}
|
return types.UserContext{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user has a correct password
|
// Verify the user
|
||||||
if hooks.Auth.CheckPassword(*user, basic.Password) {
|
if !hooks.Auth.VerifyUser(userSearch, basic.Password) {
|
||||||
// Return user context since we are logged in with basic auth
|
log.Error().Str("username", basic.Username).Msg("Password incorrect")
|
||||||
|
|
||||||
|
// Return empty context
|
||||||
|
return types.UserContext{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user type
|
||||||
|
if userSearch.Type == "ldap" {
|
||||||
|
log.Debug().Msg("User is LDAP")
|
||||||
|
|
||||||
return types.UserContext{
|
return types.UserContext{
|
||||||
Username: basic.Username,
|
Username: basic.Username,
|
||||||
Name: utils.Capitalize(basic.Username),
|
Name: utils.Capitalize(basic.Username),
|
||||||
Email: fmt.Sprintf("%s@%s", strings.ToLower(basic.Username), hooks.Config.Domain),
|
Email: fmt.Sprintf("%s@%s", strings.ToLower(basic.Username), hooks.Config.Domain),
|
||||||
IsLoggedIn: true,
|
IsLoggedIn: true,
|
||||||
Provider: "basic",
|
Provider: "basic",
|
||||||
TotpEnabled: user.TotpSecret != "",
|
TotpEnabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user := hooks.Auth.GetLocalUser(basic.Username)
|
||||||
|
|
||||||
|
return types.UserContext{
|
||||||
|
Username: basic.Username,
|
||||||
|
Name: utils.Capitalize(basic.Username),
|
||||||
|
Email: fmt.Sprintf("%s@%s", strings.ToLower(basic.Username), hooks.Config.Domain),
|
||||||
|
IsLoggedIn: true,
|
||||||
|
Provider: "basic",
|
||||||
|
TotpEnabled: user.TotpSecret != "",
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cookie error after basic auth
|
// Check cookie error after basic auth
|
||||||
@@ -85,18 +104,25 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
|
|||||||
if cookie.Provider == "username" {
|
if cookie.Provider == "username" {
|
||||||
log.Debug().Msg("Provider is username")
|
log.Debug().Msg("Provider is username")
|
||||||
|
|
||||||
// Check if user exists
|
// Search for the user with the username
|
||||||
if hooks.Auth.GetUser(cookie.Username) != nil {
|
userSearch := hooks.Auth.SearchUser(cookie.Username)
|
||||||
log.Debug().Msg("User exists")
|
|
||||||
|
|
||||||
// It exists so we are logged in
|
if userSearch.Type == "" {
|
||||||
return types.UserContext{
|
log.Error().Str("username", cookie.Username).Msg("User does not exist")
|
||||||
Username: cookie.Username,
|
|
||||||
Name: cookie.Name,
|
// Return empty context
|
||||||
Email: cookie.Email,
|
return types.UserContext{}
|
||||||
IsLoggedIn: true,
|
}
|
||||||
Provider: "username",
|
|
||||||
}
|
log.Debug().Str("type", userSearch.Type).Msg("User exists")
|
||||||
|
|
||||||
|
// It exists so we are logged in
|
||||||
|
return types.UserContext{
|
||||||
|
Username: cookie.Username,
|
||||||
|
Name: cookie.Name,
|
||||||
|
Email: cookie.Email,
|
||||||
|
IsLoggedIn: true,
|
||||||
|
Provider: "username",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
122
internal/ldap/ldap.go
Normal file
122
internal/ldap/ldap.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"tinyauth/internal/types"
|
||||||
|
|
||||||
|
ldapgo "github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LDAP struct {
|
||||||
|
Config types.LdapConfig
|
||||||
|
Conn *ldapgo.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLDAP(config types.LdapConfig) (*LDAP, error) {
|
||||||
|
// Create a new LDAP instance with the provided configuration
|
||||||
|
ldap := &LDAP{
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the LDAP server
|
||||||
|
if err := ldap.Connect(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to LDAP server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start heartbeat goroutine
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(time.Duration(5) * time.Minute) {
|
||||||
|
err := ldap.heartbeat()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("LDAP connection heartbeat failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ldap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) Connect() error {
|
||||||
|
// Connect to the LDAP server
|
||||||
|
conn, err := ldapgo.DialURL(l.Config.Address, ldapgo.DialWithTLSConfig(&tls.Config{
|
||||||
|
InsecureSkipVerify: l.Config.Insecure,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to the LDAP server with the provided credentials
|
||||||
|
err = conn.Bind(l.Config.BindDN, l.Config.BindPassword)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the connection in the LDAP struct
|
||||||
|
l.Conn = conn
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) Search(username string) (string, error) {
|
||||||
|
// Escape the username to prevent LDAP injection
|
||||||
|
escapedUsername := ldapgo.EscapeFilter(username)
|
||||||
|
filter := fmt.Sprintf(l.Config.SearchFilter, escapedUsername)
|
||||||
|
|
||||||
|
// Create a search request to find the user by username
|
||||||
|
searchRequest := ldapgo.NewSearchRequest(
|
||||||
|
l.Config.BaseDN,
|
||||||
|
ldapgo.ScopeWholeSubtree, ldapgo.NeverDerefAliases, 0, 0, false,
|
||||||
|
filter,
|
||||||
|
[]string{"dn"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Perform the search
|
||||||
|
searchResult, err := l.Conn.Search(searchRequest)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(searchResult.Entries) != 1 {
|
||||||
|
return "", fmt.Errorf("err multiple or no entries found for user %s", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User found, return the distinguished name (DN)
|
||||||
|
userDN := searchResult.Entries[0].DN
|
||||||
|
|
||||||
|
return userDN, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) Bind(userDN string, password string) error {
|
||||||
|
// Bind to the LDAP server with the user's DN and password
|
||||||
|
err := l.Conn.Bind(userDN, password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) heartbeat() error {
|
||||||
|
// Perform a simple search to check if the connection is alive
|
||||||
|
log.Info().Msg("Performing LDAP connection heartbeat")
|
||||||
|
|
||||||
|
// Create a search request to find the user by username
|
||||||
|
searchRequest := ldapgo.NewSearchRequest(
|
||||||
|
"",
|
||||||
|
ldapgo.ScopeBaseObject, ldapgo.NeverDerefAliases, 0, 0, false,
|
||||||
|
"(objectClass=*)",
|
||||||
|
[]string{},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Perform the search
|
||||||
|
_, err := l.Conn.Search(searchRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// No error means the connection is alive
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -3,18 +3,13 @@ package oauth
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewOAuth(config oauth2.Config) *OAuth {
|
|
||||||
return &OAuth{
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OAuth struct {
|
type OAuth struct {
|
||||||
Config oauth2.Config
|
Config oauth2.Config
|
||||||
Context context.Context
|
Context context.Context
|
||||||
@@ -22,10 +17,34 @@ type OAuth struct {
|
|||||||
Verifier string
|
Verifier string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oauth *OAuth) Init() {
|
func NewOAuth(config oauth2.Config, insecureSkipVerify bool) *OAuth {
|
||||||
// Create a new context and verifier
|
// Create transport with TLS
|
||||||
oauth.Context = context.Background()
|
transport := &http.Transport{
|
||||||
oauth.Verifier = oauth2.GenerateVerifier()
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: insecureSkipVerify,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new context
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Create the HTTP client with the transport
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the HTTP client in the context
|
||||||
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
|
||||||
|
|
||||||
|
// Create the verifier
|
||||||
|
verifier := oauth2.GenerateVerifier()
|
||||||
|
|
||||||
|
return &OAuth{
|
||||||
|
Config: config,
|
||||||
|
Context: ctx,
|
||||||
|
Verifier: verifier,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oauth *OAuth) GetAuthURL(state string) string {
|
func (oauth *OAuth) GetAuthURL(state string) string {
|
||||||
|
|||||||
@@ -11,12 +11,6 @@ import (
|
|||||||
"golang.org/x/oauth2/endpoints"
|
"golang.org/x/oauth2/endpoints"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewProviders(config types.OAuthConfig) *Providers {
|
|
||||||
return &Providers{
|
|
||||||
Config: config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Providers struct {
|
type Providers struct {
|
||||||
Config types.OAuthConfig
|
Config types.OAuthConfig
|
||||||
Github *oauth.OAuth
|
Github *oauth.OAuth
|
||||||
@@ -24,60 +18,57 @@ type Providers struct {
|
|||||||
Generic *oauth.OAuth
|
Generic *oauth.OAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
func (providers *Providers) Init() {
|
func NewProviders(config types.OAuthConfig) *Providers {
|
||||||
|
providers := &Providers{
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
|
||||||
// If we have a client id and secret for github, initialize the oauth provider
|
// If we have a client id and secret for github, initialize the oauth provider
|
||||||
if providers.Config.GithubClientId != "" && providers.Config.GithubClientSecret != "" {
|
if config.GithubClientId != "" && config.GithubClientSecret != "" {
|
||||||
log.Info().Msg("Initializing Github OAuth")
|
log.Info().Msg("Initializing Github OAuth")
|
||||||
|
|
||||||
// Create a new oauth provider with the github config
|
// Create a new oauth provider with the github config
|
||||||
providers.Github = oauth.NewOAuth(oauth2.Config{
|
providers.Github = oauth.NewOAuth(oauth2.Config{
|
||||||
ClientID: providers.Config.GithubClientId,
|
ClientID: config.GithubClientId,
|
||||||
ClientSecret: providers.Config.GithubClientSecret,
|
ClientSecret: config.GithubClientSecret,
|
||||||
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/github", providers.Config.AppURL),
|
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/github", config.AppURL),
|
||||||
Scopes: GithubScopes(),
|
Scopes: GithubScopes(),
|
||||||
Endpoint: endpoints.GitHub,
|
Endpoint: endpoints.GitHub,
|
||||||
})
|
}, false)
|
||||||
|
|
||||||
// Initialize the oauth provider
|
|
||||||
providers.Github.Init()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a client id and secret for google, initialize the oauth provider
|
// If we have a client id and secret for google, initialize the oauth provider
|
||||||
if providers.Config.GoogleClientId != "" && providers.Config.GoogleClientSecret != "" {
|
if config.GoogleClientId != "" && config.GoogleClientSecret != "" {
|
||||||
log.Info().Msg("Initializing Google OAuth")
|
log.Info().Msg("Initializing Google OAuth")
|
||||||
|
|
||||||
// Create a new oauth provider with the google config
|
// Create a new oauth provider with the google config
|
||||||
providers.Google = oauth.NewOAuth(oauth2.Config{
|
providers.Google = oauth.NewOAuth(oauth2.Config{
|
||||||
ClientID: providers.Config.GoogleClientId,
|
ClientID: config.GoogleClientId,
|
||||||
ClientSecret: providers.Config.GoogleClientSecret,
|
ClientSecret: config.GoogleClientSecret,
|
||||||
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/google", providers.Config.AppURL),
|
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/google", config.AppURL),
|
||||||
Scopes: GoogleScopes(),
|
Scopes: GoogleScopes(),
|
||||||
Endpoint: endpoints.Google,
|
Endpoint: endpoints.Google,
|
||||||
})
|
}, false)
|
||||||
|
|
||||||
// Initialize the oauth provider
|
|
||||||
providers.Google.Init()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a client id and secret for generic oauth, initialize the oauth provider
|
// If we have a client id and secret for generic oauth, initialize the oauth provider
|
||||||
if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" {
|
if config.GenericClientId != "" && config.GenericClientSecret != "" {
|
||||||
log.Info().Msg("Initializing Generic OAuth")
|
log.Info().Msg("Initializing Generic OAuth")
|
||||||
|
|
||||||
// Create a new oauth provider with the generic config
|
// Create a new oauth provider with the generic config
|
||||||
providers.Generic = oauth.NewOAuth(oauth2.Config{
|
providers.Generic = oauth.NewOAuth(oauth2.Config{
|
||||||
ClientID: providers.Config.GenericClientId,
|
ClientID: config.GenericClientId,
|
||||||
ClientSecret: providers.Config.GenericClientSecret,
|
ClientSecret: config.GenericClientSecret,
|
||||||
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/generic", providers.Config.AppURL),
|
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/generic", config.AppURL),
|
||||||
Scopes: providers.Config.GenericScopes,
|
Scopes: config.GenericScopes,
|
||||||
Endpoint: oauth2.Endpoint{
|
Endpoint: oauth2.Endpoint{
|
||||||
AuthURL: providers.Config.GenericAuthURL,
|
AuthURL: config.GenericAuthURL,
|
||||||
TokenURL: providers.Config.GenericTokenURL,
|
TokenURL: config.GenericTokenURL,
|
||||||
},
|
},
|
||||||
})
|
}, config.GenericSkipSSL)
|
||||||
|
|
||||||
// Initialize the oauth provider
|
|
||||||
providers.Generic.Init()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (providers *Providers) GetProvider(provider string) *oauth.OAuth {
|
func (providers *Providers) GetProvider(provider string) *oauth.OAuth {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package api
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -15,20 +15,13 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAPI(config types.APIConfig, handlers *handlers.Handlers) *API {
|
type Server struct {
|
||||||
return &API{
|
Config types.ServerConfig
|
||||||
Config: config,
|
|
||||||
Handlers: handlers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type API struct {
|
|
||||||
Config types.APIConfig
|
|
||||||
Router *gin.Engine
|
|
||||||
Handlers *handlers.Handlers
|
Handlers *handlers.Handlers
|
||||||
|
Router *gin.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) Init() {
|
func NewServer(config types.ServerConfig, handlers *handlers.Handlers) (*Server, error) {
|
||||||
// Disable gin logs
|
// Disable gin logs
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
@@ -42,7 +35,7 @@ func (api *API) Init() {
|
|||||||
dist, err := fs.Sub(assets.Assets, "dist")
|
dist, err := fs.Sub(assets.Assets, "dist")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to get UI assets")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create file server
|
// Create file server
|
||||||
@@ -69,41 +62,38 @@ func (api *API) Init() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set router
|
// Proxy routes
|
||||||
api.Router = router
|
router.GET("/api/auth/:proxy", handlers.AuthHandler)
|
||||||
|
|
||||||
|
// Auth routes
|
||||||
|
router.POST("/api/login", handlers.LoginHandler)
|
||||||
|
router.POST("/api/totp", handlers.TotpHandler)
|
||||||
|
router.POST("/api/logout", handlers.LogoutHandler)
|
||||||
|
|
||||||
|
// Context routes
|
||||||
|
router.GET("/api/app", handlers.AppHandler)
|
||||||
|
router.GET("/api/user", handlers.UserHandler)
|
||||||
|
|
||||||
|
// OAuth routes
|
||||||
|
router.GET("/api/oauth/url/:provider", handlers.OauthUrlHandler)
|
||||||
|
router.GET("/api/oauth/callback/:provider", handlers.OauthCallbackHandler)
|
||||||
|
|
||||||
|
// App routes
|
||||||
|
router.GET("/api/healthcheck", handlers.HealthcheckHandler)
|
||||||
|
|
||||||
|
// Return the server
|
||||||
|
return &Server{
|
||||||
|
Config: config,
|
||||||
|
Handlers: handlers,
|
||||||
|
Router: router,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) SetupRoutes() {
|
func (s *Server) Start() error {
|
||||||
// Proxy
|
|
||||||
api.Router.GET("/api/auth/:proxy", api.Handlers.AuthHandler)
|
|
||||||
|
|
||||||
// Auth
|
|
||||||
api.Router.POST("/api/login", api.Handlers.LoginHandler)
|
|
||||||
api.Router.POST("/api/totp", api.Handlers.TotpHandler)
|
|
||||||
api.Router.POST("/api/logout", api.Handlers.LogoutHandler)
|
|
||||||
|
|
||||||
// Context
|
|
||||||
api.Router.GET("/api/app", api.Handlers.AppHandler)
|
|
||||||
api.Router.GET("/api/user", api.Handlers.UserHandler)
|
|
||||||
|
|
||||||
// OAuth
|
|
||||||
api.Router.GET("/api/oauth/url/:provider", api.Handlers.OauthUrlHandler)
|
|
||||||
api.Router.GET("/api/oauth/callback/:provider", api.Handlers.OauthCallbackHandler)
|
|
||||||
|
|
||||||
// App
|
|
||||||
api.Router.GET("/api/healthcheck", api.Handlers.HealthcheckHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) Run() {
|
|
||||||
log.Info().Str("address", api.Config.Address).Int("port", api.Config.Port).Msg("Starting server")
|
|
||||||
|
|
||||||
// Run server
|
// Run server
|
||||||
err := api.Router.Run(fmt.Sprintf("%s:%d", api.Config.Address, api.Config.Port))
|
log.Info().Str("address", s.Config.Address).Int("port", s.Config.Port).Msg("Starting server")
|
||||||
|
|
||||||
// Check for errors
|
return s.Router.Run(fmt.Sprintf("%s:%d", s.Config.Address, s.Config.Port))
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Failed to start server")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// zerolog is a middleware for gin that logs requests using zerolog
|
// zerolog is a middleware for gin that logs requests using zerolog
|
||||||
521
internal/server/server_test.go
Normal file
521
internal/server/server_test.go
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
package server_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
"tinyauth/internal/auth"
|
||||||
|
"tinyauth/internal/docker"
|
||||||
|
"tinyauth/internal/handlers"
|
||||||
|
"tinyauth/internal/hooks"
|
||||||
|
"tinyauth/internal/providers"
|
||||||
|
"tinyauth/internal/server"
|
||||||
|
"tinyauth/internal/types"
|
||||||
|
|
||||||
|
"github.com/magiconair/properties/assert"
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Simple server config for tests
|
||||||
|
var serverConfig = types.ServerConfig{
|
||||||
|
Port: 8080,
|
||||||
|
Address: "0.0.0.0",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple handlers config for tests
|
||||||
|
var handlersConfig = types.HandlersConfig{
|
||||||
|
AppURL: "http://localhost:8080",
|
||||||
|
Domain: "localhost",
|
||||||
|
DisableContinue: false,
|
||||||
|
CookieSecure: false,
|
||||||
|
Title: "Tinyauth",
|
||||||
|
GenericName: "Generic",
|
||||||
|
ForgotPasswordMessage: "Message",
|
||||||
|
CsrfCookieName: "tinyauth-csrf",
|
||||||
|
RedirectCookieName: "tinyauth-redirect",
|
||||||
|
BackgroundImage: "https://example.com/image.png",
|
||||||
|
OAuthAutoRedirect: "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple auth config for tests
|
||||||
|
var authConfig = types.AuthConfig{
|
||||||
|
Users: types.Users{},
|
||||||
|
OauthWhitelist: "",
|
||||||
|
HMACSecret: "4bZ9K.*:;zH=,9zG!meUxu.B5-S[7.V.", // Complex on purpose
|
||||||
|
EncryptionSecret: "\\:!R(u[Sbv6ZLm.7es)H|OqH4y}0u\\rj",
|
||||||
|
CookieSecure: false,
|
||||||
|
SessionExpiry: 3600,
|
||||||
|
LoginTimeout: 0,
|
||||||
|
LoginMaxRetries: 0,
|
||||||
|
SessionCookieName: "tinyauth-session",
|
||||||
|
Domain: "localhost",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple hooks config for tests
|
||||||
|
var hooksConfig = types.HooksConfig{
|
||||||
|
Domain: "localhost",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cookie
|
||||||
|
var cookie = "MTc1MTkyMzM5MnxiME9aTzlGQjZMNEJMdDZMc0lHMk9zcXQyME9SR1ZnUmlaYWZNcWplek5vcVNpdkdHRTZqb09YWkVUYUN6NEt4MkEyOGEyX2hFQWZEUEYtbllDX0h5eDBCb3VyT2phQlRpZWFfRFdTMGw2WUg2VWw4RGdNbEhQclotOUJjblJGaWFQcmhyaWFna0dXRWNud2c1akg5eEpLZ3JzS0pfWktscVZyckZFR1VDX0R5QjFOT0hzMTNKb18ySEMxZlluSWNxa1ByM0VhSzNyMkRtdDNORWJXVGFYSnMzWjFGa0lrZlhSTWduRmttMHhQUXN4UFhNbHFXY0lBWjBnUWpKU0xXMHRubjlKbjV0LXBGdjk0MmpJX0xMX1ZYblVJVW9LWUJoWmpNanVXNkNjamhYWlR2V29rY0RNYWkxY2lMQnpqLUI2cHMyYTZkWWgtWnlFdGN0amh2WURUeUNGT3ZLS1FJVUFIb0NWR1RPMlRtY2c9PXwerwFtb9urOXnwA02qXbLeorMloaK_paQd0in4BAesmg=="
|
||||||
|
|
||||||
|
// User
|
||||||
|
var user = types.User{
|
||||||
|
Username: "user",
|
||||||
|
Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the server for tests
|
||||||
|
func getServer(t *testing.T) *server.Server {
|
||||||
|
// Create docker service
|
||||||
|
docker, err := docker.NewDocker()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to initialize docker: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create auth service
|
||||||
|
authConfig.Users = types.Users{
|
||||||
|
{
|
||||||
|
Username: user.Username,
|
||||||
|
Password: user.Password,
|
||||||
|
TotpSecret: user.TotpSecret,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
auth := auth.NewAuth(authConfig, docker, nil)
|
||||||
|
|
||||||
|
// Create providers service
|
||||||
|
providers := providers.NewProviders(types.OAuthConfig{})
|
||||||
|
|
||||||
|
// Create hooks service
|
||||||
|
hooks := hooks.NewHooks(hooksConfig, auth, providers)
|
||||||
|
|
||||||
|
// Create handlers service
|
||||||
|
handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
|
||||||
|
|
||||||
|
// Create server
|
||||||
|
srv, err := server.NewServer(serverConfig, handlers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the server
|
||||||
|
return srv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test login
|
||||||
|
func TestLogin(t *testing.T) {
|
||||||
|
t.Log("Testing login")
|
||||||
|
|
||||||
|
// Get server
|
||||||
|
srv := getServer(t)
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
user := types.LoginRequest{
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass",
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := json.Marshal(user)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error marshalling json: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("POST", "/api/login", strings.NewReader(string(json)))
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
|
||||||
|
// Get the result cookie
|
||||||
|
cookies := recorder.Result().Cookies()
|
||||||
|
|
||||||
|
// Check if the cookie is set
|
||||||
|
if len(cookies) == 0 {
|
||||||
|
t.Fatalf("Cookie not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie for further tests
|
||||||
|
cookie = cookies[0].Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test app context
|
||||||
|
func TestAppContext(t *testing.T) {
|
||||||
|
t.Log("Testing app context")
|
||||||
|
|
||||||
|
// Get server
|
||||||
|
srv := getServer(t)
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("GET", "/api/app", nil)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "tinyauth",
|
||||||
|
Value: cookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
|
||||||
|
// Read the body of the response
|
||||||
|
body, err := io.ReadAll(recorder.Body)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the body into the user struct
|
||||||
|
var app types.AppContext
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &app)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error unmarshalling body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create tests values
|
||||||
|
expected := types.AppContext{
|
||||||
|
Status: 200,
|
||||||
|
Message: "OK",
|
||||||
|
ConfiguredProviders: []string{"username"},
|
||||||
|
DisableContinue: false,
|
||||||
|
Title: "Tinyauth",
|
||||||
|
GenericName: "Generic",
|
||||||
|
ForgotPasswordMessage: "Message",
|
||||||
|
BackgroundImage: "https://example.com/image.png",
|
||||||
|
OAuthAutoRedirect: "none",
|
||||||
|
Domain: "localhost",
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should get the username back
|
||||||
|
if !reflect.DeepEqual(app, expected) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, app)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test user context
|
||||||
|
func TestUserContext(t *testing.T) {
|
||||||
|
// Refresh the cookie
|
||||||
|
TestLogin(t)
|
||||||
|
|
||||||
|
t.Log("Testing user context")
|
||||||
|
|
||||||
|
// Get server
|
||||||
|
srv := getServer(t)
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("GET", "/api/user", nil)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "tinyauth-session",
|
||||||
|
Value: cookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
|
||||||
|
// Read the body of the response
|
||||||
|
body, err := io.ReadAll(recorder.Body)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the body into the user struct
|
||||||
|
type User struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var user User
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &user)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error unmarshalling body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should get the username back
|
||||||
|
if user.Username != "user" {
|
||||||
|
t.Fatalf("Expected user, got %s", user.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test logout
|
||||||
|
func TestLogout(t *testing.T) {
|
||||||
|
// Refresh the cookie
|
||||||
|
TestLogin(t)
|
||||||
|
|
||||||
|
t.Log("Testing logout")
|
||||||
|
|
||||||
|
// Get server
|
||||||
|
srv := getServer(t)
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("POST", "/api/logout", nil)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "tinyauth-session",
|
||||||
|
Value: cookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
|
||||||
|
// Check if the cookie is different (means the cookie is gone)
|
||||||
|
if recorder.Result().Cookies()[0].Value == cookie {
|
||||||
|
t.Fatalf("Cookie not flushed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test auth endpoint
|
||||||
|
func TestAuth(t *testing.T) {
|
||||||
|
// Refresh the cookie
|
||||||
|
TestLogin(t)
|
||||||
|
|
||||||
|
t.Log("Testing auth endpoint")
|
||||||
|
|
||||||
|
// Get server
|
||||||
|
srv := getServer(t)
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("GET", "/api/auth/traefik", nil)
|
||||||
|
|
||||||
|
// Set the accept header
|
||||||
|
req.Header.Set("Accept", "text/html")
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusTemporaryRedirect)
|
||||||
|
|
||||||
|
// Recreate recorder
|
||||||
|
recorder = httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Recreate the request
|
||||||
|
req, err = http.NewRequest("GET", "/api/auth/traefik", nil)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with the cookie
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "tinyauth-session",
|
||||||
|
Value: cookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve the request again
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
|
||||||
|
// Recreate recorder
|
||||||
|
recorder = httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Recreate the request
|
||||||
|
req, err = http.NewRequest("GET", "/api/auth/nginx", nil)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the request again
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusUnauthorized)
|
||||||
|
|
||||||
|
// Recreate recorder
|
||||||
|
recorder = httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Recreate the request
|
||||||
|
req, err = http.NewRequest("GET", "/api/auth/nginx", nil)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with the cookie
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "tinyauth-session",
|
||||||
|
Value: cookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve the request again
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTOTP(t *testing.T) {
|
||||||
|
t.Log("Testing TOTP")
|
||||||
|
|
||||||
|
// Generate totp secret
|
||||||
|
key, err := totp.Generate(totp.GenerateOpts{
|
||||||
|
Issuer: "Tinyauth",
|
||||||
|
AccountName: user.Username,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to generate TOTP secret: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create secret
|
||||||
|
secret := key.Secret()
|
||||||
|
|
||||||
|
// Set the user's TOTP secret
|
||||||
|
user.TotpSecret = secret
|
||||||
|
|
||||||
|
// Get server
|
||||||
|
srv := getServer(t)
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
user := types.LoginRequest{
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass",
|
||||||
|
}
|
||||||
|
|
||||||
|
loginJson, err := json.Marshal(user)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error marshalling json: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err := http.NewRequest("POST", "/api/login", strings.NewReader(string(loginJson)))
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
|
||||||
|
// Set the cookie for next test
|
||||||
|
cookie = recorder.Result().Cookies()[0].Value
|
||||||
|
|
||||||
|
// Create TOTP code
|
||||||
|
code, err := totp.GenerateCode(secret, time.Now())
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to generate TOTP code: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create TOTP request
|
||||||
|
totpRequest := types.TotpRequest{
|
||||||
|
Code: code,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal the TOTP request
|
||||||
|
totpJson, err := json.Marshal(totpRequest)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error marshalling TOTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create recorder
|
||||||
|
recorder = httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
req, err = http.NewRequest("POST", "/api/totp", strings.NewReader(string(totpJson)))
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the cookie
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "tinyauth-session",
|
||||||
|
Value: cookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve the request
|
||||||
|
srv.Router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert.Equal(t, recorder.Code, http.StatusOK)
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ type UnauthorizedQuery struct {
|
|||||||
Username string `url:"username"`
|
Username string `url:"username"`
|
||||||
Resource string `url:"resource"`
|
Resource string `url:"resource"`
|
||||||
GroupErr bool `url:"groupErr"`
|
GroupErr bool `url:"groupErr"`
|
||||||
|
IP string `url:"ip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy is the uri parameters for the proxy endpoint
|
// Proxy is the uri parameters for the proxy endpoint
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type Config struct {
|
|||||||
GenericTokenURL string `mapstructure:"generic-token-url"`
|
GenericTokenURL string `mapstructure:"generic-token-url"`
|
||||||
GenericUserURL string `mapstructure:"generic-user-url"`
|
GenericUserURL string `mapstructure:"generic-user-url"`
|
||||||
GenericName string `mapstructure:"generic-name"`
|
GenericName string `mapstructure:"generic-name"`
|
||||||
|
GenericSkipSSL bool `mapstructure:"generic-skip-ssl"`
|
||||||
DisableContinue bool `mapstructure:"disable-continue"`
|
DisableContinue bool `mapstructure:"disable-continue"`
|
||||||
OAuthWhitelist string `mapstructure:"oauth-whitelist"`
|
OAuthWhitelist string `mapstructure:"oauth-whitelist"`
|
||||||
OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"`
|
OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"`
|
||||||
@@ -33,8 +34,14 @@ type Config struct {
|
|||||||
EnvFile string `mapstructure:"env-file"`
|
EnvFile string `mapstructure:"env-file"`
|
||||||
LoginTimeout int `mapstructure:"login-timeout"`
|
LoginTimeout int `mapstructure:"login-timeout"`
|
||||||
LoginMaxRetries int `mapstructure:"login-max-retries"`
|
LoginMaxRetries int `mapstructure:"login-max-retries"`
|
||||||
FogotPasswordMessage string `mapstructure:"forgot-password-message" validate:"required"`
|
FogotPasswordMessage string `mapstructure:"forgot-password-message"`
|
||||||
BackgroundImage string `mapstructure:"background-image" validate:"required,url"`
|
BackgroundImage string `mapstructure:"background-image" validate:"required"`
|
||||||
|
LdapAddress string `mapstructure:"ldap-address"`
|
||||||
|
LdapBindDN string `mapstructure:"ldap-bind-dn"`
|
||||||
|
LdapBindPassword string `mapstructure:"ldap-bind-password"`
|
||||||
|
LdapBaseDN string `mapstructure:"ldap-base-dn"`
|
||||||
|
LdapInsecure bool `mapstructure:"ldap-insecure"`
|
||||||
|
LdapSearchFilter string `mapstructure:"ldap-search-filter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server configuration
|
// Server configuration
|
||||||
@@ -64,11 +71,12 @@ type OAuthConfig struct {
|
|||||||
GenericAuthURL string
|
GenericAuthURL string
|
||||||
GenericTokenURL string
|
GenericTokenURL string
|
||||||
GenericUserURL string
|
GenericUserURL string
|
||||||
|
GenericSkipSSL bool
|
||||||
AppURL string
|
AppURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIConfig is the configuration for the API
|
// ServerConfig is the configuration for the server
|
||||||
type APIConfig struct {
|
type ServerConfig struct {
|
||||||
Port int
|
Port int
|
||||||
Address string
|
Address string
|
||||||
}
|
}
|
||||||
@@ -78,15 +86,62 @@ type AuthConfig struct {
|
|||||||
Users Users
|
Users Users
|
||||||
OauthWhitelist string
|
OauthWhitelist string
|
||||||
SessionExpiry int
|
SessionExpiry int
|
||||||
Secret string
|
|
||||||
CookieSecure bool
|
CookieSecure bool
|
||||||
Domain string
|
Domain string
|
||||||
LoginTimeout int
|
LoginTimeout int
|
||||||
LoginMaxRetries int
|
LoginMaxRetries int
|
||||||
SessionCookieName string
|
SessionCookieName string
|
||||||
|
HMACSecret string
|
||||||
|
EncryptionSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HooksConfig is the configuration for the hooks service
|
// HooksConfig is the configuration for the hooks service
|
||||||
type HooksConfig struct {
|
type HooksConfig struct {
|
||||||
Domain string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OAuthLabels is a list of labels that can be used in a tinyauth protected container
|
||||||
|
type OAuthLabels struct {
|
||||||
|
Whitelist string
|
||||||
|
Groups string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic auth labels for a tinyauth protected container
|
||||||
|
type BasicLabels struct {
|
||||||
|
Username string
|
||||||
|
Password PassowrdLabels
|
||||||
|
}
|
||||||
|
|
||||||
|
// PassowrdLabels is a struct that contains the password labels for a tinyauth protected container
|
||||||
|
type PassowrdLabels struct {
|
||||||
|
Plain string
|
||||||
|
File string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP labels for a tinyauth protected container
|
||||||
|
type IPLabels struct {
|
||||||
|
Allow []string
|
||||||
|
Block []string
|
||||||
|
Bypass []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Labels is a struct that contains the labels for a tinyauth protected container
|
||||||
|
type Labels struct {
|
||||||
|
Users string
|
||||||
|
Allowed string
|
||||||
|
Headers []string
|
||||||
|
Domain []string
|
||||||
|
Basic BasicLabels
|
||||||
|
OAuth OAuthLabels
|
||||||
|
IP IPLabels
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ldap config is a struct that contains the configuration for the LDAP service
|
||||||
|
type LdapConfig struct {
|
||||||
|
Address string
|
||||||
|
BindDN string
|
||||||
|
BindPassword string
|
||||||
|
BaseDN string
|
||||||
|
Insecure bool
|
||||||
|
SearchFilter string
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ type User struct {
|
|||||||
TotpSecret string
|
TotpSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserSearch is the response of the get user
|
||||||
|
type UserSearch struct {
|
||||||
|
Username string
|
||||||
|
Type string // "local", "ldap" or empty
|
||||||
|
}
|
||||||
|
|
||||||
// Users is a list of users
|
// Users is a list of users
|
||||||
type Users []User
|
type Users []User
|
||||||
|
|
||||||
@@ -32,15 +38,6 @@ type SessionCookie struct {
|
|||||||
OAuthGroups string
|
OAuthGroups string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TinyauthLabels is the labels for the tinyauth container
|
|
||||||
type TinyauthLabels struct {
|
|
||||||
OAuthWhitelist string
|
|
||||||
Users string
|
|
||||||
Allowed string
|
|
||||||
Headers map[string]string
|
|
||||||
OAuthGroups string
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserContext is the context for the user
|
// UserContext is the context for the user
|
||||||
type UserContext struct {
|
type UserContext struct {
|
||||||
Username string
|
Username string
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"tinyauth/internal/constants"
|
|
||||||
"tinyauth/internal/types"
|
"tinyauth/internal/types"
|
||||||
|
|
||||||
|
"github.com/traefik/paerser/parser"
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@@ -174,45 +180,43 @@ func GetUsers(conf string, file string) (types.Users, error) {
|
|||||||
return ParseUsers(users)
|
return ParseUsers(users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the docker labels to the tinyauth labels struct
|
// Parse the headers in a map[string]string format
|
||||||
func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels {
|
func ParseHeaders(headers []string) map[string]string {
|
||||||
// Create a new tinyauth labels struct
|
// Create a map to store the headers
|
||||||
var tinyauthLabels types.TinyauthLabels
|
headerMap := make(map[string]string)
|
||||||
|
|
||||||
// Loop through the labels
|
// Loop through the headers
|
||||||
for label, value := range labels {
|
for _, header := range headers {
|
||||||
|
split := strings.SplitN(header, "=", 2)
|
||||||
// Check if the label is in the tinyauth labels
|
if len(split) != 2 || strings.TrimSpace(split[0]) == "" || strings.TrimSpace(split[1]) == "" {
|
||||||
if slices.Contains(constants.TinyauthLabels, label) {
|
log.Warn().Str("header", header).Msg("Invalid header format, skipping")
|
||||||
|
continue
|
||||||
log.Debug().Str("label", label).Msg("Found label")
|
|
||||||
|
|
||||||
// Add the label value to the tinyauth labels struct
|
|
||||||
switch label {
|
|
||||||
case "tinyauth.oauth.whitelist":
|
|
||||||
tinyauthLabels.OAuthWhitelist = value
|
|
||||||
case "tinyauth.users":
|
|
||||||
tinyauthLabels.Users = value
|
|
||||||
case "tinyauth.allowed":
|
|
||||||
tinyauthLabels.Allowed = value
|
|
||||||
case "tinyauth.headers":
|
|
||||||
tinyauthLabels.Headers = make(map[string]string)
|
|
||||||
headers := strings.Split(value, ",")
|
|
||||||
for _, header := range headers {
|
|
||||||
headerSplit := strings.Split(header, "=")
|
|
||||||
if len(headerSplit) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tinyauthLabels.Headers[headerSplit[0]] = headerSplit[1]
|
|
||||||
}
|
|
||||||
case "tinyauth.oauth.groups":
|
|
||||||
tinyauthLabels.OAuthGroups = value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
key := SanitizeHeader(strings.TrimSpace(split[0]))
|
||||||
|
value := SanitizeHeader(strings.TrimSpace(split[1]))
|
||||||
|
headerMap[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the tinyauth labels
|
// Return the header map
|
||||||
return tinyauthLabels
|
return headerMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get labels parses a map of labels into a struct with only the needed labels
|
||||||
|
func GetLabels(labels map[string]string) (types.Labels, error) {
|
||||||
|
// Create a new labels struct
|
||||||
|
var labelsParsed types.Labels
|
||||||
|
|
||||||
|
// Decode the labels into the labels struct
|
||||||
|
err := parser.Decode(labels, &labelsParsed, "tinyauth", "tinyauth.users", "tinyauth.allowed", "tinyauth.headers", "tinyauth.domain", "tinyauth.basic", "tinyauth.oauth", "tinyauth.ip")
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error parsing labels")
|
||||||
|
return types.Labels{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the labels struct
|
||||||
|
return labelsParsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any of the OAuth providers are configured based on the client id and secret
|
// Check if any of the OAuth providers are configured based on the client id and secret
|
||||||
@@ -255,16 +259,16 @@ func ParseUser(user string) (types.User, error) {
|
|||||||
// Check if the user has a totp secret
|
// Check if the user has a totp secret
|
||||||
if len(userSplit) == 2 {
|
if len(userSplit) == 2 {
|
||||||
return types.User{
|
return types.User{
|
||||||
Username: userSplit[0],
|
Username: strings.TrimSpace(userSplit[0]),
|
||||||
Password: userSplit[1],
|
Password: strings.TrimSpace(userSplit[1]),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the user struct
|
// Return the user struct
|
||||||
return types.User{
|
return types.User{
|
||||||
Username: userSplit[0],
|
Username: strings.TrimSpace(userSplit[0]),
|
||||||
Password: userSplit[1],
|
Password: strings.TrimSpace(userSplit[1]),
|
||||||
TotpSecret: userSplit[2],
|
TotpSecret: strings.TrimSpace(userSplit[2]),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,17 +292,17 @@ func ParseSecretFile(contents string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a string matches a regex or a whitelist
|
// Check if a string matches a regex or if it is included in a comma separated list
|
||||||
func CheckWhitelist(whitelist string, str string) bool {
|
func CheckFilter(filter string, str string) bool {
|
||||||
// Check if the whitelist is empty
|
// Check if the filter is empty
|
||||||
if len(strings.TrimSpace(whitelist)) == 0 {
|
if len(strings.TrimSpace(filter)) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the whitelist is a regex
|
// Check if the filter is a regex
|
||||||
if strings.HasPrefix(whitelist, "/") && strings.HasSuffix(whitelist, "/") {
|
if strings.HasPrefix(filter, "/") && strings.HasSuffix(filter, "/") {
|
||||||
// Create regex
|
// Create regex
|
||||||
re, err := regexp.Compile(whitelist[1 : len(whitelist)-1])
|
re, err := regexp.Compile(filter[1 : len(filter)-1])
|
||||||
|
|
||||||
// Check if there was an error
|
// Check if there was an error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -312,11 +316,11 @@ func CheckWhitelist(whitelist string, str string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split the whitelist by comma
|
// Split the filter by comma
|
||||||
whitelistSplit := strings.Split(whitelist, ",")
|
filterSplit := strings.Split(filter, ",")
|
||||||
|
|
||||||
// Loop through the whitelist
|
// Loop through the filter items
|
||||||
for _, item := range whitelistSplit {
|
for _, item := range filterSplit {
|
||||||
// Check if the item matches with the string
|
// Check if the item matches with the string
|
||||||
if strings.TrimSpace(item) == str {
|
if strings.TrimSpace(item) == str {
|
||||||
return true
|
return true
|
||||||
@@ -360,3 +364,77 @@ func GenerateIdentifier(str string) string {
|
|||||||
// Convert the UUID to a string
|
// Convert the UUID to a string
|
||||||
return strings.Split(uuidString, "-")[0]
|
return strings.Split(uuidString, "-")[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a basic auth header from a username and password
|
||||||
|
func GetBasicAuth(username string, password string) string {
|
||||||
|
// Create the auth string
|
||||||
|
auth := username + ":" + password
|
||||||
|
|
||||||
|
// Encode the auth string to base64
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(auth))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if an IP is contained in a CIDR range/matches a single IP
|
||||||
|
func FilterIP(filter string, ip string) (bool, error) {
|
||||||
|
// Convert the check IP to an IP instance
|
||||||
|
ipAddr := net.ParseIP(ip)
|
||||||
|
|
||||||
|
// Check if the filter is a CIDR range
|
||||||
|
if strings.Contains(filter, "/") {
|
||||||
|
// Parse the CIDR range
|
||||||
|
_, cidr, err := net.ParseCIDR(filter)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the IP is in the CIDR range
|
||||||
|
return cidr.Contains(ipAddr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the filter as a single IP
|
||||||
|
ipFilter := net.ParseIP(filter)
|
||||||
|
|
||||||
|
// Check if the IP is valid
|
||||||
|
if ipFilter == nil {
|
||||||
|
return false, errors.New("invalid IP address in filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the IP matches the filter
|
||||||
|
if ipFilter.Equal(ipAddr) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the filter is not a CIDR range or a single IP, return false
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeriveKey(secret string, info string) (string, error) {
|
||||||
|
// Create hashing function
|
||||||
|
hash := sha256.New
|
||||||
|
|
||||||
|
// Create a new key using the secret and info
|
||||||
|
hkdf := hkdf.New(hash, []byte(secret), nil, []byte(info)) // I am not using a salt because I just want two different keys from one secret, maybe bad practice
|
||||||
|
|
||||||
|
// Create a new key
|
||||||
|
key := make([]byte, 24)
|
||||||
|
|
||||||
|
// Read the key from the HKDF
|
||||||
|
_, err := io.ReadFull(hkdf, key)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the key is not empty
|
||||||
|
if bytes.Equal(key, make([]byte, 24)) {
|
||||||
|
return "", errors.New("derived key is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the key to base64
|
||||||
|
encodedKey := base64.StdEncoding.EncodeToString(key)
|
||||||
|
|
||||||
|
// Return the key as a base64 encoded string
|
||||||
|
return encodedKey, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -279,48 +279,35 @@ func TestGetUsers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the tinyauth labels function
|
// Test the get labels function
|
||||||
func TestGetTinyauthLabels(t *testing.T) {
|
func TestGetLabels(t *testing.T) {
|
||||||
t.Log("Testing get tinyauth labels with a valid map")
|
t.Log("Testing get labels with a valid map")
|
||||||
|
|
||||||
// Test the get tinyauth labels function with a valid map
|
// Test the get tinyauth labels function with a valid map
|
||||||
labels := map[string]string{
|
labels := map[string]string{
|
||||||
"tinyauth.users": "user1,user2",
|
"tinyauth.users": "user1,user2",
|
||||||
"tinyauth.oauth.whitelist": "/regex/",
|
"tinyauth.oauth.whitelist": "/regex/",
|
||||||
"tinyauth.allowed": "random",
|
"tinyauth.allowed": "random",
|
||||||
"random": "random",
|
|
||||||
"tinyauth.headers": "X-Header=value",
|
"tinyauth.headers": "X-Header=value",
|
||||||
|
"tinyauth.oauth.groups": "group1,group2",
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := types.TinyauthLabels{
|
expected := types.Labels{
|
||||||
Users: "user1,user2",
|
Users: "user1,user2",
|
||||||
OAuthWhitelist: "/regex/",
|
Allowed: "random",
|
||||||
Allowed: "random",
|
Headers: []string{"X-Header=value"},
|
||||||
Headers: map[string]string{
|
OAuth: types.OAuthLabels{
|
||||||
"X-Header": "value",
|
Whitelist: "/regex/",
|
||||||
|
Groups: "group1,group2",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
result := utils.GetTinyauthLabels(labels)
|
result, err := utils.GetLabels(labels)
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if there was an error
|
||||||
if !reflect.DeepEqual(expected, result) {
|
if err != nil {
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
t.Fatalf("Error getting labels: %v", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Test the filter function
|
|
||||||
func TestFilter(t *testing.T) {
|
|
||||||
t.Log("Testing filter helper")
|
|
||||||
|
|
||||||
// Create variables
|
|
||||||
data := []string{"", "val1", "", "val2", "", "val3", ""}
|
|
||||||
expected := []string{"val1", "val2", "val3"}
|
|
||||||
|
|
||||||
// Test the filter function
|
|
||||||
result := utils.Filter(data, func(val string) bool {
|
|
||||||
return val != ""
|
|
||||||
})
|
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if the result is equal to the expected
|
||||||
if !reflect.DeepEqual(expected, result) {
|
if !reflect.DeepEqual(expected, result) {
|
||||||
@@ -390,108 +377,77 @@ func TestParseUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the whitelist function
|
// Test the check filter function
|
||||||
func TestCheckWhitelist(t *testing.T) {
|
func TestCheckFilter(t *testing.T) {
|
||||||
t.Log("Testing check whitelist with a comma whitelist")
|
t.Log("Testing check filter with a comma separated list")
|
||||||
|
|
||||||
// Create variables
|
// Create variables
|
||||||
whitelist := "user1,user2,user3"
|
filter := "user1,user2,user3"
|
||||||
str := "user1"
|
str := "user1"
|
||||||
expected := true
|
expected := true
|
||||||
|
|
||||||
// Test the check whitelist function
|
// Test the check filter function
|
||||||
result := utils.CheckWhitelist(whitelist, str)
|
result := utils.CheckFilter(filter, str)
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if the result is equal to the expected
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Testing check whitelist with a regex whitelist")
|
t.Log("Testing check filter with a regex filter")
|
||||||
|
|
||||||
// Create variables
|
// Create variables
|
||||||
whitelist = "/^user[0-9]+$/"
|
filter = "/^user[0-9]+$/"
|
||||||
str = "user1"
|
str = "user1"
|
||||||
expected = true
|
expected = true
|
||||||
|
|
||||||
// Test the check whitelist function
|
// Test the check filter function
|
||||||
result = utils.CheckWhitelist(whitelist, str)
|
result = utils.CheckFilter(filter, str)
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if the result is equal to the expected
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Testing check whitelist with an empty whitelist")
|
t.Log("Testing check filter with an empty filter")
|
||||||
|
|
||||||
// Create variables
|
// Create variables
|
||||||
whitelist = ""
|
filter = ""
|
||||||
str = "user1"
|
str = "user1"
|
||||||
expected = true
|
expected = true
|
||||||
|
|
||||||
// Test the check whitelist function
|
// Test the check filter function
|
||||||
result = utils.CheckWhitelist(whitelist, str)
|
result = utils.CheckFilter(filter, str)
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if the result is equal to the expected
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Testing check whitelist with an invalid regex whitelist")
|
t.Log("Testing check filter with an invalid regex filter")
|
||||||
|
|
||||||
// Create variables
|
// Create variables
|
||||||
whitelist = "/^user[0-9+$/"
|
filter = "/^user[0-9+$/"
|
||||||
str = "user1"
|
str = "user1"
|
||||||
expected = false
|
expected = false
|
||||||
|
|
||||||
// Test the check whitelist function
|
// Test the check filter function
|
||||||
result = utils.CheckWhitelist(whitelist, str)
|
result = utils.CheckFilter(filter, str)
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if the result is equal to the expected
|
||||||
if result != expected {
|
if result != expected {
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Testing check whitelist with a non matching whitelist")
|
t.Log("Testing check filter with a non matching list")
|
||||||
|
|
||||||
// Create variables
|
// Create variables
|
||||||
whitelist = "user1,user2,user3"
|
filter = "user1,user2,user3"
|
||||||
str = "user4"
|
str = "user4"
|
||||||
expected = false
|
expected = false
|
||||||
|
|
||||||
// Test the check whitelist function
|
// Test the check filter function
|
||||||
result = utils.CheckWhitelist(whitelist, str)
|
result = utils.CheckFilter(filter, str)
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test capitalize
|
|
||||||
func TestCapitalize(t *testing.T) {
|
|
||||||
t.Log("Testing capitalize with a valid string")
|
|
||||||
|
|
||||||
// Create variables
|
|
||||||
str := "test"
|
|
||||||
expected := "Test"
|
|
||||||
|
|
||||||
// Test the capitalize function
|
|
||||||
result := utils.Capitalize(str)
|
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("Testing capitalize with an empty string")
|
|
||||||
|
|
||||||
// Create variables
|
|
||||||
str = ""
|
|
||||||
expected = ""
|
|
||||||
|
|
||||||
// Test the capitalize function
|
|
||||||
result = utils.Capitalize(str)
|
|
||||||
|
|
||||||
// Check if the result is equal to the expected
|
// Check if the result is equal to the expected
|
||||||
if result != expected {
|
if result != expected {
|
||||||
@@ -529,3 +485,170 @@ func TestSanitizeHeader(t *testing.T) {
|
|||||||
t.Fatalf("Expected %v, got %v", expected, result)
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the parse headers function
|
||||||
|
func TestParseHeaders(t *testing.T) {
|
||||||
|
t.Log("Testing parse headers with a valid string")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
headers := []string{"X-Hea\x00der1=value1", "X-Header2=value\n2"}
|
||||||
|
expected := map[string]string{
|
||||||
|
"X-Header1": "value1",
|
||||||
|
"X-Header2": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the parse headers function
|
||||||
|
result := utils.ParseHeaders(headers)
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if !reflect.DeepEqual(expected, result) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Testing parse headers with an invalid string")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
headers = []string{"X-Header1=", "X-Header2", "=value", "X-Header3=value3"}
|
||||||
|
expected = map[string]string{"X-Header3": "value3"}
|
||||||
|
|
||||||
|
// Test the parse headers function
|
||||||
|
result = utils.ParseHeaders(headers)
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if !reflect.DeepEqual(expected, result) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the parse secret file function
|
||||||
|
func TestParseSecretFile(t *testing.T) {
|
||||||
|
t.Log("Testing parse secret file with a valid file")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
content := "\n\n \n\n\n secret \n\n \n "
|
||||||
|
expected := "secret"
|
||||||
|
|
||||||
|
// Test the parse secret file function
|
||||||
|
result := utils.ParseSecretFile(content)
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the filter IP function
|
||||||
|
func TestFilterIP(t *testing.T) {
|
||||||
|
t.Log("Testing filter IP with an IP and a valid CIDR")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
ip := "10.10.10.10"
|
||||||
|
filter := "10.10.10.0/24"
|
||||||
|
expected := true
|
||||||
|
|
||||||
|
// Test the filter IP function
|
||||||
|
result, err := utils.FilterIP(filter, ip)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error filtering IP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Testing filter IP with an IP and a valid IP")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
filter = "10.10.10.10"
|
||||||
|
expected = true
|
||||||
|
|
||||||
|
// Test the filter IP function
|
||||||
|
result, err = utils.FilterIP(filter, ip)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error filtering IP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Testing filter IP with an IP and an non matching CIDR")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
filter = "10.10.15.0/24"
|
||||||
|
expected = false
|
||||||
|
|
||||||
|
// Test the filter IP function
|
||||||
|
result, err = utils.FilterIP(filter, ip)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error filtering IP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Testing filter IP with a non matching IP and a valid CIDR")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
filter = "10.10.10.11"
|
||||||
|
expected = false
|
||||||
|
|
||||||
|
// Test the filter IP function
|
||||||
|
result, err = utils.FilterIP(filter, ip)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error filtering IP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Testing filter IP with an IP and an invalid CIDR")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
filter = "10.../83"
|
||||||
|
|
||||||
|
// Test the filter IP function
|
||||||
|
_, err = utils.FilterIP(filter, ip)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error filtering IP")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the derive key function
|
||||||
|
func TestDeriveKey(t *testing.T) {
|
||||||
|
t.Log("Testing the derive key function")
|
||||||
|
|
||||||
|
// Create variables
|
||||||
|
master := "master"
|
||||||
|
info := "info"
|
||||||
|
expected := "gdrdU/fXzclYjiSXRexEatVgV13qQmKl"
|
||||||
|
|
||||||
|
// Test the derive key function
|
||||||
|
result, err := utils.DeriveKey(master, info)
|
||||||
|
|
||||||
|
// Check if there was an error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error deriving key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the result is equal to the expected
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user