Compare commits

...

217 Commits

Author SHA1 Message Date
0e21f3eab6 Add missing mock 2023-03-22 03:07:33 +01:00
9fcf692cb8 Bump version 2023-03-22 03:03:24 +01:00
da577ea3cc Add stateChecker to password context 2023-03-22 03:02:44 +01:00
6ae1d8938a Fix eslint 2023-03-22 03:00:44 +01:00
3e18a7390c Bump version: Release v7 🚀 2023-03-22 01:55:49 +01:00
5f43f1afc6 Shot a new post build tutorial video 2023-03-22 01:46:05 +01:00
2fc9c03430 Relase candidate 2023-03-21 23:39:40 +01:00
d951a9ba02 Improve post build instructions 2023-03-21 23:21:30 +01:00
93385af675 Release candidate 2023-03-21 19:44:41 +01:00
dd75d0ece7 Account theme specific instructions 2023-03-21 19:44:01 +01:00
dcd37ed916 Relase candidate 2023-03-21 18:32:21 +01:00
2e4d722d7f Return undefined if the context dosen't match the theme 2023-03-21 18:32:00 +01:00
09543400ca Relase candidate 2023-03-21 16:47:58 +01:00
8b101e5043 Fix error in log related to getLogoUrl 2023-03-21 16:47:30 +01:00
b31fff9c2b Release candidate 2023-03-21 15:16:37 +01:00
0c5b100dd9 Update post build instructions 2023-03-21 15:16:23 +01:00
8937d19891 Release candidate 2023-03-21 14:21:27 +01:00
0fdd9e75a6 Fix the helper type that enable to extend the KcContext 2023-03-21 14:21:09 +01:00
77da00c2c5 Release candidate 2023-03-21 06:05:30 +01:00
3744080d11 Relase candidate 2023-03-21 05:27:53 +01:00
c9e546a8fd Fix numerous bugs, improve structure 2023-03-21 05:27:31 +01:00
6691992a79 Bump version 2023-03-21 03:02:05 +01:00
1ea0f4c339 Fix usePrepareTemplate 2023-03-21 03:01:49 +01:00
8bfa117be2 Release candidate 2023-03-21 02:36:30 +01:00
b3acecdcea Use children prop for Template 2023-03-21 02:36:13 +01:00
ec479c7e91 Update initialize-email-theme script 2023-03-21 01:50:21 +01:00
fd7760d9ed Update eject-keycloak-page 2023-03-21 01:18:02 +01:00
c9fcec6889 Replace postinstall by prepare 2023-03-20 05:39:34 +01:00
fd901ef2cf Run build only on latest ubuntu and node 16 2023-03-20 05:39:12 +01:00
8afdaa8f0e Relase candidate 2023-03-20 05:29:14 +01:00
254bfccc62 Feature Account theme customization 2023-03-20 05:28:53 +01:00
5b4aeca63c Done implementing the templates and the two first pages 2023-03-20 05:14:25 +01:00
17871daf0c Build account theme 2023-03-20 01:48:03 +01:00
cdd4460968 Rename iitialize-email-theme 2023-03-20 01:30:42 +01:00
fa6a37880b Fix script for account and email 2023-03-20 01:30:16 +01:00
d4e1dabe12 Fix build error 2023-03-20 00:59:53 +01:00
a3fd376b24 Refactor of the i18n download mechanism 2023-03-20 00:58:35 +01:00
aaac1f54e8 Untrack generated messages 2023-03-20 00:03:15 +01:00
41c0329822 Move everything under the login dir 2023-03-19 23:12:45 +01:00
74d48fd7e1 relase candidate 2023-03-19 16:59:57 +01:00
9c3c953129 Fix download i18n script 2023-03-19 16:59:27 +01:00
f5cae18da7 Relase candidate 2023-03-19 16:58:42 +01:00
59d47592d9 Fix other scripts 2023-03-19 16:58:26 +01:00
2b6c991190 Fmt 2023-03-19 16:47:00 +01:00
26020ba8bb Relase candidate 2023-03-19 16:37:48 +01:00
b573bc20b5 Fix 2023-03-19 16:37:35 +01:00
210dbfa265 Bump version 2023-03-19 16:31:25 +01:00
b37cac93ff Fix 2023-03-19 16:31:13 +01:00
eea953efb6 Relase candidate 2023-03-19 16:28:51 +01:00
7ad9d7b291 Remove all unessesary relative import 2023-03-19 16:28:38 +01:00
20937c4f72 Relase candidate 2023-03-19 15:53:13 +01:00
dbbfa07639 Feature new script: Eject-keycloak-page 2023-03-19 15:52:41 +01:00
9e1a4cad5c Update homepage 2023-03-19 15:49:27 +01:00
02bbedcfca Remove no longer used tools 2023-03-19 14:56:30 +01:00
cd70d90914 Refactor completed 2023-03-19 14:48:01 +01:00
819f297de8 Better i18n API 2023-03-19 14:03:06 +01:00
0608adde89 Better naming convention for i18n API 2023-03-19 13:54:39 +01:00
ad7bcf4669 Fix lining script 2023-03-18 19:05:27 +01:00
2eccc86e83 Remove eventEmitter warning 2023-03-18 19:02:17 +01:00
16d18f23a1 Refactor of the main component and i18n 2023-03-18 18:54:33 +01:00
5631ae1b6c Better naming convention 2023-03-18 18:27:50 +01:00
5fb29992f6 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-18 16:58:21 +01:00
910d633ac2 Fix build test app 2023-03-18 16:57:58 +01:00
32f8380e56 Fix build:test 2023-03-18 16:20:21 +01:00
43e4dd6bb6 Fix build for real 2023-03-18 16:17:33 +01:00
4f0b1688db Thank you @justkey007 for tsc-alias 2023-03-18 15:49:45 +01:00
9e75ee09bb fix(deps): update garronej_modules_update 2023-03-18 09:11:03 +00:00
9ae8822e00 Fix build 2023-03-18 06:25:19 +01:00
babffd1fe6 Make the project compile 2023-03-18 06:14:05 +01:00
5615d62032 Scripts dir outside of the src dir 2023-03-18 02:12:12 +01:00
4b89d15c1e progressing 2023-03-17 20:40:29 +01:00
815f510d5f Exclude tsbuild info of the bin dir from the bundle 2023-03-16 23:03:18 +01:00
199ba193be Rename getKcContext dir to kcContext 2023-03-16 23:02:06 +01:00
4ae9bd3f9a Fix repo url in package.json 2023-03-16 22:57:24 +01:00
1c9cf639ea Remove console log 2023-03-16 22:44:44 +01:00
0040464ca1 Move lib up one level 2023-03-16 22:43:09 +01:00
79997efbb6 First commit towars supporting account theme 2023-03-16 22:13:46 +01:00
0e42009798 Fix bin test script 2023-03-16 14:39:40 +01:00
93fdcb8739 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-16 13:33:01 +01:00
aca926e202 Fix link script command 2023-03-16 13:31:56 +01:00
9941027b10 Update package.json 2023-03-15 12:45:58 +01:00
9104de4290 Merge pull request #263 from mkreuzmayr/main
Fix start container script paths for windows
2023-03-15 12:45:43 +01:00
5dc692809c Fix start container script paths for windows 2023-03-15 10:07:01 +01:00
8dc1d1bd21 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-13 10:47:10 +01:00
fe588485a9 Update changelog 2023-03-13 10:47:02 +01:00
19ef1d7025 Bump version 2023-03-13 10:44:46 +01:00
62523a8662 Merge pull request #260 from InseeFrLab/lordvlad/issue257
Run keycloakify behind corporate proxy
2023-03-13 10:44:08 +01:00
6e97665e2e Merge branch 'main' into lordvlad/issue257 2023-03-09 18:33:18 +01:00
4988680353 Update description 2023-03-09 18:30:58 +01:00
c5de5c20c7 Bump version 2023-03-08 23:21:44 +01:00
1a0fee1aa2 Make things cleaner 2023-03-08 23:21:32 +01:00
06a44603cd Release candidate 2023-03-08 22:32:45 +01:00
e48459762e Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-08 22:28:46 +01:00
235ebeae97 Bump version 2023-03-08 22:25:10 +01:00
dfe909606e Deprecate useFormValidationSlice 2023-03-08 22:24:51 +01:00
6fd0c7726c Merge branch 'lordvlad/issue257' of github.com:InseeFrLab/keycloakify into lordvlad/issue257
* 'lordvlad/issue257' of github.com:InseeFrLab/keycloakify:
  Only test build on LTS node version
  Update dependency evt to ^2.4.15
  Update README.md
  Bump version
  Avoid passing unessesary realm values in the error.ftl page
2023-03-08 10:25:10 +01:00
819e045811 feat(proxy): respect XDG_CACHE_HOME if set 2023-03-08 10:24:52 +01:00
1ba780598d Relase candidate 2023-03-07 18:18:37 +01:00
aeb0cb3110 Only test build on LTS node version 2023-03-07 18:18:04 +01:00
88923838c5 Bump version 2023-03-07 17:29:30 +01:00
df9f6fd7fd Merge branch 'main' into lordvlad/issue257 2023-03-07 17:16:24 +01:00
98e46d6ac9 Update dependency evt to ^2.4.15 2023-03-07 17:07:49 +01:00
daff614fb4 Update README.md 2023-03-07 17:07:48 +01:00
5ea324c7f2 Bump version 2023-03-07 17:07:48 +01:00
23fedbf94a Avoid passing unessesary realm values in the error.ftl page 2023-03-07 17:07:45 +01:00
593d66d8d6 style: remove unused dependency 2023-03-07 16:45:17 +01:00
851dcd5bf7 Run keycloakify behind corporate proxy
Fixes #257

Use make-fetch-happen for the download step. This lib will use `PROXY`
and `HTTPS_PROXY` and `NO_PROXY` env vars out of the box.

Additionally we'll try and get proxy config from npm. Unfortunately,
the most straightforward options is to call npm config to do this, since
npm  config is not easily extracted as a lib and we don't want to
replicate the resolution mechanisms.
2023-03-07 16:43:12 +01:00
2e919681ae Update dependency evt to ^2.4.15 2023-03-07 10:14:46 +00:00
5da68cd48c Update README.md 2023-03-05 23:37:37 +01:00
27fdaeff46 Bump version 2023-02-27 11:55:39 +01:00
53c0079656 Use extract instead of subtype to ease copy paste into theme repo 2023-02-27 11:55:25 +01:00
93780b77e0 Bump version 2023-02-27 11:32:14 +01:00
b712ed0421 Avoid using tsafe utils to avoid forcing user to install tsafe 2023-02-27 11:32:00 +01:00
ee96f1b345 Bump version 2023-02-27 11:29:23 +01:00
d13464df3d Get rid of the ReactComponent type, classes based component are no longer used 2023-02-27 11:29:05 +01:00
6bde2e4d96 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-27 10:39:44 +01:00
0a4953c020 Bump version 2023-02-27 10:39:37 +01:00
96c488880c Abstract away Template logic 2023-02-27 10:39:22 +01:00
7e0adf3f66 Update README.md 2023-02-26 17:32:35 +01:00
09f716440a Bump version 2023-02-26 16:41:47 +01:00
2251c84171 Use the new syntax for importing as type 2023-02-26 16:37:06 +01:00
5cfe78dcd1 Update prettier config 2023-02-26 15:39:03 +01:00
6a48325132 Be more relax on the type safety to avoir headache 2023-02-26 15:37:52 +01:00
294be0a79a see prev commit 2023-02-26 15:36:52 +01:00
c94b264b44 Don't need a dir for a single file 2023-02-26 15:36:35 +01:00
7220c4e3e3 Fix deepAssign 2023-02-26 15:35:57 +01:00
5aadeba2ec fix clsx 2023-02-26 15:35:30 +01:00
0f47a5b6ba Small Template refactor 2023-02-25 20:11:55 +01:00
36f32d28f2 Stop auto updating powerhooks 2023-02-25 19:21:55 +01:00
6d69ccf229 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-25 19:21:17 +01:00
37073b42be Avoir introducing breaking changes for CSS only setup 2023-02-25 19:19:46 +01:00
837501c948 Refactor 2023-02-25 18:26:39 +01:00
b300966fa8 Refactor and get rid of unessesary dependencies 2023-02-25 18:11:23 +01:00
730eb06c84 fix(deps): update dependency powerhooks to ^0.26.2 2023-02-14 12:08:41 +00:00
aca8d3f4b7 fix(deps): update dependency powerhooks to ^0.26.1 2023-02-08 16:38:28 +00:00
b5b3af4659 Bump version 2023-02-07 01:32:36 +01:00
6cd231426d Import Blob from node builtins 2023-02-07 01:32:20 +01:00
0c7cd1cd75 Bump version 2023-02-07 01:21:17 +01:00
2425704ead Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-07 01:20:33 +01:00
4e22159206 Bump version 2023-02-07 01:20:26 +01:00
52cf1ba02c Fix tsafe related warnings 2023-02-07 01:20:12 +01:00
516e84182f fix(deps): update dependency powerhooks to ^0.26.0 2023-02-05 15:10:42 +00:00
a3a9853e18 bump version 2023-02-05 14:58:53 +01:00
08e26600fd Use keycloakify as bundler by default 2023-02-05 14:58:38 +01:00
7793c2c6ba Update package.json 2023-02-05 14:41:32 +01:00
9e826d16dd Merge pull request #241 from lordvlad/mvn-begone
Mvn begone addendum
2023-02-05 14:41:13 +01:00
80618bbd9c Merge branch 'main' into mvn-begone 2023-02-05 13:36:52 +01:00
38ad47ea75 use hand-crafted promise, pipeline does not resolve properly 2023-02-05 13:32:24 +01:00
45ed359bef fix keycloak theme source path for internal bundler 2023-02-05 13:31:34 +01:00
fcc26c3e7a now that main is a promise, we shuold catch errors 2023-02-05 13:31:03 +01:00
d4ff6b1f40 fix: bundler fix missing directory 2023-02-05 12:59:05 +01:00
557de34eea fix: bundler fix missing change 2023-02-05 12:56:01 +01:00
e034dc4d90 Merge branch 'mvn-begone' of github.com:lordvlad/keycloakify into mvn-begone
* 'mvn-begone' of github.com:lordvlad/keycloakify:
  fix(deps): update garronej_modules_update
  Update README.md
  Rollback via update
  Bump version
  keycloak test script: use env to launch bash
  fix(deps): update dependency powerhooks to ^0.22.0
  Update dependency powerhooks to ^0.21.0
  Relase candidate
  fmt
  Update README.md
  Bump version
  Update src/bin/tools/downloadAndUnzip.ts
  Bump version
  #232
  Bump version
  keycloak test script: use env to launch bash
  fix(deps): update dependency powerhooks to ^0.22.0
  Update dependency powerhooks to ^0.21.0
2023-02-05 12:35:15 +01:00
cfbd1e5e4b fix(bundler): fix type mismatch introduced in last-minute 'fixes' 2023-02-05 12:34:48 +01:00
0df661819f Bump version 2023-02-04 20:51:06 +01:00
1a9f6d10d4 Actually run the top level await 2023-02-04 20:50:53 +01:00
a787215c95 Bump version 2023-02-04 20:39:38 +01:00
64ab400af5 Temporarly restore mvn as default bundler 2023-02-04 20:38:50 +01:00
a463878bf2 Bump version 2023-02-04 20:22:45 +01:00
9f72024c61 Merge pull request #240 from InseeFrLab/mvn-begone
Mvn begone
2023-02-04 19:47:36 +01:00
243fbd4dc9 Set the artifactId name in the build option 2023-02-04 19:36:42 +01:00
4e6a290693 Make new node based bundler the default 2023-02-04 18:02:39 +01:00
ac05d529ca Minor fixes 2023-02-04 17:44:02 +01:00
b38d79004a Merge branch 'main' into mvn-begone 2023-02-03 14:42:05 +01:00
f4a547df11 introduce options to choose a bundle strategy
Pick from 'none', 'keycloakify' or 'mvn', default to 'mvn'. 'none' will
not create a jar, 'keycloakify' will create a jar file using only tools
available to native nodejs, no additional  system library required.
Choosing 'mvn' will behave as before, starting maven in a subprocess.

The bundler can be chosen in `package.json` or via `KEYCLOAKIFY_BUNDLER`
env var.

This commit also adds `KEYCLOAKIFY_GROUP_ID` and
`KEYCLOAKIFY_ARTIFACT_ID` env vars, which will be used to
define group id and artifact id in pom.xml and pom.properties, if given.
2023-02-03 14:28:06 +01:00
2b87c35058 introduce utils for creating a jar archive 2023-02-03 14:06:24 +01:00
b11833e450 fix typo 2023-02-03 13:48:32 +01:00
fa8e119514 fix(deps): update garronej_modules_update 2023-02-01 15:16:03 +00:00
677cb5c330 Update README.md 2023-01-27 15:46:30 +01:00
6e74c79bfe Merge pull request #233 from InseeFrLab/windows_compat
Windows compat
2023-01-27 15:45:31 +01:00
54474f5908 Merge branch 'main' into windows_compat 2023-01-27 15:45:25 +01:00
99cc0f519b Rollback via update 2023-01-27 01:13:16 +01:00
92a01f89ef Bump version 2023-01-27 01:12:46 +01:00
fd83a0c743 keycloak test script: use env to launch bash
This minor change allows users to use the latest version of `bash` on Mac OS.

`/bin/bash` is very old on Mac OS, and many users will have `bash` from MacPorts or Homebrew. This change allows such users to be able to use the newer `bash` installation.
2023-01-27 01:12:46 +01:00
988e46c875 fix(deps): update dependency powerhooks to ^0.22.0 2023-01-27 01:12:46 +01:00
f081c2fc20 Update dependency powerhooks to ^0.21.0 2023-01-27 01:12:46 +01:00
b4b376a1a5 Relase candidate 2023-01-27 01:09:11 +01:00
0db4179d47 fmt 2023-01-27 00:32:41 +01:00
795b7c6234 Update README.md 2023-01-27 00:27:46 +01:00
091b9a57f5 Bump version 2023-01-27 00:22:28 +01:00
564e1422ac Merge pull request #226 from lordvlad/fix-win-build
windows compatibility
2023-01-27 00:20:12 +01:00
8ed4ed3fc4 Update src/bin/tools/downloadAndUnzip.ts 2023-01-26 22:29:51 +01:00
29fe4566a7 style: remove unused variable 2023-01-26 22:20:16 +01:00
ae3bfb28ed refactor: follow suggesion to make methods read a bit more sync 2023-01-26 22:00:31 +01:00
14aab97d8a Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-01-26 10:50:01 +01:00
52d7a47cd7 Bump version 2023-01-26 10:48:57 +01:00
f338dcbeed #232 2023-01-26 10:48:24 +01:00
dcec058a22 Bump version 2023-01-23 02:57:12 +01:00
2bdc6b156b Merge pull request #231 from rome-user/patch-1
keycloak test script: use env to launch bash
2023-01-23 02:56:32 +01:00
84ca9e6b81 keycloak test script: use env to launch bash
This minor change allows users to use the latest version of `bash` on Mac OS.

`/bin/bash` is very old on Mac OS, and many users will have `bash` from MacPorts or Homebrew. This change allows such users to be able to use the newer `bash` installation.
2023-01-22 17:28:27 -08:00
11cb0fd2db Merge pull request #230 from InseeFrLab/renovate_main-garronej_modules_update
Update dependency powerhooks to ^0.22.0 (main)
2023-01-18 00:58:38 +01:00
3f620ffb6f fix(deps): update dependency powerhooks to ^0.22.0 2023-01-16 18:23:50 +00:00
1a0e05d073 rewrite download and unzip to use nodejs native methods 2023-01-16 14:42:20 +01:00
a4d2de23a1 Update dependency powerhooks to ^0.21.0 2023-01-16 02:38:07 +00:00
85cecc9811 fix(build): _properly_ fix bin paths 2023-01-12 22:56:02 +01:00
9899f742a8 fix(build): also rewrite bin paths in dist/package.json 2023-01-12 22:51:48 +01:00
b5484740b7 fix(build): remove dependency on *nix chown 2023-01-12 22:27:22 +01:00
016b15b437 Bump version 2023-01-10 09:05:33 +01:00
6fb936798e https://github.com/InseeFrLab/keycloakify/issues/217#issuecomment-1376849781 2023-01-10 09:05:19 +01:00
a692b87843 Bump version 2023-01-08 15:01:12 +01:00
19663885a4 Merge pull request #222 from InseeFrLab/pr/lordvlad/221
Pr/lordvlad/221
2023-01-08 14:59:22 +01:00
49b87777f9 Fmt 2023-01-08 14:57:18 +01:00
d4523bb1e6 Merge branch 'main' into patch-1 2023-01-08 12:22:14 +01:00
e3200899e2 fix: replace rm system calls with nodejs native functions; closes #219 2023-01-08 12:19:41 +01:00
36c7a1ab9e Bump version 2023-01-08 00:14:01 +01:00
c54fbd5eca Merge pull request #218 from pedrodsRocha1/improve-secure-login-flow
fix - handle username and password login errors the same way
2023-01-08 00:03:16 +01:00
bbe828071e fix - handle username and password login errors the same 2023-01-06 15:41:01 +01:00
23f6c7db00 Update garronej_modules_update 2022-11-29 18:31:14 +00:00
b1ea9e7a71 Update dependency powerhooks to ^0.20.27 2022-11-16 05:21:45 +00:00
fb71d0e272 Update dependency powerhooks to ^0.20.26 2022-11-13 22:11:59 +00:00
fa72a29999 Bump version 2022-11-11 15:49:05 +01:00
af77b31d54 Update default test container to Keycloak 20.0.1 2022-11-11 15:48:32 +01:00
8280dace26 Update garronej_modules_update 2022-10-25 03:18:01 +00:00
ecaf1c7b7c Bump version 2022-10-16 00:51:11 +02:00
8702ec29a8 Drop dependency to @emotion/react 2022-10-16 00:49:49 +02:00
d8206434bc Bump version 2022-10-15 15:39:56 +02:00
c71c2a8710 Fix breaking change of 6.8 2022-10-15 15:39:32 +02:00
370 changed files with 6868 additions and 37509 deletions

View File

@ -28,8 +28,8 @@ jobs:
needs: test_lint
strategy:
matrix:
node: [ '14', '15' ,'16', '17' ]
os: [ windows-latest, ubuntu-latest ]
node: [ '16' ]
os: [ ubuntu-latest ]
name: Test with Node v${{ matrix.node }} on ${{ matrix.os }}
steps:
- name: Tell if project is using npm or yarn
@ -136,4 +136,4 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}

4
.gitignore vendored
View File

@ -49,4 +49,6 @@ jspm_packages
.idea
/keycloak_email
/build_keycloak
/build_keycloak
/src/login/i18n/baseMessages/
/src/account/i18n/baseMessages/

View File

@ -6,4 +6,7 @@ node_modules/
/src/test/apps/
/src/tools/types/
/sample_react_project
/build_keycloak/
/build_keycloak/
/.vscode/
/src/login/i18n/baseMessages/
/src/account/i18n/baseMessages/

View File

@ -8,9 +8,6 @@
<a href="https://github.com/garronej/keycloakify/actions">
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
</a>
<a href="https://bundlephobia.com/package/keycloakify">
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
</a>
<a href="https://www.npmjs.com/package/keycloakify">
<img src="https://img.shields.io/npm/dm/keycloakify">
</a>
@ -27,15 +24,9 @@
<a href="https://www.keycloakify.dev">Home</a>
-
<a href="https://docs.keycloakify.dev">Documentation</a>
</p>
<p align="center"> ---- Project starter / Demo setup ---- </p>
<p align="center">
<a href="https://github.com/garronej/keycloakify-starter">CSS Level customization</a>
-
<a href="https://github.com/garronej/keycloakify-advanced-starter">Component Level customization</a>
<a href="https://github.com/codegouvfr/keycloakify-starter">Starter project</a>
</p>
<p align="center"> ---- </p>
</p>
<p align="center">
@ -49,6 +40,39 @@
# Changelog highlights
## 7.0 🍾
- Account theme support 🚀
- It's much easier to customize pages at the CSS level, you can now see in the browser dev tool the customizable classes.
- New interactive CLI tool `npx eject-keycloak-page`, that enables to select the page you want to customize at the component level.
## 6.13
- Build work behind corporate proxies, [see issue](https://github.com/InseeFrLab/keycloakify/issues/257).
## 6.12
Massive improvement in the developer experience:
- There is now only one starter repo: https://github.com/codegouvfr/keycloakify-starter
- A lot of comments have been added in the code of the starter to make it easier to get started.
- The doc has been updated: https://docs.keycloakify.dev
- A lot of improvements in the type system.
## 6.11.4
- You no longer need to have Maven installed to build the theme. Thanks to @lordvlad, [see PR](https://github.com/InseeFrLab/keycloakify/pull/239).
- Feature new build options: [`bundler`](https://docs.keycloakify.dev/build-options#keycloakify.bundler), [`groupId`](https://docs.keycloakify.dev/build-options#keycloakify.groupid), [`artifactId`](https://docs.keycloakify.dev/build-options#keycloakify.artifactid), [`version`](https://docs.keycloakify.dev/build-options#version).
Theses options can be user to customize the output name of the .jar. You can use environnement variables to overrides the values read in the package.json. Thanks to @lordvlad.
## 6.10.0
- Widows compat (thanks to @lordvlad, [see PR](https://github.com/InseeFrLab/keycloakify/pull/226)). WSL is no longer required 🎉
## 6.8.4
- `@emotion/react` is no longer a peer dependency of Keycloakify.
## 6.8.0
- It is now possible to pass a custom `<Template />` component as a prop to `<KcApp />` and every

59
package.json Executable file → Normal file
View File

@ -1,30 +1,35 @@
{
"name": "keycloakify",
"version": "6.8.2",
"description": "Keycloak theme generator for Reacts app",
"version": "7.0.1",
"description": "Create Keycloak themes using React",
"repository": {
"type": "git",
"url": "git://github.com/garronej/keycloakify.git"
"url": "git://github.com/inseefrlab/keycloakify.git"
},
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src/lib && yarn grant-exec-perms && yarn copy-files dist/",
"build:test": "rimraf dist_test/ && tsc -p src/test && yarn copy-files dist_test/",
"prepare": "yarn generate-i18n-messages",
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src/tsconfig.json && tsc-alias -p src/tsconfig.json && yarn grant-exec-perms && yarn copy-files dist/",
"build:watch": "tsc -p src/tsconfig.json && (concurrently \"tsc -p src/tsconfig.json -w\" \"tsc-alias -p src/tsconfig.json\")",
"build:test": "rimraf dist_test/ && tsc -p test/tsconfig.json && tsc-alias -p test/tsconfig.json && yarn copy-files dist_test/src",
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
"copy-files": "copyfiles -u 1 src/**/*.ftl",
"pretest": "yarn build:test",
"test": "node dist_test/test/bin && node dist_test/test/lib",
"generate-messages": "node dist/bin/generate-i18n-messages.js",
"link_in_test_app": "node dist/bin/link_in_test_app.js",
"test": "yarn build:test && node dist_test/test/bin && node dist_test/test/lib",
"test:sample-app": "yarn build:test && node dist_test/test/bin/main.js",
"_format": "prettier '**/*.{ts,tsx,json,md}'",
"format": "yarn _format --write",
"format:check": "yarn _format --list-different"
"format:check": "yarn _format --list-different",
"generate-i18n-messages": "ts-node --skipProject scripts/generate-i18n-messages.ts",
"link-in-app": "ts-node --skipProject scripts/link-in-app.ts",
"link-in-starter": "yarn link-in-app keycloakify-starter",
"tsc-watch": "tsc -p src/bin -w & tsc -p src/lib -w "
},
"bin": {
"keycloakify": "dist/bin/keycloakify/index.js",
"create-keycloak-email-directory": "dist/bin/create-keycloak-email-directory.js",
"download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js"
"initialize-email-theme": "dist/bin/initialize-email-theme.js",
"download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js",
"eject-keycloak-page": "dist/bin/eject-keycloak-page.js"
},
"lint-staged": {
"*.{ts,tsx,json,md}": [
@ -41,7 +46,8 @@
"files": [
"src/",
"dist/",
"!dist/tsconfig.tsbuildinfo"
"!dist/tsconfig.tsbuildinfo",
"!dist/bin/tsconfig.tsbuildinfo"
],
"keywords": [
"bluehats",
@ -53,18 +59,17 @@
"login",
"register"
],
"homepage": "https://github.com/garronej/keycloakify",
"homepage": "https://www.keycloakify.dev",
"peerDependencies": {
"@emotion/react": "^11.4.1",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@emotion/react": "^11.4.1",
"@types/memoizee": "^0.4.7",
"@types/make-fetch-happen": "^10.0.1",
"@types/minimist": "^1.2.2",
"@types/node": "^17.0.25",
"@types/node": "^18.15.3",
"@types/react": "18.0.9",
"concurrently": "^7.6.0",
"copyfiles": "^2.4.1",
"husky": "^4.3.8",
"lint-staged": "^11.0.0",
@ -72,23 +77,23 @@
"properties-parser": "^0.3.1",
"react": "18.1.0",
"rimraf": "^3.0.2",
"typescript": "^4.2.3"
"scripting-tools": "^0.19.13",
"ts-node": "^10.9.1",
"tsc-alias": "^1.8.3",
"typescript": "^5.0.1-rc"
},
"dependencies": {
"@octokit/rest": "^18.12.0",
"cheerio": "^1.0.0-rc.5",
"cli-select": "^1.1.2",
"evt": "^2.4.6",
"memoizee": "^0.4.15",
"evt": "^2.4.18",
"make-fetch-happen": "^11.0.3",
"minimal-polyfills": "^2.2.2",
"minimist": "^1.2.6",
"path-browserify": "^1.0.1",
"powerhooks": "^0.20.23",
"react-markdown": "^5.0.3",
"rfc4648": "^1.5.2",
"scripting-tools": "^0.19.13",
"tsafe": "^1.1.3",
"tss-react": "^4.3.4",
"tsafe": "^1.6.0",
"zod": "^3.17.10"
}
}

View File

@ -13,11 +13,11 @@
"packageRules": [
{
"packagePatterns": ["*"],
"excludePackagePatterns": ["tss-react", "powerhooks", "tsafe", "evt"],
"excludePackagePatterns": ["tsafe", "evt"],
"enabled": false
},
{
"packagePatterns": ["tss-react", "powerhooks", "tsafe", "evt"],
"packagePatterns": ["tsafe", "evt"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "branch",

View File

@ -1,12 +1,11 @@
import "minimal-polyfills/Object.fromEntries";
import * as fs from "fs";
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
import { crawl } from "./tools/crawl";
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { getProjectRoot } from "./tools/getProjectRoot";
import { rm_rf, rm_r } from "./tools/rm";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
import { crawl } from "../src/bin/tools/crawl";
import { downloadBuiltinKeycloakTheme } from "../src/bin/download-builtin-keycloak-theme";
import { getProjectRoot } from "../src/bin/tools/getProjectRoot";
import { getCliOptions } from "../src/bin/tools/cliOptions";
import { getLogger } from "../src/bin/tools/logger";
//NOTE: To run without argument when we want to generate src/i18n/generated_kcMessages files,
// update the version array for generating for newer version.
@ -17,14 +16,14 @@ const propertiesParser = require("properties-parser");
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
logger.log(JSON.stringify({ keycloakVersion }));
(async () => {
const keycloakVersion = "21.0.1";
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
rm_rf(tmpDirPath);
fs.rmSync(tmpDirPath, { "recursive": true, "force": true });
downloadBuiltinKeycloakTheme({
await downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
isSilent
@ -54,13 +53,27 @@ for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
});
}
rm_r(tmpDirPath);
fs.rmSync(tmpDirPath, { recursive: true, force: true });
Object.keys(record).forEach(pageType => {
const recordForPageType = record[pageType];
Object.keys(record).forEach(themeType => {
const recordForPageType = record[themeType];
Object.keys(recordForPageType).forEach(language => {
const filePath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages", keycloakVersion, pageType, `${language}.ts`);
if (themeType !== "login" && themeType !== "account") {
return;
}
const baseMessagesDirPath = pathJoin(getProjectRoot(), "src", themeType, "i18n", "baseMessages");
const languages = Object.keys(recordForPageType);
const generatedFileHeader = [
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
"//PLEASE DO NOT EDIT MANUALLY",
""
].join("\n");
languages.forEach(language => {
const filePath = pathJoin(baseMessagesDirPath, `${language}.ts`);
fs.mkdirSync(pathDirname(filePath), { "recursive": true });
@ -68,9 +81,7 @@ for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
filePath,
Buffer.from(
[
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
"//PLEASE DO NOT EDIT MANUALLY",
"",
generatedFileHeader,
"/* spell-checker: disable */",
`const messages= ${JSON.stringify(recordForPageType[language], null, 2)};`,
"",
@ -83,5 +94,24 @@ for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
logger.log(`${filePath} wrote`);
});
fs.writeFileSync(
pathJoin(baseMessagesDirPath, "index.ts"),
Buffer.from(
[
generatedFileHeader,
"export async function getMessages(currentLanguageTag: string) {",
" const { default: messages } = await (() => {",
" switch (currentLanguageTag) {",
...languages.map(language => ` case "${language}": return import("./${language}");`),
' default: return { "default": {} };',
" }",
" })();",
" return messages;",
"}"
].join("\n"),
"utf8"
)
);
});
}
})();

143
scripts/link-in-app.ts Normal file
View File

@ -0,0 +1,143 @@
import { execSync } from "child_process";
import { join as pathJoin, relative as pathRelative } from "path";
import { getProjectRoot } from "../src/bin/tools/getProjectRoot";
import * as fs from "fs";
const singletonDependencies: string[] = ["react", "@types/react"];
const rootDirPath = getProjectRoot();
//NOTE: This is only required because of: https://github.com/garronej/ts-ci/blob/c0e207b9677523d4ec97fe672ddd72ccbb3c1cc4/README.md?plain=1#L54-L58
fs.writeFileSync(
pathJoin(rootDirPath, "dist", "package.json"),
Buffer.from(
JSON.stringify(
(() => {
const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8"));
return {
...packageJsonParsed,
"main": packageJsonParsed["main"]?.replace(/^dist\//, ""),
"types": packageJsonParsed["types"]?.replace(/^dist\//, ""),
"module": packageJsonParsed["module"]?.replace(/^dist\//, ""),
"exports": !("exports" in packageJsonParsed)
? undefined
: Object.fromEntries(
Object.entries(packageJsonParsed["exports"]).map(([key, value]) => [
key,
(value as string).replace(/^\.\/dist\//, "./")
])
)
};
})(),
null,
2
),
"utf8"
)
);
fs.cpSync(pathJoin(rootDirPath, "src"), pathJoin(rootDirPath, "dist", "src"), { "recursive": true });
const commonThirdPartyDeps = (() => {
// For example [ "@emotion" ] it's more convenient than
// having to list every sub emotion packages (@emotion/css @emotion/utils ...)
// in singletonDependencies
const namespaceSingletonDependencies: string[] = [];
return [
...namespaceSingletonDependencies
.map(namespaceModuleName =>
fs
.readdirSync(pathJoin(rootDirPath, "node_modules", namespaceModuleName))
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
)
.reduce((prev, curr) => [...prev, ...curr], []),
...singletonDependencies
];
})();
const yarnGlobalDirPath = pathJoin(rootDirPath, ".yarn_home");
fs.rmSync(yarnGlobalDirPath, { "recursive": true, "force": true });
fs.mkdirSync(yarnGlobalDirPath);
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
const { targetModuleName, cwd } = params;
const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : ["--no-bin-links"])].join(" ");
console.log(`$ cd ${pathRelative(rootDirPath, cwd) || "."} && ${cmd}`);
execSync(cmd, {
cwd,
"env": {
...process.env,
"HOME": yarnGlobalDirPath
}
});
};
const testAppPaths = (() => {
const [, , ...testAppNames] = process.argv;
return testAppNames
.map(testAppName => {
const testAppPath = pathJoin(rootDirPath, "..", testAppName);
if (fs.existsSync(testAppPath)) {
return testAppPath;
}
console.warn(`Skipping ${testAppName} since it cant be found here: ${testAppPath}`);
return undefined;
})
.filter((path): path is string => path !== undefined);
})();
if (testAppPaths.length === 0) {
console.error("No test app to link into!");
process.exit(-1);
}
testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath }));
console.log("=== Linking common dependencies ===");
const total = commonThirdPartyDeps.length;
let current = 0;
commonThirdPartyDeps.forEach(commonThirdPartyDep => {
current++;
console.log(`${current}/${total} ${commonThirdPartyDep}`);
const localInstallPath = pathJoin(
...[rootDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])]
);
execYarnLink({ "cwd": localInstallPath });
});
commonThirdPartyDeps.forEach(commonThirdPartyDep =>
testAppPaths.forEach(testAppPath =>
execYarnLink({
"cwd": testAppPath,
"targetModuleName": commonThirdPartyDep
})
)
);
console.log("=== Linking in house dependencies ===");
execYarnLink({ "cwd": pathJoin(rootDirPath, "dist") });
testAppPaths.forEach(testAppPath =>
execYarnLink({
"cwd": testAppPath,
"targetModuleName": JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8"))["name"]
})
);
export {};

26
src/account/Fallback.tsx Normal file
View File

@ -0,0 +1,26 @@
import { lazy, Suspense } from "react";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { I18n } from "keycloakify/account/i18n";
import type { KcContext } from "./kcContext";
import { assert, type Equals } from "tsafe/assert";
const Password = lazy(() => import("keycloakify/account/pages/Password"));
const Account = lazy(() => import("keycloakify/account/pages/Account"));
export default function Fallback(props: PageProps<KcContext, I18n>) {
const { kcContext, ...rest } = props;
return (
<Suspense>
{(() => {
switch (kcContext.pageId) {
case "password.ftl":
return <Password kcContext={kcContext} {...rest} />;
case "account.ftl":
return <Account kcContext={kcContext} {...rest} />;
}
assert<Equals<typeof kcContext, never>>(false);
})()}
</Suspense>
);
}

131
src/account/Template.tsx Normal file
View File

@ -0,0 +1,131 @@
import { clsx } from "keycloakify/tools/clsx";
import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate";
import { type TemplateProps } from "keycloakify/account/TemplateProps";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import type { KcContext } from "./kcContext";
import type { I18n } from "./i18n";
import { assert } from "keycloakify/tools/assert";
export default function Template(props: TemplateProps<KcContext, I18n>) {
const { kcContext, i18n, doUseDefaultCss, active, classes, children } = props;
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
const { locale, url, features, realm, message, referrer } = kcContext;
const { isReady } = usePrepareTemplate({
"doFetchDefaultThemeResources": doUseDefaultCss,
url,
"stylesCommon": ["node_modules/patternfly/dist/css/patternfly.min.css", "node_modules/patternfly/dist/css/patternfly-additions.min.css"],
"styles": ["css/account.css"],
"htmlClassName": undefined,
"bodyClassName": clsx("admin-console", "user", getClassName("kcBodyClass"))
});
if (!isReady) {
return null;
}
return (
<>
<header className="navbar navbar-default navbar-pf navbar-main header">
<nav className="navbar" role="navigation">
<div className="navbar-header">
<div className="container">
<h1 className="navbar-title">Keycloak</h1>
</div>
</div>
<div className="navbar-collapse navbar-collapse-1">
<div className="container">
<ul className="nav navbar-nav navbar-utility">
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
<li>
<div className="kc-dropdown" id="kc-locale-dropdown">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" id="kc-current-locale-link">
{labelBySupportedLanguageTag[currentLanguageTag]}
</a>
<ul>
{locale.supported.map(({ languageTag }) => (
<li key={languageTag} className="kc-dropdown-item">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" onClick={() => changeLocale(languageTag)}>
{labelBySupportedLanguageTag[languageTag]}
</a>
</li>
))}
</ul>
</div>
</li>
)}
{referrer?.url !== undefined && (
<li>
<a href={referrer.url} id="referrer">
{msg("backTo", referrer.name)}
</a>
</li>
)}
<li>
<a href={url.getLogoutUrl()}>{msg("doSignOut")}</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div className="container">
<div className="bs-sidebar col-sm-3">
<ul>
<li className={clsx(active === "account" && "active")}>
<a href={url.accountUrl}>{msg("account")}</a>
</li>
{features.passwordUpdateSupported && (
<li className={clsx(active === "password" && "active")}>
<a href={url.passwordUrl}>{msg("password")}</a>
</li>
)}
<li className={clsx(active === "totp" && "active")}>
<a href={url.totpUrl}>{msg("authenticator")}</a>
</li>
{features.identityFederation && (
<li className={clsx(active === "social" && "active")}>
<a href={url.socialUrl}>{msg("federatedIdentity")}</a>
</li>
)}
<li className={clsx(active === "sessions" && "active")}>
<a href={url.sessionsUrl}>{msg("sessions")}</a>
</li>
<li className={clsx(active === "applications" && "active")}>
<a href={url.applicationsUrl}>{msg("applications")}</a>
</li>
{features.log && (
<li className={clsx(active === "log" && "active")}>
<a href={url.logUrl}>{msg("log")}</a>
</li>
)}
{realm.userManagedAccessAllowed && features.authorization && (
<li className={clsx(active === "authorization" && "active")}>
<a href={url.resourceUrl}>{msg("myResources")}</a>
</li>
)}
</ul>
</div>
<div className="col-sm-9 content-area">
{message !== undefined && (
<div className={clsx("alert", `alert-${message.type}`)}>
{message.type === "success" && <span className="pficon pficon-ok"></span>}
{message.type === "error" && <span className="pficon pficon-error-circle-o"></span>}
<span className="kc-feedback-text">{message.summary}</span>
</div>
)}
{children}
</div>
</div>
</>
);
}

View File

@ -0,0 +1,14 @@
import type { ReactNode } from "react";
import type { KcContext } from "./kcContext";
import type { I18n } from "./i18n";
export type TemplateProps<KcContext extends KcContext.Common, I18nExtended extends I18n> = {
kcContext: KcContext;
i18n: I18nExtended;
doUseDefaultCss: boolean;
active: string;
classes?: Partial<Record<ClassKey, string>>;
children: ReactNode;
};
export type ClassKey = "kcBodyClass" | "kcButtonClass" | "kcButtonPrimaryClass" | "kcButtonLargeClass" | "kcButtonDefaultClass";

View File

@ -1,10 +1,11 @@
import "minimal-polyfills/Object.fromEntries";
//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
import React, { useEffect, useState, useRef } from "react";
import type baseMessages from "./generated_messages/18.0.1/login/en";
import { useEffect, useState, useRef } from "react";
import fallbackMessages from "./baseMessages/en";
import { getMessages } from "./baseMessages";
import { assert } from "tsafe/assert";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { Markdown } from "../tools/Markdown";
import type { KcContext } from "../kcContext/KcContext";
import { Markdown } from "keycloakify/tools/Markdown";
export const fallbackLanguageTag = "en";
@ -15,11 +16,11 @@ export type KcContextLike = {
};
};
assert<KcContextBase extends KcContextLike ? true : false>();
assert<KcContext extends KcContextLike ? true : false>();
export type MessageKeyBase = keyof typeof baseMessages | keyof typeof keycloakifyExtraMessages[typeof fallbackLanguageTag];
export type MessageKey = keyof typeof fallbackMessages | keyof (typeof keycloakifyExtraMessages)[typeof fallbackLanguageTag];
export type I18n<MessageKey extends string = MessageKeyBase> = {
export type GenericI18n<MessageKey extends string> = {
/**
* e.g: "en", "fr", "zh-CN"
*
@ -67,134 +68,72 @@ export type I18n<MessageKey extends string = MessageKeyBase> = {
advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
};
export function __unsafe_useI18n<ExtraMessageKey extends string = never>(params: {
kcContext: KcContextLike;
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
doSkip: boolean;
}): I18n<MessageKeyBase | ExtraMessageKey> | null {
const { kcContext, extraMessages, doSkip } = params;
export type I18n = GenericI18n<MessageKey>;
const [i18n, setI18n] = useState<I18n<ExtraMessageKey | MessageKeyBase> | undefined>(undefined);
export function createUseI18n<ExtraMessageKey extends string = never>(extraMessages: {
[languageTag: string]: { [key in ExtraMessageKey]: string };
}) {
function useI18n(params: { kcContext: KcContextLike }): GenericI18n<MessageKey | ExtraMessageKey> | null {
const { kcContext } = params;
const refHasStartedFetching = useRef(false);
const [i18n, setI18n] = useState<GenericI18n<ExtraMessageKey | MessageKey> | undefined>(undefined);
useEffect(() => {
if (doSkip || refHasStartedFetching.current) {
return;
}
const refHasStartedFetching = useRef(false);
refHasStartedFetching.current = true;
useEffect(() => {
if (refHasStartedFetching.current) {
return;
}
(async () => {
const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {};
refHasStartedFetching.current = true;
const [fallbackMessages, messages] = await Promise.all([
import("./generated_messages/18.0.1/login/en"),
(() => {
switch (currentLanguageTag) {
case "ca":
return import("./generated_messages/18.0.1/login/ca");
case "cs":
return import("./generated_messages/18.0.1/login/cs");
case "da":
return import("./generated_messages/18.0.1/login/da");
case "de":
return import("./generated_messages/18.0.1/login/de");
case "en":
return import("./generated_messages/18.0.1/login/en");
case "es":
return import("./generated_messages/18.0.1/login/es");
case "fi":
return import("./generated_messages/18.0.1/login/fi");
case "fr":
return import("./generated_messages/18.0.1/login/fr");
case "hu":
return import("./generated_messages/18.0.1/login/hu");
case "it":
return import("./generated_messages/18.0.1/login/it");
case "ja":
return import("./generated_messages/18.0.1/login/ja");
case "lt":
return import("./generated_messages/18.0.1/login/lt");
case "lv":
return import("./generated_messages/18.0.1/login/lv");
case "nl":
return import("./generated_messages/18.0.1/login/nl");
case "no":
return import("./generated_messages/18.0.1/login/no");
case "pl":
return import("./generated_messages/18.0.1/login/pl");
case "pt-BR":
return import("./generated_messages/18.0.1/login/pt-BR");
case "ru":
return import("./generated_messages/18.0.1/login/ru");
case "sk":
return import("./generated_messages/18.0.1/login/sk");
case "sv":
return import("./generated_messages/18.0.1/login/sv");
case "tr":
return import("./generated_messages/18.0.1/login/tr");
case "zh-CN":
return import("./generated_messages/18.0.1/login/zh-CN");
default:
return { "default": {} };
}
})()
]).then(modules => modules.map(module => module.default));
(async () => {
const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {};
setI18n({
...createI18nTranslationFunctions({
"fallbackMessages": {
...fallbackMessages,
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
...(extraMessages[fallbackLanguageTag] ?? {})
} as any,
"messages": {
...messages,
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
...(extraMessages[currentLanguageTag] ?? {})
} as any
}),
currentLanguageTag,
"changeLocale": newLanguageTag => {
const { locale } = kcContext;
setI18n({
...createI18nTranslationFunctions({
"fallbackMessages": {
...fallbackMessages,
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
...(extraMessages[fallbackLanguageTag] ?? {})
} as any,
"messages": {
...(await getMessages(currentLanguageTag)),
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
...(extraMessages[currentLanguageTag] ?? {})
} as any
}),
currentLanguageTag,
"changeLocale": newLanguageTag => {
const { locale } = kcContext;
assert(locale !== undefined, "Internationalization not enabled");
assert(locale !== undefined, "Internationalization not enabled");
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
window.location.href = targetSupportedLocale.url;
window.location.href = targetSupportedLocale.url;
assert(false, "never");
},
"labelBySupportedLanguageTag": Object.fromEntries(
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
)
});
})();
}, []);
assert(false, "never");
},
"labelBySupportedLanguageTag": Object.fromEntries(
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
)
});
})();
}, []);
return i18n ?? null;
}
return i18n ?? null;
}
const useI18n_private = __unsafe_useI18n;
export function useI18n<ExtraMessageKey extends string = never>(params: {
kcContext: KcContextLike;
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
}): I18n<MessageKeyBase | ExtraMessageKey> | null {
return useI18n_private({
...params,
"doSkip": false
});
return { useI18n };
}
function createI18nTranslationFunctions<MessageKey extends string>(params: {
fallbackMessages: Record<MessageKey, string>;
messages: Record<MessageKey, string>;
}): Pick<I18n<MessageKey>, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> {
}): Pick<GenericI18n<MessageKey>, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> {
const { fallbackMessages, messages } = params;
function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderMarkdown: boolean }): string | JSX.Element | undefined {

View File

@ -0,0 +1 @@
export type { I18n } from "./i18n";

8
src/account/index.ts Normal file
View File

@ -0,0 +1,8 @@
import Fallback from "keycloakify/account/Fallback";
export default Fallback;
export { getKcContext } from "keycloakify/account/kcContext/getKcContext";
export { createUseI18n } from "keycloakify/account/i18n/i18n";
export type { PageProps } from "keycloakify/account/pages/PageProps";

View File

@ -0,0 +1,84 @@
import type { AccountThemePageId } from "keycloakify/bin/keycloakify/generateFtl";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
export type KcContext = KcContext.Password | KcContext.Account;
export declare namespace KcContext {
export type Common = {
locale?: {
supported: {
url: string;
label: string;
languageTag: string;
}[];
currentLanguageTag: string;
};
url: {
accountUrl: string;
passwordUrl: string;
totpUrl: string;
socialUrl: string;
sessionsUrl: string;
applicationsUrl: string;
logUrl: string;
resourceUrl: string;
resourcesCommonPath: string;
resourcesPath: string;
getLogoutUrl: () => string;
};
features: {
passwordUpdateSupported: boolean;
identityFederation: boolean;
log: boolean;
authorization: boolean;
};
realm: {
internationalizationEnabled: boolean;
userManagedAccessAllowed: boolean;
};
message?: {
type: "success" | "warning" | "error" | "info";
summary: string;
};
referrer?: {
url?: string;
name: string;
};
messagesPerField: {
printIfExists: <T>(fieldName: string, x: T) => T | undefined;
existsError: (fieldName: string) => boolean;
get: (fieldName: string) => string;
exists: (fieldName: string) => boolean;
};
account: {
email?: string;
firstName: string;
lastName?: string;
username?: string;
};
};
export type Password = Common & {
pageId: "password.ftl";
password: {
passwordSet: boolean;
};
stateChecker: string;
};
export type Account = Common & {
pageId: "account.ftl";
url: {
referrerURI: string;
accountUrl: string;
};
realm: {
registrationEmailAsUsername: boolean;
editUsernameAllowed: boolean;
};
stateChecker: string;
};
}
assert<Equals<KcContext["pageId"], AccountThemePageId>>();

View File

@ -0,0 +1,76 @@
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
import { deepAssign } from "keycloakify/tools/deepAssign";
import type { ExtendKcContext } from "./getKcContextFromWindow";
import { getKcContextFromWindow } from "./getKcContextFromWindow";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import { pathBasename } from "keycloakify/tools/pathBasename";
import { mockTestingResourcesCommonPath } from "keycloakify/bin/mockTestingResourcesPath";
import { symToStr } from "tsafe/symToStr";
import { kcContextMocks, kcContextCommonMock } from "keycloakify/account/kcContext/kcContextMocks";
export function getKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
mockPageId?: ExtendKcContext<KcContextExtension>["pageId"];
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
}): { kcContext: ExtendKcContext<KcContextExtension> | undefined } {
const { mockPageId, mockData } = params ?? {};
const realKcContext = getKcContextFromWindow<KcContextExtension>();
if (mockPageId !== undefined && realKcContext === undefined) {
//TODO maybe trow if no mock fo custom page
console.log(
[
`%cKeycloakify: ${symToStr({ mockPageId })} set to ${mockPageId}.`,
`If assets are missing make sure you have built your Keycloak theme at least once.`
].join(" "),
"background: red; color: yellow; font-size: medium"
);
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
const partialKcContextCustomMock = mockData?.find(({ pageId }) => pageId === mockPageId);
if (kcContextDefaultMock === undefined && partialKcContextCustomMock === undefined) {
console.warn(
[
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
`provide mock data needed to debug the page outside of Keycloak as you are trying to do now.`,
`Please check the documentation of the getKcContext function`
].join("\n")
);
}
const kcContext: any = {};
deepAssign({
"target": kcContext,
"source": kcContextDefaultMock !== undefined ? kcContextDefaultMock : { "pageId": mockPageId, ...kcContextCommonMock }
});
if (partialKcContextCustomMock !== undefined) {
deepAssign({
"target": kcContext,
"source": partialKcContextCustomMock
});
}
return { kcContext };
}
if (realKcContext === undefined) {
return { "kcContext": undefined };
}
if (!("account" in realKcContext)) {
return { "kcContext": undefined };
}
{
const { url } = realKcContext;
url.resourcesCommonPath = pathJoin(url.resourcesPath, pathBasename(mockTestingResourcesCommonPath));
}
return { "kcContext": realKcContext };
}

View File

@ -0,0 +1,11 @@
import type { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminatingKey";
import { ftlValuesGlobalName } from "keycloakify/bin/keycloakify/ftlValuesGlobalName";
import type { KcContext } from "./KcContext";
export type ExtendKcContext<KcContextExtension extends { pageId: string }> = [KcContextExtension] extends [never]
? KcContext
: AndByDiscriminatingKey<"pageId", KcContextExtension & KcContext.Common, KcContext>;
export function getKcContextFromWindow<KcContextExtension extends { pageId: string } = never>(): ExtendKcContext<KcContextExtension> | undefined {
return typeof window === "undefined" ? undefined : (window as any)[ftlValuesGlobalName];
}

View File

@ -0,0 +1 @@
export type { KcContext } from "./KcContext";

View File

@ -0,0 +1,175 @@
import "minimal-polyfills/Object.fromEntries";
import { mockTestingResourcesCommonPath, mockTestingResourcesPath } from "keycloakify/bin/mockTestingResourcesPath";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import { id } from "tsafe/id";
import type { KcContext } from "./KcContext";
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
export const kcContextCommonMock: KcContext.Common = {
"url": {
"resourcesPath": pathJoin(PUBLIC_URL, mockTestingResourcesPath),
"resourcesCommonPath": pathJoin(PUBLIC_URL, mockTestingResourcesCommonPath),
"resourceUrl": "#",
"accountUrl": "#",
"applicationsUrl": "#",
"getLogoutUrl": () => "#",
"logUrl": "#",
"passwordUrl": "#",
"sessionsUrl": "#",
"socialUrl": "#",
"totpUrl": "#"
},
"realm": {
"internationalizationEnabled": true,
"userManagedAccessAllowed": true
},
"messagesPerField": {
"printIfExists": () => {
return undefined;
},
"existsError": () => false,
"get": key => `Fake error for ${key}`,
"exists": () => false
},
"locale": {
"supported": [
/* spell-checker: disable */
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
"label": "Deutsch",
"languageTag": "de"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
"label": "Norsk",
"languageTag": "no"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
"label": "Русский",
"languageTag": "ru"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
"label": "Svenska",
"languageTag": "sv"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
"label": "Português (Brasil)",
"languageTag": "pt-BR"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
"label": "Lietuvių",
"languageTag": "lt"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
"label": "English",
"languageTag": "en"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
"label": "Italiano",
"languageTag": "it"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
"label": "Français",
"languageTag": "fr"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
"label": "中文简体",
"languageTag": "zh-CN"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
"label": "Español",
"languageTag": "es"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
"label": "Čeština",
"languageTag": "cs"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
"label": "日本語",
"languageTag": "ja"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
"label": "Slovenčina",
"languageTag": "sk"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
"label": "Polski",
"languageTag": "pl"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
"label": "Català",
"languageTag": "ca"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
"label": "Nederlands",
"languageTag": "nl"
},
{
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
"label": "Türkçe",
"languageTag": "tr"
}
/* spell-checker: enable */
],
"currentLanguageTag": "en"
},
"message": {
"type": "success",
"summary": "This is a test message"
},
"features": {
"authorization": true,
"identityFederation": true,
"log": true,
"passwordUpdateSupported": true
},
"referrer": undefined,
"account": {
"firstName": "john",
"lastName": "doe",
"email": "john.doe@code.gouv.fr",
"username": "doe_j"
}
};
export const kcContextMocks: KcContext[] = [
id<KcContext.Password>({
...kcContextCommonMock,
"pageId": "password.ftl",
"password": {
"passwordSet": true
},
"stateChecker": "state checker"
}),
id<KcContext.Account>({
...kcContextCommonMock,
"pageId": "account.ftl",
"url": {
...kcContextCommonMock.url,
"referrerURI": "#",
"accountUrl": "#"
},
"realm": {
...kcContextCommonMock.realm,
"registrationEmailAsUsername": true,
"editUsernameAllowed": true
},
"stateChecker": ""
})
];

View File

@ -0,0 +1,12 @@
import { createUseClassName } from "keycloakify/lib/useGetClassName";
import type { ClassKey } from "keycloakify/account/TemplateProps";
export const { useGetClassName } = createUseClassName<ClassKey>({
"defaultClasses": {
"kcBodyClass": undefined,
"kcButtonClass": "btn",
"kcButtonPrimaryClass": "btn-primary",
"kcButtonLargeClass": "btn-lg",
"kcButtonDefaultClass": "btn-default"
}
});

View File

@ -0,0 +1,134 @@
import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
export default function LogoutConfirm(props: PageProps<Extract<KcContext, { pageId: "account.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
doUseDefaultCss,
"classes": {
...classes,
"kcBodyClass": clsx(classes?.kcBodyClass, "user")
}
});
const { url, realm, messagesPerField, stateChecker, account } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="account">
<div className="row">
<div className="col-md-10">
<h2>{msg("editAccountHtmlTitle")}</h2>
</div>
<div className="col-md-2 subtitle">
<span className="subtitle">
<span className="required">*</span> {msg("requiredFields")}
</span>
</div>
</div>
<form action={url.accountUrl} className="form-horizontal" method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
{!realm.registrationEmailAsUsername && (
<div className={clsx("form-group", messagesPerField.printIfExists("username", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="username" className="control-label">
{msg("username")}
</label>
{realm.editUsernameAllowed && <span className="required">*</span>}
</div>
<div className="col-sm-10 col-md-10">
<input
type="text"
className="form-control"
id="username"
name="username"
disabled={!realm.editUsernameAllowed}
value={account.username ?? ""}
/>
</div>
</div>
)}
<div className={clsx("form-group", messagesPerField.printIfExists("email", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="email" className="control-label">
{msg("email")}
</label>{" "}
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input type="text" className="form-control" id="email" name="email" autoFocus value={account.email ?? ""} />
</div>
</div>
<div className={clsx("form-group", messagesPerField.printIfExists("firstName", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="firstName" className="control-label">
{msg("firstName")}
</label>{" "}
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input type="text" className="form-control" id="firstName" name="firstName" value={account.firstName ?? ""} />
</div>
</div>
<div className={clsx("form-group", messagesPerField.printIfExists("lastName", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="lastName" className="control-label">
{msg("lastName")}
</label>{" "}
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input type="text" className="form-control" id="lastName" name="lastName" value={account.lastName ?? ""} />
</div>
</div>
<div className="form-group">
<div id="kc-form-buttons" className="col-md-offset-2 col-md-10 submit">
<div>
{url.referrerURI !== undefined && <a href={url.referrerURI}>${msg("backToApplication")}</a>}
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
name="submitAction"
value="Save"
>
{msg("doSave")}
</button>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
name="submitAction"
value="Cancel"
>
{msg("doCancel")}
</button>
I
</div>
</div>
</div>
</form>
</Template>
);
}

View File

@ -0,0 +1,11 @@
import type { LazyExoticComponent } from "react";
import type { I18n } from "keycloakify/account/i18n";
import type { TemplateProps, ClassKey } from "keycloakify/account/TemplateProps";
export type PageProps<KcContext, I18nExtended extends I18n> = {
Template: LazyExoticComponent<(props: TemplateProps<any, any>) => JSX.Element | null>;
kcContext: KcContext;
i18n: I18nExtended;
doUseDefaultCss: boolean;
classes?: Partial<Record<ClassKey, string>>;
};

View File

@ -0,0 +1,105 @@
import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
export default function LogoutConfirm(props: PageProps<Extract<KcContext, { pageId: "password.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
doUseDefaultCss,
"classes": {
...classes,
"kcBodyClass": clsx(classes?.kcBodyClass, "password")
}
});
const { url, password, account, stateChecker } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="password">
<div className="row">
<div className="col-md-10">
<h2>{msg("changePasswordHtmlTitle")}</h2>
</div>
<div className="col-md-2 subtitle">
<span className="subtitle">${msg("allFieldsRequired")}</span>
</div>
</div>
<form action={url.passwordUrl} className="form-horizontal" method="post">
<input
type="text"
id="username"
name="username"
value={account.username ?? ""}
autoComplete="username"
readOnly
style={{ "display": "none;" }}
/>
{password.passwordSet && (
<div className="form-group">
<div className="col-sm-2 col-md-2">
<label htmlFor="password" className="control-label">
{msg("password")}
</label>
</div>
<div className="col-sm-10 col-md-10">
<input type="password" className="form-control" id="password" name="password" autoFocus autoComplete="current-password" />
</div>
</div>
)}
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<div className="form-group">
<div className="col-sm-2 col-md-2">
<label htmlFor="password-new" className="control-label">
{msg("passwordNew")}
</label>
</div>
<div className="col-sm-10 col-md-10">
<input type="password" className="form-control" id="password-new" name="password-new" autoComplete="new-password" />
</div>
</div>
<div className="form-group">
<div className="col-sm-2 col-md-2">
<label htmlFor="password-confirm" className="control-label two-lines">
{msg("passwordConfirm")}
</label>
</div>
<div className="col-sm-10 col-md-10">
<input type="password" className="form-control" id="password-confirm" name="password-confirm" autoComplete="new-password" />
</div>
</div>
<div className="form-group">
<div id="kc-form-buttons" className="col-md-offset-2 col-md-10 submit">
<div>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
name="submitAction"
value="Save"
>
{msg("doSave")}
</button>
</div>
</div>
</div>
</form>
</Template>
);
}

View File

@ -1,41 +0,0 @@
#!/usr/bin/env node
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { keycloakThemeEmailDirPath } from "./keycloakify";
import { join as pathJoin, basename as pathBasename } from "path";
import { transformCodebase } from "./tools/transformCodebase";
import { promptKeycloakVersion } from "./promptKeycloakVersion";
import * as fs from "fs";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
if (require.main === module) {
(async () => {
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
if (fs.existsSync(keycloakThemeEmailDirPath)) {
logger.warn(`There is already a ./${pathBasename(keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
process.exit(-1);
}
const { keycloakVersion } = await promptKeycloakVersion();
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeEmailDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": builtinKeycloakThemeTmpDirPath,
isSilent
});
transformCodebase({
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
"destDirPath": keycloakThemeEmailDirPath
});
logger.log(`./${pathBasename(keycloakThemeEmailDirPath)} ready to be customized`);
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
})();
}

View File

@ -7,11 +7,11 @@ import { promptKeycloakVersion } from "./promptKeycloakVersion";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
export function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; isSilent: boolean }) {
export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; isSilent: boolean }) {
const { keycloakVersion, destDirPath, isSilent } = params;
for (const ext of ["", "-community"]) {
downloadAndUnzip({
await downloadAndUnzip({
"destDirPath": destDirPath,
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`,
@ -31,7 +31,7 @@ if (require.main === module) {
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
downloadBuiltinKeycloakTheme({
await downloadBuiltinKeycloakTheme({
keycloakVersion,
destDirPath,
isSilent

View File

@ -0,0 +1,64 @@
#!/usr/bin/env node
import { getProjectRoot } from "./tools/getProjectRoot";
import cliSelect from "cli-select";
import {
loginThemePageIds,
accountThemePageIds,
type LoginThemePageId,
type AccountThemePageId,
themeTypes,
type ThemeType
} from "./keycloakify/generateFtl/generateFtl";
import { capitalize } from "tsafe/capitalize";
import { readFile, writeFile } from "fs/promises";
import { existsSync } from "fs";
import { join as pathJoin, relative as pathRelative } from "path";
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
import { assert, Equals } from "tsafe/assert";
(async () => {
const projectRootDir = getProjectRoot();
console.log("Select a theme type");
const { value: themeType } = await cliSelect<ThemeType>({
"values": [...themeTypes]
}).catch(() => {
console.log("Aborting");
process.exit(-1);
});
console.log("Select a page you would like to eject");
const { value: pageId } = await cliSelect<LoginThemePageId | AccountThemePageId>({
"values": (() => {
switch (themeType) {
case "login":
return [...loginThemePageIds];
case "account":
return [...accountThemePageIds];
}
assert<Equals<typeof themeType, never>>(false);
})()
}).catch(() => {
console.log("Aborting");
process.exit(-1);
});
const pageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
const targetFilePath = pathJoin(process.cwd(), "src", "keycloak-theme", themeType, "pages", pageBasename);
if (existsSync(targetFilePath)) {
console.log(`${pageId} is already ejected, ${pathRelative(process.cwd(), targetFilePath)} already exists`);
process.exit(-1);
}
writeFile(targetFilePath, await readFile(pathJoin(projectRootDir, "src", themeType, "pages", pageBasename)));
console.log(`${pathRelative(process.cwd(), targetFilePath)} created`);
})();

View File

@ -0,0 +1,48 @@
#!/usr/bin/env node
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { keycloakThemeEmailDirPath } from "./keycloakify";
import { join as pathJoin, relative as pathRelative } from "path";
import { transformCodebase } from "./tools/transformCodebase";
import { promptKeycloakVersion } from "./promptKeycloakVersion";
import * as fs from "fs";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
(async () => {
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
if (fs.existsSync(keycloakThemeEmailDirPath)) {
logger.warn(`There is already a ${pathRelative(process.cwd(), keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
process.exit(-1);
}
const { keycloakVersion } = await promptKeycloakVersion();
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeEmailDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
await downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": builtinKeycloakThemeTmpDirPath,
isSilent
});
transformCodebase({
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
"destDirPath": keycloakThemeEmailDirPath
});
{
const themePropertyFilePath = pathJoin(keycloakThemeEmailDirPath, "theme.properties");
fs.writeFileSync(themePropertyFilePath, Buffer.from(`parent=base\n${fs.readFileSync(themePropertyFilePath).toString("utf8")}`, "utf8"));
}
logger.log(
`${pathRelative(process.cwd(), keycloakThemeEmailDirPath)} ready to be customized, feel free to remove every file you do not customize`
);
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
})();

View File

@ -3,15 +3,25 @@ import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
import { id } from "tsafe/id";
import { parse as urlParse } from "url";
import { typeGuard } from "tsafe/typeGuard";
import { symToStr } from "tsafe/symToStr";
const bundlers = ["mvn", "keycloakify", "none"] as const;
type Bundler = (typeof bundlers)[number];
type ParsedPackageJson = {
name: string;
version: string;
homepage?: string;
keycloakify?: {
/** @deprecated: use extraLoginPages instead */
extraPages?: string[];
extraLoginPages?: string[];
extraAccountPages?: string[];
extraThemeProperties?: string[];
areAppAndKeycloakServerSharingSameDomain?: boolean;
artifactId?: string;
groupId?: string;
bundler?: Bundler;
};
};
@ -22,13 +32,18 @@ const zParsedPackageJson = z.object({
"keycloakify": z
.object({
"extraPages": z.array(z.string()).optional(),
"extraLoginPages": z.array(z.string()).optional(),
"extraAccountPages": z.array(z.string()).optional(),
"extraThemeProperties": z.array(z.string()).optional(),
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional()
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional(),
"artifactId": z.string().optional(),
"groupId": z.string().optional(),
"bundler": z.enum(bundlers).optional()
})
.optional()
});
assert<Equals<ReturnType<typeof zParsedPackageJson["parse"]>, ParsedPackageJson>>();
assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>();
/** Consolidated build option gathered form CLI arguments and config in package.json */
export type BuildOptions = BuildOptions.Standalone | BuildOptions.ExternalAssets;
@ -38,10 +53,12 @@ export namespace BuildOptions {
isSilent: boolean;
version: string;
themeName: string;
extraPages?: string[];
extraLoginPages: string[] | undefined;
extraAccountPages: string[] | undefined;
extraThemeProperties?: string[];
//NOTE: Only for the pom.xml file, questionable utility...
groupId: string;
artifactId: string;
bundler: Bundler;
};
export type Standalone = Common & {
@ -108,7 +125,7 @@ export function readBuildOptions(params: {
const common: BuildOptions.Common = (() => {
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
const { extraPages, extraThemeProperties } = keycloakify ?? {};
const { extraPages, extraLoginPages, extraAccountPages, extraThemeProperties, groupId, artifactId, bundler } = keycloakify ?? {};
const themeName = name
.replace(/^@(.*)/, "$1")
@ -117,10 +134,26 @@ export function readBuildOptions(params: {
return {
themeName,
"bundler": (() => {
const { KEYCLOAKIFY_BUNDLER } = process.env;
assert(
typeGuard<Bundler | undefined>(
KEYCLOAKIFY_BUNDLER,
[undefined, ...id<readonly string[]>(bundlers)].includes(KEYCLOAKIFY_BUNDLER)
),
`${symToStr({ KEYCLOAKIFY_BUNDLER })} should be one of ${bundlers.join(", ")}`
);
return KEYCLOAKIFY_BUNDLER ?? bundler ?? "keycloakify";
})(),
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? artifactId ?? `${themeName}-keycloak-theme`,
"groupId": (() => {
const fallbackGroupId = `${themeName}.keycloak`;
return (
process.env.KEYCLOAKIFY_GROUP_ID ??
groupId ??
(!homepage
? fallbackGroupId
: urlParse(homepage)
@ -130,8 +163,9 @@ export function readBuildOptions(params: {
.join(".") ?? fallbackGroupId) + ".keycloak"
);
})(),
"version": version,
extraPages,
"version": process.env.KEYCLOAKIFY_VERSION ?? version,
"extraLoginPages": [...(extraPages ?? []), ...(extraLoginPages ?? [])],
extraAccountPages,
extraThemeProperties,
isSilent
};

View File

@ -2,8 +2,7 @@
<#assign pageId="PAGE_ID_xIgLsPgGId9D8e">
(()=>{
const out =
${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
const out = ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
out["msg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
out["advancedMsg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
@ -36,7 +35,11 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
return <#if messagesPerField.existsError('username', 'password')>x<#else>undefined</#if>;
<#else>
return <#if messagesPerField.existsError('${fieldName}')>x<#else>undefined</#if>;
</#if>
<#recover>
</#attempt>
}
@ -51,7 +54,11 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
return <#if messagesPerField.existsError('username', 'password')>true<#else>false</#if>;
<#else>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
</#if>
<#recover>
</#attempt>
}
@ -66,8 +73,14 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
<#if messagesPerField.existsError('username', 'password')>
return 'Invalid username or password.';
</#if>
<#else>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
</#if>
</#if>
<#recover>
</#attempt>
@ -83,7 +96,11 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
return <#if messagesPerField.exists('username') || messagesPerField.exists('password')>true<#else>false</#if>;
<#else>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
</#if>
<#recover>
</#attempt>
}
@ -93,6 +110,15 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
}
};
<#if account??>
out["url"]["getLogoutUrl"] = function () {
<#attempt>
return "${url.getLogoutUrl()}";
<#recover>
</#attempt>
};
</#if>
out["pageId"] = "${pageId}";
return out;
@ -156,6 +182,10 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
) || (
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) &&
are_same_path(path, ["realm"])
) || (
"error.ftl" == pageId &&
are_same_path(path, ["realm"]) &&
!["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
)
>
<#local out_seq += ["/*If you need '" + key + "' on " + pageId + ", please submit an issue to the Keycloakify repo*/"]>

View File

@ -10,8 +10,11 @@ import type { BuildOptions } from "../BuildOptions";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
export const pageIds = [
export const themeTypes = ["login", "account"] as const;
export type ThemeType = (typeof themeTypes)[number];
export const loginThemePageIds = [
"login.ftl",
"login-username.ftl",
"login-password.ftl",
@ -35,6 +38,11 @@ export const pageIds = [
"idp-review-user-profile.ftl"
] as const;
export const accountThemePageIds = ["password.ftl", "account.ftl"] as const;
export type LoginThemePageId = (typeof loginThemePageIds)[number];
export type AccountThemePageId = (typeof accountThemePageIds)[number];
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
@ -68,8 +76,6 @@ export namespace BuildOptionsLike {
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export type PageId = typeof pageIds[number];
export function generateFtlFilesCodeFactory(params: {
indexHtmlCode: string;
//NOTE: Expected to be an empty object if external assets mode is enabled.

View File

@ -1,5 +1,6 @@
import * as fs from "fs";
import { join as pathJoin, dirname as pathDirname } from "path";
import { themeTypes } from "./generateFtl/generateFtl";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import type { BuildOptions } from "./BuildOptions";
@ -7,6 +8,8 @@ import type { BuildOptions } from "./BuildOptions";
export type BuildOptionsLike = {
themeName: string;
groupId: string;
artifactId?: string;
version: string;
};
{
@ -16,7 +19,6 @@ export type BuildOptionsLike = {
}
export function generateJavaStackFiles(params: {
version: string;
keycloakThemeBuildingDirPath: string;
doBundlesEmailTemplate: boolean;
buildOptions: BuildOptionsLike;
@ -24,8 +26,7 @@ export function generateJavaStackFiles(params: {
jarFilePath: string;
} {
const {
version,
buildOptions: { groupId, themeName },
buildOptions: { groupId, themeName, version, artifactId },
keycloakThemeBuildingDirPath,
doBundlesEmailTemplate
} = params;
@ -34,8 +35,6 @@ export function generateJavaStackFiles(params: {
const { pomFileCode } = (function generatePomFileCode(): {
pomFileCode: string;
} {
const artefactId = `${themeName}-keycloak-theme`;
const pomFileCode = [
`<?xml version="1.0"?>`,
`<project xmlns="http://maven.apache.org/POM/4.0.0"`,
@ -43,9 +42,9 @@ export function generateJavaStackFiles(params: {
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
` <modelVersion>4.0.0</modelVersion>`,
` <groupId>${groupId}</groupId>`,
` <artifactId>${artefactId}</artifactId>`,
` <artifactId>${artifactId}</artifactId>`,
` <version>${version}</version>`,
` <name>${artefactId}</name>`,
` <name>${artifactId}</name>`,
` <description />`,
`</project>`
].join("\n");
@ -71,7 +70,7 @@ export function generateJavaStackFiles(params: {
"themes": [
{
"name": themeName,
"types": ["login", ...(doBundlesEmailTemplate ? ["email"] : [])]
"types": [...themeTypes, ...(doBundlesEmailTemplate ? ["email"] : [])]
}
]
},
@ -84,6 +83,6 @@ export function generateJavaStackFiles(params: {
}
return {
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${themeName}-${version}.jar`)
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${artifactId}-${version}.jar`)
};
}

View File

@ -3,9 +3,8 @@ import * as fs from "fs";
import { join as pathJoin, basename as pathBasename } from "path";
import { replaceImportsFromStaticInJsCode } from "./replacers/replaceImportsFromStaticInJsCode";
import { replaceImportsInCssCode } from "./replacers/replaceImportsInCssCode";
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
import { generateFtlFilesCodeFactory, loginThemePageIds, accountThemePageIds, themeTypes, type ThemeType } from "./generateFtl";
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
import * as child_process from "child_process";
import { mockTestingResourcesCommonPath, mockTestingResourcesPath, mockTestingSubDirOfPublicDirBasename } from "../mockTestingResourcesPath";
import { isInside } from "../tools/isInside";
import type { BuildOptions } from "./BuildOptions";
@ -18,7 +17,8 @@ export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.Ex
export namespace BuildOptionsLike {
export type Common = {
themeName: string;
extraPages?: string[];
extraLoginPages?: string[];
extraAccountPages?: string[];
extraThemeProperties?: string[];
isSilent: boolean;
};
@ -53,68 +53,177 @@ export namespace BuildOptionsLike {
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function generateKeycloakThemeResources(params: {
export async function generateKeycloakThemeResources(params: {
reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string;
keycloakThemeEmailDirPath: string;
keycloakVersion: string;
buildOptions: BuildOptionsLike;
}): { doBundlesEmailTemplate: boolean } {
}): Promise<{ doBundlesEmailTemplate: boolean }> {
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
const logger = getLogger({ isSilent: buildOptions.isSilent });
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, "login");
const getThemeDirPath = (themeType: ThemeType | "email") =>
pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
let allCssGlobalsToDefine: Record<string, string> = {};
transformCodebase({
"destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
"srcDirPath": reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if (
buildOptions.isStandalone &&
isInside({
"dirPath": pathJoin(reactAppBuildDirPath, mockTestingSubDirOfPublicDirBasename),
filePath
})
) {
return undefined;
let generateFtlFilesCode_glob: ReturnType<typeof generateFtlFilesCodeFactory>["generateFtlFilesCode"] | undefined = undefined;
for (const themeType of themeTypes) {
const themeDirPath = getThemeDirPath(themeType);
copy_app_resources_to_theme_path: {
const isFirstPass = themeType.indexOf(themeType) === 0;
if (!isFirstPass && !buildOptions.isStandalone) {
break copy_app_resources_to_theme_path;
}
if (/\.css?$/i.test(filePath)) {
if (!buildOptions.isStandalone) {
return undefined;
transformCodebase({
"destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
"srcDirPath": reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if (
buildOptions.isStandalone &&
isInside({
"dirPath": pathJoin(reactAppBuildDirPath, mockTestingSubDirOfPublicDirBasename),
filePath
})
) {
return undefined;
}
if (/\.css?$/i.test(filePath)) {
if (!buildOptions.isStandalone) {
return undefined;
}
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
"cssCode": sourceCode.toString("utf8")
});
register_css_variables: {
if (!isFirstPass) {
break register_css_variables;
}
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...cssGlobalsToDefine
};
}
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
}
if (/\.js?$/i.test(filePath)) {
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
return undefined;
}
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
buildOptions
});
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
}
return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
}
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
"cssCode": sourceCode.toString("utf8")
});
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...cssGlobalsToDefine
};
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
}
if (/\.js?$/i.test(filePath)) {
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
return undefined;
}
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
buildOptions
});
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
}
return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
});
}
});
const generateFtlFilesCode = (() => {
if (generateFtlFilesCode_glob !== undefined) {
return generateFtlFilesCode_glob;
}
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
"cssGlobalsToDefine": allCssGlobalsToDefine,
"buildOptions": buildOptions
});
return generateFtlFilesCode;
})();
[
...(() => {
switch (themeType) {
case "login":
return loginThemePageIds;
case "account":
return accountThemePageIds;
}
})(),
...((() => {
switch (themeType) {
case "login":
return buildOptions.extraLoginPages;
case "account":
return buildOptions.extraAccountPages;
}
})() ?? [])
].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
fs.mkdirSync(themeDirPath, { "recursive": true });
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
});
{
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
await downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
isSilent: buildOptions.isSilent
});
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
"destDirPath": themeResourcesDirPath
});
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
"destDirPath": pathJoin(themeResourcesDirPath, pathBasename(mockTestingResourcesCommonPath))
});
transformCodebase({
"srcDirPath": themeResourcesDirPath,
"destDirPath": pathJoin(reactAppPublicDirPath, mockTestingResourcesPath)
});
const keycloakResourcesWithinPublicDirPath = pathJoin(reactAppPublicDirPath, mockTestingSubDirOfPublicDirBasename);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
Buffer.from(
["This is just a test folder that helps develop", "the login and register page without having to run a Keycloak container"].join(
" "
)
)
);
fs.writeFileSync(pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"), Buffer.from("*", "utf8"));
fs.rmSync(tmpDirPath, { recursive: true, force: true });
}
fs.writeFileSync(
pathJoin(themeDirPath, "theme.properties"),
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
);
}
let doBundlesEmailTemplate: boolean;
@ -134,70 +243,9 @@ export function generateKeycloakThemeResources(params: {
transformCodebase({
"srcDirPath": keycloakThemeEmailDirPath,
"destDirPath": pathJoin(themeDirPath, "..", "email")
"destDirPath": getThemeDirPath("email")
});
}
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
"cssGlobalsToDefine": allCssGlobalsToDefine,
"buildOptions": buildOptions
});
[...pageIds, ...(buildOptions.extraPages ?? [])].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
fs.mkdirSync(themeDirPath, { "recursive": true });
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
});
{
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
isSilent: buildOptions.isSilent
});
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
"destDirPath": themeResourcesDirPath
});
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
"destDirPath": pathJoin(themeResourcesDirPath, pathBasename(mockTestingResourcesCommonPath))
});
transformCodebase({
"srcDirPath": themeResourcesDirPath,
"destDirPath": pathJoin(reactAppPublicDirPath, mockTestingResourcesPath)
});
const keycloakResourcesWithinPublicDirPath = pathJoin(reactAppPublicDirPath, mockTestingSubDirOfPublicDirBasename);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
Buffer.from(
["This is just a test folder that helps develop", "the login and register page without having to run a Keycloak container"].join(" ")
)
);
fs.writeFileSync(pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"), Buffer.from("*", "utf8"));
child_process.execSync(`rm -r ${tmpDirPath}`);
}
fs.writeFileSync(
pathJoin(themeDirPath, "theme.properties"),
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
);
return { doBundlesEmailTemplate };
}

View File

@ -30,15 +30,18 @@ export function generateStartKeycloakTestingContainer(params: {
buildOptions: { themeName }
} = params;
const keycloakThemePath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName).replace(/\\/g, "/");
fs.writeFileSync(
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
Buffer.from(
[
"#!/bin/bash",
"#!/usr/bin/env bash",
"",
`docker rm ${containerName} || true`,
"",
`cd ${keycloakThemeBuildingDirPath}`,
`cd "${keycloakThemeBuildingDirPath.replace(/\\/g, "/")}"`,
"",
"docker run \\",
" -p 8080:8080 \\",
@ -46,14 +49,7 @@ export function generateStartKeycloakTestingContainer(params: {
" -e KEYCLOAK_ADMIN=admin \\",
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
` -v ${pathJoin(
keycloakThemeBuildingDirPath,
"src",
"main",
"resources",
"theme",
themeName
)}:/opt/keycloak/themes/${themeName}:rw \\`,
` -v "${keycloakThemePath}":"/opt/keycloak/themes/${themeName}":rw \\`,
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
` start-dev`,
""

View File

@ -4,5 +4,5 @@ export * from "./keycloakify";
import { main } from "./keycloakify";
if (require.main === module) {
main();
main().catch(e => console.error(e));
}

View File

@ -1,19 +1,22 @@
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
import { generateJavaStackFiles } from "./generateJavaStackFiles";
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
import { join as pathJoin, relative as pathRelative, basename as pathBasename, sep as pathSep } from "path";
import * as child_process from "child_process";
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
import * as fs from "fs";
import { readBuildOptions } from "./BuildOptions";
import { getLogger } from "../tools/logger";
import { getCliOptions } from "../tools/cliOptions";
import jar from "../tools/jar";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
const reactProjectDirPath = process.cwd();
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
export const keycloakThemeEmailDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "keycloak_email");
export const keycloakThemeEmailDirPath = pathJoin(reactProjectDirPath, "src", "keycloak-theme", "email");
export function main() {
export async function main() {
const { isSilent, hasExternalAssets } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
logger.log("🔏 Building the keycloak theme...⌚");
@ -33,30 +36,47 @@ export function main() {
"isSilent": isSilent
});
const { doBundlesEmailTemplate } = generateKeycloakThemeResources({
const { doBundlesEmailTemplate } = await generateKeycloakThemeResources({
keycloakThemeBuildingDirPath,
keycloakThemeEmailDirPath,
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
buildOptions,
//We have to leave it at that otherwise we break our default theme.
//Problem is that we can't guarantee that the the old resources
//Problem is that we can`t guarantee that the the old resources
//will still be available on the newer keycloak version.
"keycloakVersion": "11.0.3"
});
const { jarFilePath } = generateJavaStackFiles({
"version": buildOptions.version,
keycloakThemeBuildingDirPath,
doBundlesEmailTemplate,
buildOptions
});
child_process.execSync("mvn package", {
"cwd": keycloakThemeBuildingDirPath
});
switch (buildOptions.bundler) {
case "none":
logger.log("😱 Skipping bundling step, there will be no jar");
break;
case "keycloakify":
logger.log("🫶 Let keycloakify do its thang");
await jar({
"rootPath": pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources"),
"version": buildOptions.version,
"groupId": buildOptions.groupId,
"artifactId": buildOptions.artifactId,
"targetPath": jarFilePath
});
break;
case "mvn":
logger.log("🫙 Run maven to deliver a jar");
child_process.execSync("mvn package", { "cwd": keycloakThemeBuildingDirPath });
break;
default:
assert<Equals<typeof buildOptions.bundler, never>>(false);
}
//We want, however, to test in a container running the latest Keycloak version
const containerKeycloakVersion = "19.0.1";
// We want, however, to test in a container running the latest Keycloak version
const containerKeycloakVersion = "20.0.1";
generateStartKeycloakTestingContainer({
keycloakThemeBuildingDirPath,
@ -101,19 +121,31 @@ export function main() {
"",
`To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`,
"",
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename))} 👈`,
`👉 $ .${pathSep}${pathRelative(
reactProjectDirPath,
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename)
)} 👈`,
"",
"Test with different Keycloak versions by editing the .sh file. see available versions here: https://quay.io/repository/keycloak/keycloak?tab=tags",
"",
"Once your container is up and running: ",
`Test with different Keycloak versions by editing the .sh file. see available versions here: https://quay.io/repository/keycloak/keycloak?tab=tags`,
``,
`Once your container is up and running: `,
"- Log into the admin console 👉 http://localhost:8080/admin username: admin, password: admin 👈",
'- Create a realm named "myrealm"',
'- Create a client with ID: "myclient", "Root URL": "https://www.keycloak.org/app/" and "Valid redirect URIs": "https://www.keycloak.org/app/*"',
`- Select Login Theme: ${buildOptions.themeName} (don't forget to save at the bottom of the page)`,
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
"",
"Video demoing this process: https://youtu.be/N3wlBoH4hKg",
""
`- Create a realm: myrealm`,
`- Enable registration: Realm settings -> Login tab -> User registration: on`,
`- Enable the Account theme: Realm settings -> Themes tab -> Account theme, select ${buildOptions.themeName} `,
`- Create a client id myclient`,
` Root URL: https://www.keycloak.org/app/`,
` Valid redirect URIs: https://www.keycloak.org/app* http://localhost* (localhost is optional)`,
` Valid post logout redirect URIs: https://www.keycloak.org/app* http://localhost*`,
` Web origins: *`,
` Login Theme: ${buildOptions.themeName}`,
` Save (button at the bottom of the page)`,
``,
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
`- Got to 👉 http://localhost:8080/realms/myrealm/account 👈 to see your account theme`,
``,
`Video tutorial: https://youtu.be/WMyGZNHQkjU`,
``
].join("\n")
);
}

View File

@ -1,126 +0,0 @@
import { execSync } from "child_process";
import { join as pathJoin, relative as pathRelative } from "path";
import { exclude } from "tsafe/exclude";
import * as fs from "fs";
const keycloakifyDirPath = pathJoin(__dirname, "..", "..");
fs.writeFileSync(
pathJoin(keycloakifyDirPath, "dist", "package.json"),
Buffer.from(
JSON.stringify(
(() => {
const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(keycloakifyDirPath, "package.json")).toString("utf8"));
return {
...packageJsonParsed,
"main": packageJsonParsed["main"].replace(/^dist\//, ""),
"types": packageJsonParsed["types"].replace(/^dist\//, "")
};
})(),
null,
2
),
"utf8"
)
);
const commonThirdPartyDeps = (() => {
const namespaceModuleNames = ["@emotion"];
const standaloneModuleNames = ["react", "@types/react", "powerhooks", "tss-react", "evt"];
return [
...namespaceModuleNames
.map(namespaceModuleName =>
fs
.readdirSync(pathJoin(keycloakifyDirPath, "node_modules", namespaceModuleName))
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
)
.reduce((prev, curr) => [...prev, ...curr], []),
...standaloneModuleNames
];
})();
const yarnHomeDirPath = pathJoin(keycloakifyDirPath, ".yarn_home");
execSync(["rm -rf", "mkdir"].map(cmd => `${cmd} ${yarnHomeDirPath}`).join(" && "));
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
const { targetModuleName, cwd } = params;
const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : [])].join(" ");
console.log(`$ cd ${pathRelative(keycloakifyDirPath, cwd) || "."} && ${cmd}`);
execSync(cmd, {
cwd,
"env": {
...process.env,
"HOME": yarnHomeDirPath
}
});
};
const testAppPaths = (() => {
const arg = process.argv[2];
const testAppNames = arg !== undefined ? [arg] : ["keycloakify-starter", "keycloakify-advanced-starter"];
return testAppNames
.map(testAppName => {
const testAppPath = pathJoin(keycloakifyDirPath, "..", testAppName);
if (fs.existsSync(testAppPath)) {
return testAppPath;
}
console.warn(`Skipping ${testAppName} since it cant be found here: ${testAppPath}`);
return undefined;
})
.filter(exclude(undefined));
})();
if (testAppPaths.length === 0) {
console.error("No test app to link into!");
process.exit(-1);
}
testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath }));
console.log("=== Linking common dependencies ===");
const total = commonThirdPartyDeps.length;
let current = 0;
commonThirdPartyDeps.forEach(commonThirdPartyDep => {
current++;
console.log(`${current}/${total} ${commonThirdPartyDep}`);
const localInstallPath = pathJoin(
...[keycloakifyDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])]
);
execYarnLink({ "cwd": localInstallPath });
});
commonThirdPartyDeps.forEach(commonThirdPartyDep =>
testAppPaths.forEach(testAppPath =>
execYarnLink({
"cwd": testAppPath,
"targetModuleName": commonThirdPartyDep
})
)
);
console.log("=== Linking in house dependencies ===");
execYarnLink({ "cwd": pathJoin(keycloakifyDirPath, "dist") });
testAppPaths.forEach(testAppPath =>
execYarnLink({
"cwd": testAppPath,
"targetModuleName": "keycloakify"
})
);

55
src/bin/tools/crc32.ts Normal file
View File

@ -0,0 +1,55 @@
import { Readable } from "stream";
const crc32tab = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
];
/**
*
* @param input either a byte stream, a string or a buffer, you want to have the checksum for
* @returns a promise for a checksum (uint32)
*/
export function crc32(input: Readable | String | Buffer): Promise<number> {
if (typeof input === "string") {
let crc = ~0;
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input.charCodeAt(i)) & 0xff];
return Promise.resolve((crc ^ -1) >>> 0);
} else if (input instanceof Buffer) {
let crc = ~0;
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input[i]) & 0xff];
return Promise.resolve((crc ^ -1) >>> 0);
} else if (input instanceof Readable) {
return new Promise<number>((resolve, reject) => {
let crc = ~0;
input.setMaxListeners(Infinity);
input.on("end", () => resolve((crc ^ -1) >>> 0));
input.on("error", e => reject(e));
input.on("data", (chunk: Buffer) => {
for (let i = 0; i < chunk.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ chunk[i]) & 0xff];
});
});
} else {
throw new Error("Unsupported input " + typeof input);
}
}

61
src/bin/tools/deflate.ts Normal file
View File

@ -0,0 +1,61 @@
import { PassThrough, Readable, TransformCallback, Writable } from "stream";
import { pipeline } from "stream/promises";
import { deflateRaw as deflateRawCb, createDeflateRaw } from "zlib";
import { promisify } from "util";
import { crc32 } from "./crc32";
import tee from "./tee";
const deflateRaw = promisify(deflateRawCb);
/**
* A stream transformer that records the number of bytes
* passed in its `size` property.
*/
class ByteCounter extends PassThrough {
size: number = 0;
_transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback) {
if ("length" in chunk) this.size += chunk.length;
super._transform(chunk, encoding, callback);
}
}
/**
* @param data buffer containing the data to be compressed
* @returns a buffer containing the compressed/deflated data and the crc32 checksum
* of the source data
*/
export async function deflateBuffer(data: Buffer) {
const [deflated, checksum] = await Promise.all([deflateRaw(data), crc32(data)]);
return { deflated, crc32: checksum };
}
/**
* @param input a byte stream, containing data to be compressed
* @param sink a method that will accept chunks of compressed data; We don't pass
* a writable here, since we don't want the writablestream to be closed after
* a single file
* @returns a promise, which will resolve with the crc32 checksum and the
* compressed size
*/
export async function deflateStream(input: Readable, sink: (chunk: Buffer) => void) {
const deflateWriter = new Writable({
write(chunk, _, callback) {
sink(chunk);
callback();
}
});
// tee the input stream, so we can compress and calc crc32 in parallel
const [rs1, rs2] = tee(input);
const byteCounter = new ByteCounter();
const [_, crc] = await Promise.all([
// pipe input into zip compressor, count the bytes
// returned and pass compressed data to the sink
pipeline(rs1, createDeflateRaw(), byteCounter, deflateWriter),
// calc checksum
crc32(rs2)
]);
return { crc32: crc, compressedSize: byteCounter.size };
}

View File

@ -1,81 +1,277 @@
import { basename as pathBasename, join as pathJoin } from "path";
import { execSync } from "child_process";
import * as fs from "fs";
import { dirname as pathDirname, basename as pathBasename, join as pathJoin, join } from "path";
import { createReadStream, createWriteStream } from "fs";
import { stat, mkdir, unlink, writeFile } from "fs/promises";
import { transformCodebase } from "./transformCodebase";
import { rm, rm_rf } from "./rm";
import * as crypto from "crypto";
import { createHash } from "crypto";
import fetch from "make-fetch-happen";
import { createInflateRaw } from "zlib";
import type { Readable } from "stream";
import { homedir } from "os";
import { FetchOptions } from "make-fetch-happen";
import { exec as execCallback } from "child_process";
import { promisify } from "util";
/** assert url ends with .zip */
export function downloadAndUnzip(params: {
const exec = promisify(execCallback);
function hash(s: string) {
return createHash("sha256").update(s).digest("hex");
}
async function maybeStat(path: string) {
try {
return await stat(path);
} catch (error) {
if ((error as Error & { code: string }).code === "ENOENT") return undefined;
throw error;
}
}
/**
* Get an npm configuration value as string, undefined if not set.
*
* @param key
* @returns string or undefined
*/
async function getNmpConfig(key: string): Promise<string | undefined> {
const { stdout } = await exec(`npm config get ${key}`);
const value = stdout.trim();
return value && value !== "null" ? value : undefined;
}
/**
* Get proxy configuration from npm config files. Note that we don't care about
* proxy config in env vars, because make-fetch-happen will do that for us.
*
* @returns proxy configuration
*/
async function getNpmProxyConfig(): Promise<Pick<FetchOptions, "proxy" | "noProxy">> {
const proxy = (await getNmpConfig("https-proxy")) ?? (await getNmpConfig("proxy"));
const noProxy = (await getNmpConfig("noproxy")) ?? (await getNmpConfig("no-proxy"));
return { proxy, noProxy };
}
/**
* Download a file from `url` to `dir`. Will try to avoid downloading existing
* files by using the cache directory ~/.keycloakify/cache
*
* If the target directory does not exist, it will be created.
*
* If the target file exists, it will be overwritten.
*
* We use make-fetch-happen's internal file cache here, so we don't need to
* worry about redownloading the same file over and over. Unfortunately, that
* cache does not have a single file per entry, but bundles and indexes them,
* so we still need to write the contents to the target directory (possibly
* over and over), cause the current unzip implementation wants random access.
*
* @param url download url
* @param dir target directory
* @param filename target filename
* @returns promise for the full path of the downloaded file
*/
async function download(url: string, dir: string, filename: string): Promise<string> {
const proxyOpts = await getNpmProxyConfig();
const cacheRoot = process.env.XDG_CACHE_HOME ?? homedir();
const cachePath = join(cacheRoot, ".keycloakify/cache");
const opts: FetchOptions = { cachePath, ...proxyOpts };
const response = await fetch(url, opts);
const filepath = pathJoin(dir, filename);
await mkdir(dir, { recursive: true });
await writeFile(filepath, response.body);
return filepath;
}
/**
* @typedef
* @type MultiError = Error & { cause: Error[] }
*/
/**
* Extract the archive `zipFile` into the directory `dir`. If `archiveDir` is given,
* only that directory will be extracted, stripping the given path components.
*
* If dir does not exist, it will be created.
*
* If any archive file exists, it will be overwritten.
*
* Will unzip using all available nodejs worker threads.
*
* Will try to clean up extracted files on failure.
*
* If unpacking fails, will either throw an regular error, or
* possibly an `MultiError`, which contains a `cause` field with
* a number of root cause errors.
*
* Warning this method is not optimized for continuous reading of the zip
* archive, but is a trade-off between simplicity and allowing extraction
* of a single directory from the archive.
*
* @param zipFile the file to unzip
* @param dir the target directory
* @param archiveDir if given, unpack only files from this archive directory
* @throws {MultiError} error
* @returns Promise for a list of full file paths pointing to actually extracted files
*/
async function unzip(zipFile: string, dir: string, archiveDir?: string): Promise<string[]> {
await mkdir(dir, { recursive: true });
const promises: Promise<string>[] = [];
// Iterate over all files in the zip, skip files which are not in archiveDir,
// if given.
for await (const record of iterateZipArchive(zipFile)) {
const { path: recordPath, createReadStream: createRecordReadStream } = record;
const filePath = pathJoin(dir, recordPath);
const parent = pathDirname(filePath);
if (archiveDir && !recordPath.startsWith(archiveDir)) continue;
promises.push(
new Promise<string>(async (resolve, reject) => {
await mkdir(parent, { recursive: true });
// Pull the file out of the archive, write it to the target directory
const input = createRecordReadStream();
const output = createWriteStream(filePath);
output.setMaxListeners(Infinity);
output.on("error", e => reject(Object.assign(e, { filePath })));
output.on("finish", () => resolve(filePath));
input.pipe(output);
})
);
}
// Wait until _all_ files are either extracted or failed
const results = await Promise.allSettled(promises);
const success = results.filter(r => r.status === "fulfilled").map(r => (r as PromiseFulfilledResult<string>).value);
const failure = results.filter(r => r.status === "rejected").map(r => (r as PromiseRejectedResult).reason);
// If any extraction failed, try to clean up, then throw a MultiError,
// which has a `cause` field, containing a list of root cause errors.
if (failure.length) {
await Promise.all(success.map(path => unlink(path)));
await Promise.all(failure.map(e => e && e.path && unlink(e.path as string)));
const e = new Error("Failed to extract: " + failure.map(e => e.message).join(";"));
(e as any).cause = failure;
throw e;
}
return success;
}
/**
*
* @param file file to read
* @param start first byte to read
* @param end last byte to read
* @returns Promise of a buffer of read bytes
*/
async function readFileChunk(file: string, start: number, end: number): Promise<Buffer> {
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
const stream = createReadStream(file, { start, end });
stream.setMaxListeners(Infinity);
stream.on("error", e => reject(e));
stream.on("end", () => resolve(Buffer.concat(chunks)));
stream.on("data", chunk => chunks.push(chunk as Buffer));
});
}
type ZipRecord = {
path: string;
createReadStream: () => Readable;
compressionMethod: "deflate" | undefined;
};
type ZipRecordGenerator = AsyncGenerator<ZipRecord, void, unknown>;
/**
* Iterate over all records of a zipfile, and yield a ZipRecord.
* Use `record.createReadStream()` to actually read the file.
*
* Warning this method will only work with single-disk zip files.
* Warning this method may fail if the zip archive has an crazy amount
* of files and the central directory is not fully contained within the
* last 65k bytes of the zip file.
*
* @param zipFile
* @returns AsyncGenerator which will yield ZipRecords
*/
async function* iterateZipArchive(zipFile: string): ZipRecordGenerator {
// Need to know zip file size before we can do anything else
const { size } = await stat(zipFile);
const chunkSize = 65_535 + 22 + 1; // max comment size + end header size + wiggle
// Read last ~65k bytes. Zip files have an comment up to 65_535 bytes at the very end,
// before that comes the zip central directory end header.
let chunk = await readFileChunk(zipFile, size - chunkSize, size);
const unread = size - chunk.length;
let i = chunk.length - 4;
let found = false;
// Find central directory end header, reading backwards from the end
while (!found && i-- > 0) if (chunk[i] === 0x50 && chunk.readUInt32LE(i) === 0x06054b50) found = true;
if (!found) throw new Error("Not a zip file");
// This method will fail on a multi-disk zip, so bail early.
if (chunk.readUInt16LE(i + 4) !== 0) throw new Error("Multi-disk zip not supported");
let nFiles = chunk.readUint16LE(i + 10);
// Get the position of the central directory
const directorySize = chunk.readUint32LE(i + 12);
const directoryOffset = chunk.readUint32LE(i + 16);
if (directoryOffset === 0xffff_ffff) throw new Error("zip64 not supported");
if (directoryOffset > size) throw new Error(`Central directory offset ${directoryOffset} is outside file`);
i = directoryOffset - unread;
// If i < 0, it means that the central directory is not contained within `chunk`
if (i < 0) {
chunk = await readFileChunk(zipFile, directoryOffset, directoryOffset + directorySize);
i = 0;
}
// Now iterate the central directory records, yield an `ZipRecord` for every entry
while (nFiles-- > 0) {
// Check for marker bytes
if (chunk.readUInt32LE(i) !== 0x02014b50) throw new Error("No central directory record at position " + (unread + i));
const compressionMethod = ({ 8: "deflate" } as const)[chunk.readUint16LE(i + 10)];
const compressedFileSize = chunk.readUint32LE(i + 20);
const filenameLength = chunk.readUint16LE(i + 28);
const extraLength = chunk.readUint16LE(i + 30);
const commentLength = chunk.readUint16LE(i + 32);
// Start of the actual content byte stream is after the 'local' record header,
// which is 30 bytes long plus filename and extra field
const start = chunk.readUint32LE(i + 42) + 30 + filenameLength + extraLength;
const end = start + compressedFileSize;
const filename = chunk.slice(i + 46, i + 46 + filenameLength).toString("utf-8");
const createRecordReadStream = () => {
const input = createReadStream(zipFile, { start, end });
if (compressionMethod === "deflate") {
const inflate = createInflateRaw();
input.pipe(inflate);
return inflate;
}
return input;
};
if (end > start) yield { path: filename, createReadStream: createRecordReadStream, compressionMethod };
// advance pointer to next central directory entry
i += 46 + filenameLength + extraLength + commentLength;
}
}
export async function downloadAndUnzip({
url,
destDirPath,
pathOfDirToExtractInArchive,
cacheDirPath
}: {
isSilent: boolean;
url: string;
destDirPath: string;
pathOfDirToExtractInArchive?: string;
cacheDirPath: string;
}) {
const { url, destDirPath, pathOfDirToExtractInArchive, cacheDirPath } = params;
const downloadHash = hash(JSON.stringify({ url, pathOfDirToExtractInArchive })).substring(0, 15);
const extractDirPath = pathJoin(cacheDirPath, `_${downloadHash}`);
const extractDirPath = pathJoin(
cacheDirPath,
`_${crypto.createHash("sha256").update(JSON.stringify({ url, pathOfDirToExtractInArchive })).digest("hex").substring(0, 15)}`
);
const filename = pathBasename(url);
const zipFilepath = await download(url, cacheDirPath, filename);
const zipMtime = (await stat(zipFilepath)).mtimeMs;
const unzipMtime = (await maybeStat(extractDirPath))?.mtimeMs;
fs.mkdirSync(cacheDirPath, { "recursive": true });
if (!unzipMtime || zipMtime > unzipMtime) await unzip(zipFilepath, extractDirPath, pathOfDirToExtractInArchive);
const { readIsSuccessByExtractDirPath, writeIsSuccessByExtractDirPath } = (() => {
const filePath = pathJoin(cacheDirPath, "isSuccessByExtractDirPath.json");
type IsSuccessByExtractDirPath = Record<string, boolean | undefined>;
function readIsSuccessByExtractDirPath(): IsSuccessByExtractDirPath {
if (!fs.existsSync(filePath)) {
return {};
}
return JSON.parse(fs.readFileSync(filePath).toString("utf8"));
}
function writeIsSuccessByExtractDirPath(isSuccessByExtractDirPath: IsSuccessByExtractDirPath): void {
fs.writeFileSync(filePath, Buffer.from(JSON.stringify(isSuccessByExtractDirPath, null, 2), "utf8"));
}
return { readIsSuccessByExtractDirPath, writeIsSuccessByExtractDirPath };
})();
downloadAndUnzip: {
const isSuccessByExtractDirPath = readIsSuccessByExtractDirPath();
if (isSuccessByExtractDirPath[extractDirPath]) {
break downloadAndUnzip;
}
writeIsSuccessByExtractDirPath({
...isSuccessByExtractDirPath,
[extractDirPath]: false
});
rm_rf(extractDirPath);
fs.mkdirSync(extractDirPath);
const zipFileBasename = pathBasename(url);
execSync(`curl -L ${url} -o ${zipFileBasename} ${params.isSilent ? "-s" : ""}`, { "cwd": extractDirPath });
execSync(`unzip -o ${zipFileBasename}${pathOfDirToExtractInArchive === undefined ? "" : ` "${pathOfDirToExtractInArchive}/**/*"`}`, {
"cwd": extractDirPath
});
rm(zipFileBasename, { "cwd": extractDirPath });
writeIsSuccessByExtractDirPath({
...isSuccessByExtractDirPath,
[extractDirPath]: true
});
}
transformCodebase({
"srcDirPath": pathOfDirToExtractInArchive === undefined ? extractDirPath : pathJoin(extractDirPath, pathOfDirToExtractInArchive),
destDirPath
});
const srcDirPath = pathOfDirToExtractInArchive === undefined ? extractDirPath : pathJoin(extractDirPath, pathOfDirToExtractInArchive);
transformCodebase({ srcDirPath, destDirPath });
}

View File

@ -1,10 +1,17 @@
import { getProjectRoot } from "./getProjectRoot";
import { join as pathJoin } from "path";
import * as child_process from "child_process";
import * as fs from "fs";
import { constants } from "fs";
import { chmod, stat } from "fs/promises";
Object.entries<string>(JSON.parse(fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8"))["bin"]).forEach(([, scriptPath]) =>
child_process.execSync(`chmod +x ${scriptPath}`, {
"cwd": getProjectRoot()
})
);
(async () => {
const { bin } = await import(pathJoin(getProjectRoot(), "package.json"));
const promises = Object.values<string>(bin).map(async scriptPath => {
const fullPath = pathJoin(getProjectRoot(), scriptPath);
const oldMode = (await stat(fullPath)).mode;
const newMode = oldMode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH;
await chmod(fullPath, newMode);
});
await Promise.all(promises);
})();

102
src/bin/tools/jar.ts Normal file
View File

@ -0,0 +1,102 @@
import { Readable, Transform } from "stream";
import { dirname, relative, sep } from "path";
import { createWriteStream } from "fs";
import walk from "./walk";
import type { ZipSource } from "./zip";
import zip from "./zip";
import { mkdir } from "fs/promises";
/** Trim leading whitespace from every line */
const trimIndent = (s: string) => s.replace(/(\n)\s+/g, "$1");
type JarArgs = {
rootPath: string;
targetPath: string;
groupId: string;
artifactId: string;
version: string;
};
/**
* Create a jar archive, using the resources found at `rootPath` (a directory) and write the
* archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
* the contents of the pom.properties file which is going to be added to the archive.
*/
export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
const manifest: ZipSource = {
path: "META-INF/MANIFEST.MF",
data: Buffer.from(
trimIndent(
`Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Keycloakify
Built-By: unknown
Build-Jdk: 19.0.0`
)
)
};
const pomProps: ZipSource = {
path: `META-INF/maven/${groupId}/${artifactId}/pom.properties`,
data: Buffer.from(
trimIndent(
`# Generated by keycloakify
# ${new Date()}
artifactId=${artifactId}
groupId=${groupId}
version=${version}`
)
)
};
/**
* Convert every path entry to a ZipSource record, and when all records are
* processed, append records for MANIFEST.mf and pom.properties
*/
const pathToRecord = () =>
new Transform({
objectMode: true,
transform: function (fsPath, _, cb) {
const path = relative(rootPath, fsPath).split(sep).join("/");
this.push({ path, fsPath });
cb();
},
final: function () {
this.push(manifest);
this.push(pomProps);
this.push(null);
}
});
await mkdir(dirname(targetPath), { recursive: true });
// Create an async pipeline, wait until everything is fully processed
await new Promise<void>((resolve, reject) => {
// walk all files in `rootPath` recursively
Readable.from(walk(rootPath))
// transform every path into a ZipSource object
.pipe(pathToRecord())
// let the zip lib convert all ZipSource objects into a byte stream
.pipe(zip())
// write that byte stream to targetPath
.pipe(createWriteStream(targetPath, { encoding: "binary" }))
.on("finish", () => resolve())
.on("error", e => reject(e));
});
}
/**
* Standalone usage, call e.g. `ts-node jar.ts dirWithSources some-jar.jar`
*/
if (require.main === module) {
const main = () =>
jar({
rootPath: process.argv[2],
targetPath: process.argv[3],
artifactId: process.env.ARTIFACT_ID ?? "artifact",
groupId: process.env.GROUP_ID ?? "group",
version: process.env.VERSION ?? "1.0.0"
});
main().catch(e => console.error(e));
}

View File

@ -0,0 +1,7 @@
import { capitalize } from "tsafe/capitalize";
export function kebabCaseToCamelCase(kebabCaseString: string): string {
const [first, ...rest] = kebabCaseString.split("-");
return [first, ...rest.map(capitalize)].join("");
}

View File

@ -1,31 +0,0 @@
import { execSync } from "child_process";
function rmInternal(params: { pathToRemove: string; args: string | undefined; cwd: string | undefined }) {
const { pathToRemove, args, cwd } = params;
execSync(`rm ${args ? `-${args} ` : ""}${pathToRemove.replace(/ /g, "\\ ")}`, cwd !== undefined ? { cwd } : undefined);
}
export function rm(pathToRemove: string, options?: { cwd: string }) {
rmInternal({
pathToRemove,
"args": undefined,
"cwd": options?.cwd
});
}
export function rm_r(pathToRemove: string, options?: { cwd: string }) {
rmInternal({
pathToRemove,
"args": "r",
"cwd": options?.cwd
});
}
export function rm_rf(pathToRemove: string, options?: { cwd: string }) {
rmInternal({
pathToRemove,
"args": "rf",
"cwd": options?.cwd
});
}

37
src/bin/tools/tee.ts Normal file
View File

@ -0,0 +1,37 @@
import { PassThrough, Readable } from "stream";
export default function tee(input: Readable) {
const a = new PassThrough();
const b = new PassThrough();
let aFull = false;
let bFull = false;
a.on("drain", () => {
aFull = false;
if (!aFull && !bFull) input.resume();
});
b.on("drain", () => {
bFull = false;
if (!aFull && !bFull) input.resume();
});
input.on("error", e => {
a.emit("error", e);
b.emit("error", e);
});
input.on("data", chunk => {
aFull = !a.write(chunk);
bFull = !b.write(chunk);
if (aFull || bFull) input.pause();
});
input.on("end", () => {
a.end();
b.end();
});
return [a, b] as const;
}

19
src/bin/tools/walk.ts Normal file
View File

@ -0,0 +1,19 @@
import { readdir } from "fs/promises";
import { resolve } from "path";
/**
* Asynchronously and recursively walk a directory tree, yielding every file and directory
* found
*
* @param root the starting directory
* @returns AsyncGenerator
*/
export default async function* walk(root: string): AsyncGenerator<string, void, void> {
for (const entry of await readdir(root, { withFileTypes: true })) {
const absolutePath = resolve(root, entry.name);
if (entry.isDirectory()) {
yield absolutePath;
yield* walk(absolutePath);
} else yield absolutePath;
}
}

246
src/bin/tools/zip.ts Normal file
View File

@ -0,0 +1,246 @@
import { Transform, TransformOptions } from "stream";
import { createReadStream } from "fs";
import { stat } from "fs/promises";
import { Blob } from "buffer";
import { deflateBuffer, deflateStream } from "./deflate";
/**
* Zip source
* @property filename the name of the entry in the archie
* @property path of the source file, if the source is an actual file
* @property data the actual data buffer, if the source is constructed in-memory
*/
export type ZipSource = { path: string } & ({ fsPath: string } | { data: Buffer });
export type ZipRecord = {
path: string;
compression: "deflate" | undefined;
uncompressedSize: number;
compressedSize?: number;
crc32?: number;
offset?: number;
};
/**
* @returns the actual byte size of an string
*/
function utf8size(s: string) {
return new Blob([s]).size;
}
/**
* @param record
* @returns a buffer representing a Zip local header
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#Local_file_header
*/
function localHeader(record: ZipRecord) {
const { path, compression, uncompressedSize } = record;
const filenameSize = utf8size(path);
const buf = Buffer.alloc(30 + filenameSize);
buf.writeUInt32LE(0x04_03_4b_50, 0); // local header signature
buf.writeUInt16LE(10, 4); // min version
// we write 0x08 because crc and compressed size are unknown at
buf.writeUInt16LE(0x08, 6); // general purpose bit flag
buf.writeUInt16LE(compression ? ({ "deflate": 8 } as const)[compression] : 0, 8);
buf.writeUInt16LE(0, 10); // modified time
buf.writeUInt16LE(0, 12); // modified date
buf.writeUInt32LE(0, 14); // crc unknown
buf.writeUInt32LE(0, 18); // compressed size unknown
buf.writeUInt32LE(uncompressedSize, 22);
buf.writeUInt16LE(filenameSize, 26);
buf.writeUInt16LE(0, 28); // extra field length
buf.write(path, 30, "utf-8");
return buf;
}
/**
* @param record
* @returns a buffer representing a Zip central header
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#Central_directory_file_header
*/
function centralHeader(record: ZipRecord) {
const { path, compression, crc32, compressedSize, uncompressedSize, offset } = record;
const filenameSize = utf8size(path);
const buf = Buffer.alloc(46 + filenameSize);
const isFile = !path.endsWith("/");
if (typeof offset === "undefined") throw new Error("Illegal argument");
// we don't want to deal with possibly messed up file or directory
// permissions, so we ignore the original permissions
const externalAttr = isFile ? 0x81a40000 : 0x41ed0000;
buf.writeUInt32LE(0x0201_4b50, 0); // central header signature
buf.writeUInt16LE(10, 4); // version
buf.writeUInt16LE(10, 6); // min version
buf.writeUInt16LE(0, 8); // general purpose bit flag
buf.writeUInt16LE(compression ? ({ "deflate": 8 } as const)[compression] : 0, 10);
buf.writeUInt16LE(0, 12); // modified time
buf.writeUInt16LE(0, 14); // modified date
buf.writeUInt32LE(crc32 || 0, 16);
buf.writeUInt32LE(compressedSize || 0, 20);
buf.writeUInt32LE(uncompressedSize, 24);
buf.writeUInt16LE(filenameSize, 28);
buf.writeUInt16LE(0, 30); // extra field length
buf.writeUInt16LE(0, 32); // comment field length
buf.writeUInt16LE(0, 34); // disk number
buf.writeUInt16LE(0, 36); // internal
buf.writeUInt32LE(externalAttr, 38); // external
buf.writeUInt32LE(offset, 42); // offset where file starts
buf.write(path, 46, "utf-8");
return buf;
}
/**
* @returns a buffer representing an Zip End-Of-Central-Directory block
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#End_of_central_directory_record_(EOCD)
*/
function eocd({ offset, cdSize, nRecords }: { offset: number; cdSize: number; nRecords: number }) {
const buf = Buffer.alloc(22);
buf.writeUint32LE(0x06054b50, 0); // eocd signature
buf.writeUInt16LE(0, 4); // disc number
buf.writeUint16LE(0, 6); // disc where central directory starts
buf.writeUint16LE(nRecords, 8); // records on this disc
buf.writeUInt16LE(nRecords, 10); // records total
buf.writeUInt32LE(cdSize, 12); // byte size of cd
buf.writeUInt32LE(offset, 16); // cd offset
buf.writeUint16LE(0, 20); // comment length
return buf;
}
/**
* @returns a stream Transform, which reads a stream of ZipRecords and
* writes a bytestream
*/
export default function zip() {
/**
* This is called when the input stream of ZipSource items is finished.
* Will write central directory and end-of-central-direcotry blocks.
*/
const final = () => {
// write central directory
let cdSize = 0;
for (const record of records) {
const head = centralHeader(record);
zipTransform.push(head);
cdSize += head.length;
}
// write end-of-central-directory
zipTransform.push(eocd({ offset, cdSize, nRecords: records.length }));
// signal stream end
zipTransform.push(null);
};
/**
* Write a directory entry to the archive
* @param path
*/
const writeDir = async (path: string) => {
const record: ZipRecord = {
path: path + "/",
offset,
compression: undefined,
uncompressedSize: 0
};
const head = localHeader(record);
zipTransform.push(head);
records.push(record);
offset += head.length;
};
/**
* Write a file entry to the archive
* @param archivePath path of the file in archive
* @param fsPath path to file on filesystem
* @param size of the actual, uncompressed, file
*/
const writeFile = async (archivePath: string, fsPath: string, size: number) => {
const record: ZipRecord = {
path: archivePath,
offset,
compression: "deflate",
uncompressedSize: size
};
const head = localHeader(record);
zipTransform.push(head);
const { crc32, compressedSize } = await deflateStream(createReadStream(fsPath), chunk => zipTransform.push(chunk));
record.crc32 = crc32;
record.compressedSize = compressedSize;
records.push(record);
offset += head.length + compressedSize;
};
/**
* Write archive record based on filesystem file or directory
* @param archivePath path of item in archive
* @param fsPath path to item on filesystem
*/
const writeFromPath = async (archivePath: string, fsPath: string) => {
const fileStats = await stat(fsPath);
fileStats.isDirectory() ? await writeDir(archivePath) /**/ : await writeFile(archivePath, fsPath, fileStats.size) /**/;
};
/**
* Write archive record based on data in a buffer
* @param path
* @param data
*/
const writeFromBuffer = async (path: string, data: Buffer) => {
const { deflated, crc32 } = await deflateBuffer(data);
const record: ZipRecord = {
path,
compression: "deflate",
crc32,
uncompressedSize: data.length,
compressedSize: deflated.length,
offset
};
const head = localHeader(record);
zipTransform.push(head);
zipTransform.push(deflated);
records.push(record);
offset += head.length + deflated.length;
};
/**
* Write an archive record
* @param source
*/
const writeRecord = async (source: ZipSource) => {
if ("fsPath" in source) await writeFromPath(source.path, source.fsPath);
else if ("data" in source) await writeFromBuffer(source.path, source.data);
else throw new Error("Illegal argument " + typeof source + " " + JSON.stringify(source));
};
/**
* The actual stream transform function
* @param source
* @param _ encoding, ignored
* @param cb
*/
const transform: TransformOptions["transform"] = async (source: ZipSource, _, cb) => {
await writeRecord(source);
cb();
};
/** offset and records keep local state during processing */
let offset = 0;
const records: ZipRecord[] = [];
const zipTransform = new Transform({
readableObjectMode: false,
writableObjectMode: true,
transform,
final
});
return zipTransform;
}

1
src/index.ts Normal file
View File

@ -0,0 +1 @@
export { createKeycloakAdapter } from "keycloakify/lib/keycloakJsAdapter";

View File

@ -1,43 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { I18n } from "../i18n";
export type ErrorProps = KcProps & {
kcContext: KcContextBase.Error;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const Error = memo((props: ErrorProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { message, client } = kcContext;
const { msg } = i18n;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={msg("errorTitle")}
formNode={
<div id="kc-error-message">
<p className="instruction">{message.summary}</p>
{client !== undefined && client.baseUrl !== undefined && (
<p>
<a id="backToApplication" href={client.baseUrl}>
{msg("backToApplication")}
</a>
</p>
)}
</div>
}
/>
);
});
export default Error;

View File

@ -1,60 +0,0 @@
import React, { useState, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
import { UserProfileFormFields } from "./shared/UserProfileCommons";
export type IdpReviewUserProfileProps = KcProps & {
kcContext: KcContextBase.IdpReviewUserProfile;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const IdpReviewUserProfile = memo((props: IdpReviewUserProfileProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
const { url } = kcContext;
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("loginIdpReviewProfileTitle")}
formNode={
<form id="kc-idp-review-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
value={msgStr("doSubmit")}
disabled={!isFomSubmittable}
/>
</div>
</div>
</form>
}
/>
);
});
export default IdpReviewUserProfile;

View File

@ -1,60 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { I18n } from "../i18n";
export type InfoProps = KcProps & {
kcContext: KcContextBase.Info;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const Info = memo((props: InfoProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { msgStr, msg } = i18n;
assert(kcContext.message !== undefined);
const { messageHeader, message, requiredActions, skipLink, pageRedirectUri, actionUri, client } = kcContext;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
formNode={
<div id="kc-info-message">
<p className="instruction">
{message.summary}
{requiredActions !== undefined && (
<b>{requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",")}</b>
)}
</p>
{!skipLink && pageRedirectUri !== undefined ? (
<p>
<a href={pageRedirectUri}>{msg("backToApplication")}</a>
</p>
) : actionUri !== undefined ? (
<p>
<a href={actionUri}>{msg("proceedWithAction")}</a>
</p>
) : (
client.baseUrl !== undefined && (
<p>
<a href={client.baseUrl}>{msg("backToApplication")}</a>
</p>
)
)}
</div>
}
/>
);
});
export default Info;

View File

@ -1,109 +0,0 @@
import React, { lazy, memo, Suspense } from "react";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { KcProps } from "./KcProps";
import { __unsafe_useI18n as useI18n } from "../i18n";
import type { I18n } from "../i18n";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
const Login = lazy(() => import("./Login"));
const Register = lazy(() => import("./Register"));
const RegisterUserProfile = lazy(() => import("./RegisterUserProfile"));
const Info = lazy(() => import("./Info"));
const Error = lazy(() => import("./Error"));
const LoginResetPassword = lazy(() => import("./LoginResetPassword"));
const LoginVerifyEmail = lazy(() => import("./LoginVerifyEmail"));
const Terms = lazy(() => import("./Terms"));
const LoginOtp = lazy(() => import("./LoginOtp"));
const LoginPassword = lazy(() => import("./LoginPassword"));
const LoginUsername = lazy(() => import("./LoginUsername"));
const WebauthnAuthenticate = lazy(() => import("./WebauthnAuthenticate"));
const LoginUpdatePassword = lazy(() => import("./LoginUpdatePassword"));
const LoginUpdateProfile = lazy(() => import("./LoginUpdateProfile"));
const LoginIdpLinkConfirm = lazy(() => import("./LoginIdpLinkConfirm"));
const LoginPageExpired = lazy(() => import("./LoginPageExpired"));
const LoginIdpLinkEmail = lazy(() => import("./LoginIdpLinkEmail"));
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
const UpdateUserProfile = lazy(() => import("./UpdateUserProfile"));
const IdpReviewUserProfile = lazy(() => import("./IdpReviewUserProfile"));
export type KcAppProps = KcProps & {
kcContext: KcContextBase;
i18n?: I18n;
doFetchDefaultThemeResources?: boolean;
Template: (props: TemplateProps) => JSX.Element | null;
};
const KcApp = memo((props_: KcAppProps) => {
const { kcContext, i18n: userProvidedI18n, Template = DefaultTemplate, ...kcProps } = props_;
const i18n = (function useClosure() {
const i18n = useI18n({
kcContext,
"extraMessages": {},
"doSkip": userProvidedI18n !== undefined
});
return userProvidedI18n ?? i18n;
})();
if (i18n === null) {
return null;
}
const commonProps = { i18n, Template, ...kcProps };
return (
<Suspense>
{(() => {
switch (kcContext.pageId) {
case "login.ftl":
return <Login {...{ kcContext, ...commonProps }} />;
case "register.ftl":
return <Register {...{ kcContext, ...commonProps }} />;
case "register-user-profile.ftl":
return <RegisterUserProfile {...{ kcContext, ...commonProps }} />;
case "info.ftl":
return <Info {...{ kcContext, ...commonProps }} />;
case "error.ftl":
return <Error {...{ kcContext, ...commonProps }} />;
case "login-reset-password.ftl":
return <LoginResetPassword {...{ kcContext, ...commonProps }} />;
case "login-verify-email.ftl":
return <LoginVerifyEmail {...{ kcContext, ...commonProps }} />;
case "terms.ftl":
return <Terms {...{ kcContext, ...commonProps }} />;
case "login-otp.ftl":
return <LoginOtp {...{ kcContext, ...commonProps }} />;
case "login-username.ftl":
return <LoginUsername {...{ kcContext, ...commonProps }} />;
case "login-password.ftl":
return <LoginPassword {...{ kcContext, ...commonProps }} />;
case "webauthn-authenticate.ftl":
return <WebauthnAuthenticate {...{ kcContext, ...commonProps }} />;
case "login-update-password.ftl":
return <LoginUpdatePassword {...{ kcContext, ...commonProps }} />;
case "login-update-profile.ftl":
return <LoginUpdateProfile {...{ kcContext, ...commonProps }} />;
case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm {...{ kcContext, ...commonProps }} />;
case "login-idp-link-email.ftl":
return <LoginIdpLinkEmail {...{ kcContext, ...commonProps }} />;
case "login-page-expired.ftl":
return <LoginPageExpired {...{ kcContext, ...commonProps }} />;
case "login-config-totp.ftl":
return <LoginConfigTotp {...{ kcContext, ...commonProps }} />;
case "logout-confirm.ftl":
return <LogoutConfirm {...{ kcContext, ...commonProps }} />;
case "update-user-profile.ftl":
return <UpdateUserProfile {...{ kcContext, ...commonProps }} />;
case "idp-review-user-profile.ftl":
return <IdpReviewUserProfile {...{ kcContext, ...commonProps }} />;
}
})()}
</Suspense>
);
});
export default KcApp;

View File

@ -1,213 +0,0 @@
import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined";
import { assert } from "tsafe/assert";
/** Class names can be provided as an array or separated by whitespace */
export type KcPropsGeneric<CssClasses extends string> = {
[key in CssClasses]: readonly string[] | string | undefined;
};
export type KcTemplateClassKey =
| "stylesCommon"
| "styles"
| "scripts"
| "kcHtmlClass"
| "kcLoginClass"
| "kcHeaderClass"
| "kcHeaderWrapperClass"
| "kcFormCardClass"
| "kcFormCardAccountClass"
| "kcFormHeaderClass"
| "kcLocaleWrapperClass"
| "kcContentWrapperClass"
| "kcLabelWrapperClass"
| "kcFormGroupClass"
| "kcResetFlowIcon"
| "kcFeedbackSuccessIcon"
| "kcFeedbackWarningIcon"
| "kcFeedbackErrorIcon"
| "kcFeedbackInfoIcon"
| "kcFormSocialAccountContentClass"
| "kcFormSocialAccountClass"
| "kcSignUpClass"
| "kcInfoAreaWrapperClass";
export type KcTemplateProps = KcPropsGeneric<KcTemplateClassKey>;
export const defaultKcTemplateProps = {
"stylesCommon": [
"node_modules/patternfly/dist/css/patternfly.min.css",
"node_modules/patternfly/dist/css/patternfly-additions.min.css",
"lib/zocial/zocial.css"
],
"styles": ["css/login.css"],
"scripts": [],
"kcHtmlClass": ["login-pf"],
"kcLoginClass": ["login-pf-page"],
"kcContentWrapperClass": ["row"],
"kcHeaderClass": ["login-pf-page-header"],
"kcHeaderWrapperClass": [],
"kcFormCardClass": ["card-pf"],
"kcFormCardAccountClass": ["login-pf-accounts"],
"kcFormSocialAccountClass": ["login-pf-social-section"],
"kcFormSocialAccountContentClass": ["col-xs-12", "col-sm-6"],
"kcFormHeaderClass": ["login-pf-header"],
"kcLocaleWrapperClass": [],
"kcFeedbackErrorIcon": ["pficon", "pficon-error-circle-o"],
"kcFeedbackWarningIcon": ["pficon", "pficon-warning-triangle-o"],
"kcFeedbackSuccessIcon": ["pficon", "pficon-ok"],
"kcFeedbackInfoIcon": ["pficon", "pficon-info"],
"kcResetFlowIcon": ["pficon", "pficon-arrow fa-2x"],
"kcFormGroupClass": ["form-group"],
"kcLabelWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
"kcSignUpClass": ["login-pf-signup"],
"kcInfoAreaWrapperClass": []
} as const;
assert<typeof defaultKcTemplateProps extends KcTemplateProps ? true : false>();
/** Tu use if you don't want any default */
export const allClearKcTemplateProps = allPropertiesValuesToUndefined(defaultKcTemplateProps);
assert<typeof allClearKcTemplateProps extends KcTemplateProps ? true : false>();
export type KcProps = KcPropsGeneric<
| KcTemplateClassKey
| "kcLogoLink"
| "kcLogoClass"
| "kcContainerClass"
| "kcContentClass"
| "kcFeedbackAreaClass"
| "kcLocaleClass"
| "kcAlertIconClasserror"
| "kcFormAreaClass"
| "kcFormSocialAccountListClass"
| "kcFormSocialAccountDoubleListClass"
| "kcFormSocialAccountListLinkClass"
| "kcWebAuthnKeyIcon"
| "kcWebAuthnDefaultIcon"
| "kcFormClass"
| "kcFormGroupErrorClass"
| "kcLabelClass"
| "kcInputClass"
| "kcInputErrorMessageClass"
| "kcInputWrapperClass"
| "kcFormOptionsClass"
| "kcFormButtonsClass"
| "kcFormSettingClass"
| "kcTextareaClass"
| "kcInfoAreaClass"
| "kcFormGroupHeader"
| "kcButtonClass"
| "kcButtonPrimaryClass"
| "kcButtonDefaultClass"
| "kcButtonLargeClass"
| "kcButtonBlockClass"
| "kcInputLargeClass"
| "kcSrOnlyClass"
| "kcSelectAuthListClass"
| "kcSelectAuthListItemClass"
| "kcSelectAuthListItemFillClass"
| "kcSelectAuthListItemInfoClass"
| "kcSelectAuthListItemLeftClass"
| "kcSelectAuthListItemBodyClass"
| "kcSelectAuthListItemDescriptionClass"
| "kcSelectAuthListItemHeadingClass"
| "kcSelectAuthListItemHelpTextClass"
| "kcSelectAuthListItemIconPropertyClass"
| "kcSelectAuthListItemIconClass"
| "kcSelectAuthListItemTitle"
| "kcAuthenticatorDefaultClass"
| "kcAuthenticatorPasswordClass"
| "kcAuthenticatorOTPClass"
| "kcAuthenticatorWebAuthnClass"
| "kcAuthenticatorWebAuthnPasswordlessClass"
| "kcSelectOTPListClass"
| "kcSelectOTPListItemClass"
| "kcAuthenticatorOtpCircleClass"
| "kcSelectOTPItemHeadingClass"
| "kcFormOptionsWrapperClass"
>;
export const defaultKcProps = {
...defaultKcTemplateProps,
"kcLogoLink": "http://www.keycloak.org",
"kcLogoClass": "login-pf-brand",
"kcContainerClass": "container-fluid",
"kcContentClass": ["col-sm-8", "col-sm-offset-2", "col-md-6", "col-md-offset-3", "col-lg-6", "col-lg-offset-3"],
"kcFeedbackAreaClass": ["col-md-12"],
"kcLocaleClass": ["col-xs-12", "col-sm-1"],
"kcAlertIconClasserror": ["pficon", "pficon-error-circle-o"],
"kcFormAreaClass": ["col-sm-10", "col-sm-offset-1", "col-md-8", "col-md-offset-2", "col-lg-8", "col-lg-offset-2"],
"kcFormSocialAccountListClass": ["login-pf-social", "list-unstyled", "login-pf-social-all"],
"kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"],
"kcFormSocialAccountListLinkClass": ["login-pf-social-link"],
"kcWebAuthnKeyIcon": ["pficon", "pficon-key"],
"kcWebAuthnDefaultIcon": ["pficon", "pficon-key"],
"kcFormClass": ["form-horizontal"],
"kcFormGroupErrorClass": ["has-error"],
"kcLabelClass": ["control-label"],
"kcInputClass": ["form-control"],
"kcInputErrorMessageClass": ["pf-c-form__helper-text", "pf-m-error", "required", "kc-feedback-text"],
"kcInputWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
"kcFormOptionsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
"kcFormButtonsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
"kcFormSettingClass": ["login-pf-settings"],
"kcTextareaClass": ["form-control"],
"kcInfoAreaClass": ["col-xs-12", "col-sm-4", "col-md-4", "col-lg-5", "details"],
// user-profile grouping
"kcFormGroupHeader": ["pf-c-form__group"],
// css classes for form buttons main class used for all buttons
"kcButtonClass": ["btn"],
// classes defining priority of the button - primary or default (there is typically only one priority button for the form)
"kcButtonPrimaryClass": ["btn-primary"],
"kcButtonDefaultClass": ["btn-default"],
// classes defining size of the button
"kcButtonLargeClass": ["btn-lg"],
"kcButtonBlockClass": ["btn-block"],
// css classes for input
"kcInputLargeClass": ["input-lg"],
// css classes for form accessability
"kcSrOnlyClass": ["sr-only"],
// css classes for select-authenticator form
"kcSelectAuthListClass": ["list-group", "list-view-pf"],
"kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"],
"kcSelectAuthListItemFillClass": ["pf-l-split__item", "pf-m-fill"],
"kcSelectAuthListItemIconPropertyClass": ["fa-2x", "select-auth-box-icon-properties"],
"kcSelectAuthListItemIconClass": ["pf-l-split__item", "select-auth-box-icon"],
"kcSelectAuthListItemTitle": ["select-auth-box-paragraph"],
"kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"],
"kcSelectAuthListItemLeftClass": ["list-view-pf-left"],
"kcSelectAuthListItemBodyClass": ["list-view-pf-body"],
"kcSelectAuthListItemDescriptionClass": ["list-view-pf-description"],
"kcSelectAuthListItemHeadingClass": ["list-group-item-heading"],
"kcSelectAuthListItemHelpTextClass": ["list-group-item-text"],
// css classes for the authenticators
"kcAuthenticatorDefaultClass": ["fa", "list-view-pf-icon-lg"],
"kcAuthenticatorPasswordClass": ["fa", "fa-unlock list-view-pf-icon-lg"],
"kcAuthenticatorOTPClass": ["fa", "fa-mobile", "list-view-pf-icon-lg"],
"kcAuthenticatorWebAuthnClass": ["fa", "fa-key", "list-view-pf-icon-lg"],
"kcAuthenticatorWebAuthnPasswordlessClass": ["fa", "fa-key", "list-view-pf-icon-lg"],
//css classes for the OTP Login Form
"kcSelectOTPListClass": ["card-pf", "card-pf-view", "card-pf-view-select", "card-pf-view-single-select"],
"kcSelectOTPListItemClass": ["card-pf-body", "card-pf-top-element"],
"kcAuthenticatorOtpCircleClass": ["fa", "fa-mobile", "card-pf-icon-circle"],
"kcSelectOTPItemHeadingClass": ["card-pf-title", "text-center"],
"kcFormOptionsWrapperClass": []
} as const;
assert<typeof defaultKcProps extends KcProps ? true : false>();
/** Tu use if you don't want any default */
export const allClearKcProps = allPropertiesValuesToUndefined(defaultKcProps);
assert<typeof allClearKcProps extends KcProps ? true : false>();

View File

@ -1,206 +0,0 @@
import React, { useState, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import { useConstCallback } from "powerhooks/useConstCallback";
import type { FormEventHandler } from "react";
import type { I18n } from "../i18n";
export type LoginProps = KcProps & {
kcContext: KcContextBase.Login;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const Login = memo((props: LoginProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
const { msg, msgStr } = i18n;
const { cx } = useCssAndCx();
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
e.preventDefault();
setIsLoginButtonDisabled(true);
const formElement = e.target as HTMLFormElement;
//NOTE: Even if we login with email Keycloak expect username and password in
//the POST request.
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
formElement.submit();
});
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayInfo={social.displayInfo}
displayWide={realm.password && social.providers !== undefined}
headerNode={msg("doLogIn")}
formNode={
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
<div
id="kc-form-wrapper"
className={cx(
realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
)}
>
{realm.password && (
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
<div className={cx(kcProps.kcFormGroupClass)}>
{(() => {
const label = !realm.loginWithEmailAllowed
? "username"
: realm.registrationEmailAsUsername
? "email"
: "usernameOrEmail";
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
return (
<>
<label htmlFor={autoCompleteHelper} className={cx(kcProps.kcLabelClass)}>
{msg(label)}
</label>
<input
tabIndex={1}
id={autoCompleteHelper}
className={cx(kcProps.kcInputClass)}
//NOTE: This is used by Google Chrome auto fill so we use it to tell
//the browser how to pre fill the form but before submit we put it back
//to username because it is what keycloak expects.
name={autoCompleteHelper}
defaultValue={login.username ?? ""}
type="text"
{...(usernameEditDisabled
? { "disabled": true }
: {
"autoFocus": true,
"autoComplete": "off"
})}
/>
</>
);
})()}
</div>
<div className={cx(kcProps.kcFormGroupClass)}>
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
{msg("password")}
</label>
<input
tabIndex={2}
id="password"
className={cx(kcProps.kcInputClass)}
name="password"
type="password"
autoComplete="off"
/>
</div>
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
<div id="kc-form-options">
{realm.rememberMe && !usernameEditDisabled && (
<div className="checkbox">
<label>
<input
tabIndex={3}
id="rememberMe"
name="rememberMe"
type="checkbox"
{...(login.rememberMe
? {
"checked": true
}
: {})}
/>
{msg("rememberMe")}
</label>
</div>
)}
</div>
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
{realm.resetPasswordAllowed && (
<span>
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
{msg("doForgotPassword")}
</a>
</span>
)}
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
<input
type="hidden"
id="id-hidden-input"
name="credentialId"
{...(auth?.selectedCredential !== undefined
? {
"value": auth.selectedCredential
}
: {})}
/>
<input
tabIndex={4}
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="login"
id="kc-login"
type="submit"
value={msgStr("doLogIn")}
disabled={isLoginButtonDisabled}
/>
</div>
</form>
)}
</div>
{realm.password && social.providers !== undefined && (
<div id="kc-social-providers" className={cx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
<ul
className={cx(
kcProps.kcFormSocialAccountListClass,
social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
)}
>
{social.providers.map(p => (
<li key={p.providerId} className={cx(kcProps.kcFormSocialAccountListLinkClass)}>
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
<span>{p.displayName}</span>
</a>
</li>
))}
</ul>
</div>
)}
</div>
}
infoNode={
realm.password &&
realm.registrationAllowed &&
!registrationDisabled && (
<div id="kc-registration">
<span>
{msg("noAccount")}
<a tabIndex={6} href={url.registrationUrl}>
{msg("doRegister")}
</a>
</span>
</div>
)
}
/>
);
});
export default Login;

View File

@ -1,195 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type LoginConfigTotpProps = KcProps & {
kcContext: KcContextBase.LoginConfigTotp;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginConfigTotp = memo((props: LoginConfigTotpProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
"HmacSHA1": "SHA1",
"HmacSHA256": "SHA256",
"HmacSHA512": "SHA512"
};
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("loginTotpTitle")}
formNode={
<>
<ol id="kc-totp-settings">
<li>
<p>{msg("loginTotpStep1")}</p>
<ul id="kc-totp-supported-apps">
{totp.policy.supportedApplications.map(app => (
<li>{app}</li>
))}
</ul>
</li>
{mode && mode == "manual" ? (
<>
<li>
<p>{msg("loginTotpManualStep2")}</p>
<p>
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
</p>
<p>
<a href={totp.qrUrl} id="mode-barcode">
{msg("loginTotpScanBarcode")}
</a>
</p>
</li>
<li>
<p>{msg("loginTotpManualStep3")}</p>
<p>
<ul>
<li id="kc-totp-type">
{msg("loginTotpType")}: {msg(`loginTotp.${totp.policy.type}`)}
</li>
<li id="kc-totp-algorithm">
{msg("loginTotpAlgorithm")}: {algToKeyUriAlg?.[totp.policy.algorithm] ?? totp.policy.algorithm}
</li>
<li id="kc-totp-digits">
{msg("loginTotpDigits")}: {totp.policy.digits}
</li>
{totp.policy.type === "totp" ? (
<li id="kc-totp-period">
{msg("loginTotpInterval")}: {totp.policy.period}
</li>
) : (
<li id="kc-totp-counter">
{msg("loginTotpCounter")}: {totp.policy.initialCounter}
</li>
)}
</ul>
</p>
</li>
</>
) : (
<li>
<p>{msg("loginTotpStep2")}</p>
<img id="kc-totp-secret-qr-code" src={`data:image/png;base64, ${totp.totpSecretQrCode}`} alt="Figure: Barcode" />
<br />
<p>
<a href={totp.manualUrl} id="mode-manual">
{msg("loginTotpUnableToScan")}
</a>
</p>
</li>
)}
<li>
<p>{msg("loginTotpStep3")}</p>
<p>{msg("loginTotpStep3DeviceName")}</p>
</li>
</ol>
<form action={url.loginAction} className={cx(kcProps.kcFormClass)} id="kc-totp-settings-form" method="post">
<div className={cx(kcProps.kcFormGroupClass)}>
<div className={cx(kcProps.kcInputWrapperClass)}>
<label htmlFor="totp" className={cx(kcProps.kcLabelClass)}>
{msg("authenticatorCode")}
</label>{" "}
<span className="required">*</span>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="totp"
name="totp"
autoComplete="off"
className={cx(kcProps.kcInputClass)}
aria-invalid={messagesPerField.existsError("totp")}
/>
{messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={cx(kcProps.kcInputErrorMessageClass)} aria-live="polite">
{messagesPerField.get("totp")}
</span>
)}
</div>
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
{mode && <input type="hidden" id="mode" value={mode} />}
</div>
<div className={cx(kcProps.kcFormGroupClass)}>
<div className={cx(kcProps.kcInputWrapperClass)}>
<label htmlFor="userLabel" className={cx(kcProps.kcLabelClass)}>
{msg("loginTotpDeviceName")}
</label>{" "}
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="userLabel"
name="userLabel"
autoComplete="off"
className={cx(kcProps.kcInputClass)}
aria-invalid={messagesPerField.existsError("userLabel")}
/>
{messagesPerField.existsError("userLabel") && (
<span id="input-error-otp-label" className={cx(kcProps.kcInputErrorMessageClass)} aria-live="polite">
{messagesPerField.get("userLabel")}
</span>
)}
</div>
</div>
{isAppInitiatedAction ? (
<>
<input
type="submit"
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
id="saveTOTPBtn"
value={msgStr("doSubmit")}
/>
<button
type="submit"
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonDefaultClass,
kcProps.kcButtonLargeClass,
kcProps.kcButtonLargeClass
)}
id="cancelTOTPBtn"
name="cancel-aia"
value="true"
>
${msg("doCancel")}
</button>
</>
) : (
<input
type="submit"
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
id="saveTOTPBtn"
value={msgStr("doSubmit")}
/>
)}
</form>
</>
}
/>
);
});
export default LoginConfigTotp;

View File

@ -1,67 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type LoginIdpLinkConfirmProps = KcProps & {
kcContext: KcContextBase.LoginIdpLinkConfirm;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginIdpLinkConfirm = memo((props: LoginIdpLinkConfirmProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url, idpAlias } = kcContext;
const { msg } = i18n;
const { cx } = useCssAndCx();
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("confirmLinkIdpTitle")}
formNode={
<form id="kc-register-form" action={url.loginAction} method="post">
<div className={cx(kcProps.kcFormGroupClass)}>
<button
type="submit"
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonDefaultClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="submitAction"
id="updateProfile"
value="updateProfile"
>
{msg("confirmLinkIdpReviewProfile")}
</button>
<button
type="submit"
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonDefaultClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="submitAction"
id="linkAccount"
value="linkAccount"
>
{msg("confirmLinkIdpContinue", idpAlias)}
</button>
</div>
</form>
}
/>
);
});
export default LoginIdpLinkConfirm;

View File

@ -1,43 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { I18n } from "../i18n";
export type LoginIdpLinkEmailProps = KcProps & {
kcContext: KcContextBase.LoginIdpLinkEmail;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginIdpLinkEmail = memo((props: LoginIdpLinkEmailProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url, realm, brokerContext, idpAlias } = kcContext;
const { msg } = i18n;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("emailLinkIdpTitle", idpAlias)}
formNode={
<>
<p id="instruction1" className="instruction">
{msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.displayName)}
</p>
<p id="instruction2" className="instruction">
{msg("emailLinkIdp2")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp3")}
</p>
<p id="instruction3" className="instruction">
{msg("emailLinkIdp4")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp5")}
</p>
</>
}
/>
);
});
export default LoginIdpLinkEmail;

View File

@ -1,127 +0,0 @@
import React, { useEffect, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { headInsert } from "../tools/headInsert";
import { pathJoin } from "../../bin/tools/pathJoin";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type LoginOtpProps = KcProps & {
kcContext: KcContextBase.LoginOtp;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginOtp = memo((props: LoginOtpProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { otpLogin, url } = kcContext;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
useEffect(() => {
let isCleanedUp = false;
headInsert({
"type": "javascript",
"src": pathJoin(kcContext.url.resourcesCommonPath, "node_modules/jquery/dist/jquery.min.js")
}).then(() => {
if (isCleanedUp) return;
evaluateInlineScript();
});
return () => {
isCleanedUp = true;
};
}, []);
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("doLogIn")}
formNode={
<form id="kc-otp-login-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
{otpLogin.userOtpCredentials.length > 1 && (
<div className={cx(kcProps.kcFormGroupClass)}>
<div className={cx(kcProps.kcInputWrapperClass)}>
{otpLogin.userOtpCredentials.map(otpCredential => (
<div key={otpCredential.id} className={cx(kcProps.kcSelectOTPListClass)}>
<input type="hidden" value="${otpCredential.id}" />
<div className={cx(kcProps.kcSelectOTPListItemClass)}>
<span className={cx(kcProps.kcAuthenticatorOtpCircleClass)} />
<h2 className={cx(kcProps.kcSelectOTPItemHeadingClass)}>{otpCredential.userLabel}</h2>
</div>
</div>
))}
</div>
</div>
)}
<div className={cx(kcProps.kcFormGroupClass)}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="otp" className={cx(kcProps.kcLabelClass)}>
{msg("loginOtpOneTime")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input id="otp" name="otp" autoComplete="off" type="text" className={cx(kcProps.kcInputClass)} autoFocus />
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="login"
id="kc-login"
type="submit"
value={msgStr("doLogIn")}
/>
</div>
</div>
</form>
}
/>
);
});
declare const $: any;
function evaluateInlineScript() {
$(document).ready(function () {
// Card Single Select
$(".card-pf-view-single-select").click(function (this: any) {
if ($(this).hasClass("active")) {
$(this).removeClass("active");
$(this).children().removeAttr("name");
} else {
$(".card-pf-view-single-select").removeClass("active");
$(".card-pf-view-single-select").children().removeAttr("name");
$(this).addClass("active");
$(this).children().attr("name", "selectedCredentialId");
}
});
var defaultCred = $(".card-pf-view-single-select")[0];
if (defaultCred) {
defaultCred.click();
}
});
}
export default LoginOtp;

View File

@ -1,47 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { I18n } from "../i18n";
export type LoginPageExpired = KcProps & {
kcContext: KcContextBase.LoginPageExpired;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginPageExpired = memo((props: LoginPageExpired) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url } = kcContext;
const { msg } = i18n;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={msg("pageExpiredTitle")}
formNode={
<>
<p id="instruction1" className="instruction">
{msg("pageExpiredMsg1")}
<a id="loginRestartLink" href={url.loginRestartFlowUrl}>
{msg("doClickHere")}
</a>{" "}
.<br />
{msg("pageExpiredMsg2")}{" "}
<a id="loginContinueLink" href={url.loginAction}>
{msg("doClickHere")}
</a>{" "}
.
</p>
</>
}
/>
);
});
export default LoginPageExpired;

View File

@ -1,99 +0,0 @@
import React, { useState, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import { useConstCallback } from "powerhooks/useConstCallback";
import type { FormEventHandler } from "react";
import type { I18n } from "../i18n";
export type LoginPasswordProps = KcProps & {
kcContext: KcContextBase.LoginPassword;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginPassword = memo((props: LoginPasswordProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { realm, url, login } = kcContext;
const { msg, msgStr } = i18n;
const { cx } = useCssAndCx();
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
e.preventDefault();
setIsLoginButtonDisabled(true);
const formElement = e.target as HTMLFormElement;
formElement.submit();
});
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("doLogIn")}
formNode={
<div id="kc-form">
<div id="kc-form-wrapper">
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
<div className={cx(kcProps.kcFormGroupClass)}>
<hr />
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
{msg("password")}
</label>
<input
tabIndex={2}
id="password"
className={cx(kcProps.kcInputClass)}
name="password"
type="password"
autoFocus={true}
autoComplete="on"
defaultValue={login.password ?? ""}
/>
</div>
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
<div id="kc-form-options" />
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
{realm.resetPasswordAllowed && (
<span>
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
{msg("doForgotPassword")}
</a>
</span>
)}
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
<input
tabIndex={4}
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="login"
id="kc-login"
type="submit"
value={msgStr("doLogIn")}
disabled={isLoginButtonDisabled}
/>
</div>
</form>
</div>
</div>
}
/>
);
});
export default LoginPassword;

View File

@ -1,82 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type LoginResetPasswordProps = KcProps & {
kcContext: KcContextBase.LoginResetPassword;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginResetPassword = memo((props: LoginResetPasswordProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url, realm, auth } = kcContext;
const { msg, msgStr } = i18n;
const { cx } = useCssAndCx();
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={msg("emailForgotTitle")}
formNode={
<form id="kc-reset-password-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
<div className={cx(kcProps.kcFormGroupClass)}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
{!realm.loginWithEmailAllowed
? msg("username")
: !realm.registrationEmailAsUsername
? msg("usernameOrEmail")
: msg("email")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="username"
name="username"
className={cx(kcProps.kcInputClass)}
autoFocus
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
value={msgStr("doSubmit")}
/>
</div>
</div>
</form>
}
infoNode={msg("emailInstruction")}
/>
);
});
export default LoginResetPassword;

View File

@ -1,128 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type LoginUpdatePasswordProps = KcProps & {
kcContext: KcContextBase.LoginUpdatePassword;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginUpdatePassword = memo((props: LoginUpdatePasswordProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("updatePasswordTitle")}
formNode={
<form id="kc-passwd-update-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
<input
type="text"
id="username"
name="username"
value={username}
readOnly={true}
autoComplete="username"
style={{ display: "none" }}
/>
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="password-new" className={cx(kcProps.kcLabelClass)}>
{msg("passwordNew")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="password"
id="password-new"
name="password-new"
autoFocus
autoComplete="new-password"
className={cx(kcProps.kcInputClass)}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="password-confirm" className={cx(kcProps.kcLabelClass)}>
{msg("passwordConfirm")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="password"
id="password-confirm"
name="password-confirm"
autoComplete="new-password"
className={cx(kcProps.kcInputClass)}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
{isAppInitiatedAction && (
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" checked />
{msgStr("logoutOtherSessions")}
</label>
</div>
)}
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
{isAppInitiatedAction ? (
<>
<input
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
<button
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
type="submit"
name="cancel-aia"
value="true"
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
)}
</div>
</div>
</form>
}
/>
);
});
export default LoginUpdatePassword;

View File

@ -1,137 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type LoginUpdateProfile = KcProps & {
kcContext: KcContextBase.LoginUpdateProfile;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginUpdateProfile = memo((props: LoginUpdateProfile) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("loginProfileTitle")}
formNode={
<form id="kc-update-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
{user.editUsernameAllowed && (
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
{msg("username")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="username"
name="username"
defaultValue={user.username ?? ""}
className={cx(kcProps.kcInputClass)}
/>
</div>
</div>
)}
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="email" className={cx(kcProps.kcLabelClass)}>
{msg("email")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(kcProps.kcInputClass)} />
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="firstName" className={cx(kcProps.kcLabelClass)}>
{msg("firstName")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="firstName"
name="firstName"
defaultValue={user.firstName ?? ""}
className={cx(kcProps.kcInputClass)}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="lastName" className={cx(kcProps.kcLabelClass)}>
{msg("lastName")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="lastName"
name="lastName"
defaultValue={user.lastName ?? ""}
className={cx(kcProps.kcInputClass)}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)} />
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
{isAppInitiatedAction ? (
<>
<input
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
<button
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
type="submit"
name="cancel-aia"
value="true"
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
)}
</div>
</div>
</form>
}
/>
);
});
export default LoginUpdateProfile;

View File

@ -1,171 +0,0 @@
import React, { useState, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import { useConstCallback } from "powerhooks/useConstCallback";
import type { FormEventHandler } from "react";
import type { I18n } from "../i18n";
export type LoginUsernameProps = KcProps & {
kcContext: KcContextBase.LoginUsername;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginUsername = memo((props: LoginUsernameProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext;
const { msg, msgStr } = i18n;
const { cx } = useCssAndCx();
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
e.preventDefault();
setIsLoginButtonDisabled(true);
const formElement = e.target as HTMLFormElement;
//NOTE: Even if we login with email Keycloak expect username and password in
//the POST request.
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
formElement.submit();
});
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayInfo={social.displayInfo}
displayWide={realm.password && social.providers !== undefined}
headerNode={msg("doLogIn")}
formNode={
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
<div
id="kc-form-wrapper"
className={cx(
realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
)}
>
{realm.password && (
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
<div className={cx(kcProps.kcFormGroupClass)}>
{!usernameHidden &&
(() => {
const label = !realm.loginWithEmailAllowed
? "username"
: realm.registrationEmailAsUsername
? "email"
: "usernameOrEmail";
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
return (
<>
<label htmlFor={autoCompleteHelper} className={cx(kcProps.kcLabelClass)}>
{msg(label)}
</label>
<input
tabIndex={1}
id={autoCompleteHelper}
className={cx(kcProps.kcInputClass)}
//NOTE: This is used by Google Chrome auto fill so we use it to tell
//the browser how to pre fill the form but before submit we put it back
//to username because it is what keycloak expects.
name={autoCompleteHelper}
defaultValue={login.username ?? ""}
type="text"
autoFocus={true}
autoComplete="off"
/>
</>
);
})()}
</div>
<div className={cx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
<div id="kc-form-options">
{realm.rememberMe && !usernameHidden && (
<div className="checkbox">
<label>
<input
tabIndex={3}
id="rememberMe"
name="rememberMe"
type="checkbox"
{...(login.rememberMe
? {
"checked": true
}
: {})}
/>
{msg("rememberMe")}
</label>
</div>
)}
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
<input
tabIndex={4}
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="login"
id="kc-login"
type="submit"
value={msgStr("doLogIn")}
disabled={isLoginButtonDisabled}
/>
</div>
</form>
)}
</div>
{realm.password && social.providers !== undefined && (
<div id="kc-social-providers" className={cx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
<ul
className={cx(
kcProps.kcFormSocialAccountListClass,
social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
)}
>
{social.providers.map(p => (
<li key={p.providerId} className={cx(kcProps.kcFormSocialAccountListLinkClass)}>
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
<span>{p.displayName}</span>
</a>
</li>
))}
</ul>
</div>
)}
</div>
}
infoNode={
realm.password &&
realm.registrationAllowed &&
!registrationDisabled && (
<div id="kc-registration">
<span>
{msg("noAccount")}
<a tabIndex={6} href={url.registrationUrl}>
{msg("doRegister")}
</a>
</span>
</div>
)
}
/>
);
});
export default LoginUsername;

View File

@ -1,43 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { I18n } from "../i18n";
export type LoginVerifyEmailProps = KcProps & {
kcContext: KcContextBase.LoginVerifyEmail;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LoginVerifyEmail = memo((props: LoginVerifyEmailProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { msg } = i18n;
const { url, user } = kcContext;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={msg("emailVerifyTitle")}
formNode={
<>
<p className="instruction">{msg("emailVerifyInstruction1", user?.email)}</p>
<p className="instruction">
{msg("emailVerifyInstruction2")}
<br />
<a href={url.loginAction}>{msg("doClickHere")}</a>
&nbsp;
{msg("emailVerifyInstruction3")}
</p>
</>
}
/>
);
});
export default LoginVerifyEmail;

View File

@ -1,71 +0,0 @@
import React, { memo } from "react";
import { useCssAndCx } from "../tools/useCssAndCx";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { I18n } from "../i18n";
export type LogoutConfirmProps = KcProps & {
kcContext: KcContextBase.LogoutConfirm;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const LogoutConfirm = memo((props: LogoutConfirmProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url, client, logoutConfirm } = kcContext;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={msg("logoutConfirmTitle")}
formNode={
<>
<div id="kc-logout-confirm" className="content-area">
<p className="instruction">{msg("logoutConfirmHeader")}</p>
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
<input type="hidden" name="session_code" value={logoutConfirm.code} />
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options">
<div className={cx(kcProps.kcFormOptionsWrapperClass)}></div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormGroupClass)}>
<input
tabIndex={4}
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
name="confirmLogout"
id="kc-logout"
type="submit"
value={msgStr("doLogout")}
/>
</div>
</div>
</form>
<div id="kc-info-message">
{!logoutConfirm.skipLink && client.baseUrl && (
<p>
<a href={client.baseUrl} dangerouslySetInnerHTML={{ __html: msgStr("backToApplication") }} />
</p>
)}
</div>
</div>
</>
}
/>
);
});
export default LogoutConfirm;

View File

@ -1,172 +0,0 @@
import React, { memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
export type RegisterProps = KcProps & {
kcContext: KcContextBase.Register;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const Register = memo((props: RegisterProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
const { msg, msgStr } = i18n;
const { cx } = useCssAndCx();
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("registerTitle")}
formNode={
<form id="kc-register-form" className={cx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="firstName" className={cx(kcProps.kcLabelClass)}>
{msg("firstName")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="firstName"
className={cx(kcProps.kcInputClass)}
name="firstName"
defaultValue={register.formData.firstName ?? ""}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="lastName" className={cx(kcProps.kcLabelClass)}>
{msg("lastName")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="lastName"
className={cx(kcProps.kcInputClass)}
name="lastName"
defaultValue={register.formData.lastName ?? ""}
/>
</div>
</div>
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="email" className={cx(kcProps.kcLabelClass)}>
{msg("email")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="email"
className={cx(kcProps.kcInputClass)}
name="email"
defaultValue={register.formData.email ?? ""}
autoComplete="email"
/>
</div>
</div>
{!realm.registrationEmailAsUsername && (
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="username" className={cx(kcProps.kcLabelClass)}>
{msg("username")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="text"
id="username"
className={cx(kcProps.kcInputClass)}
name="username"
defaultValue={register.formData.username ?? ""}
autoComplete="username"
/>
</div>
</div>
)}
{passwordRequired && (
<>
<div className={cx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="password" className={cx(kcProps.kcLabelClass)}>
{msg("password")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input
type="password"
id="password"
className={cx(kcProps.kcInputClass)}
name="password"
autoComplete="new-password"
/>
</div>
</div>
<div
className={cx(
kcProps.kcFormGroupClass,
messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass)
)}
>
<div className={cx(kcProps.kcLabelWrapperClass)}>
<label htmlFor="password-confirm" className={cx(kcProps.kcLabelClass)}>
{msg("passwordConfirm")}
</label>
</div>
<div className={cx(kcProps.kcInputWrapperClass)}>
<input type="password" id="password-confirm" className={cx(kcProps.kcInputClass)} name="password-confirm" />
</div>
</div>
</>
)}
{recaptchaRequired && (
<div className="form-group">
<div className={cx(kcProps.kcInputWrapperClass)}>
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
</div>
</div>
)}
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
value={msgStr("doRegister")}
/>
</div>
</div>
</form>
}
/>
);
});
export default Register;

View File

@ -1,81 +0,0 @@
import React, { useMemo, memo, useState } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
import { UserProfileFormFields } from "./shared/UserProfileCommons";
export type RegisterUserProfileProps = KcProps & {
kcContext: KcContextBase.RegisterUserProfile;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const RegisterUserProfile = memo((props: RegisterUserProfileProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps_ } = props;
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
const { msg, msgStr } = i18n;
const { cx, css } = useCssAndCx();
const kcProps = useMemo(
() => ({
...kcProps_,
"kcFormGroupClass": cx(kcProps_.kcFormGroupClass, css({ "marginBottom": 20 }))
}),
[cx, css]
);
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={messagesPerField.exists("global")}
displayRequiredFields={true}
headerNode={msg("registerTitle")}
formNode={
<form id="kc-register-form" className={cx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
{recaptchaRequired && (
<div className="form-group">
<div className={cx(kcProps.kcInputWrapperClass)}>
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
</div>
</div>
)}
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
value={msgStr("doRegister")}
disabled={!isFomSubmittable}
/>
</div>
</div>
</form>
}
/>
);
});
export default RegisterUserProfile;

View File

@ -1,118 +0,0 @@
import React, { useEffect, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import { Evt } from "evt";
import { useRerenderOnStateChange } from "evt/hooks";
import { assert } from "tsafe/assert";
import { fallbackLanguageTag } from "../i18n";
import type { I18n } from "../i18n";
import memoize from "memoizee";
import { useConst } from "powerhooks/useConst";
import { useConstCallback } from "powerhooks/useConstCallback";
import { Markdown } from "../tools/Markdown";
import type { Extends } from "tsafe";
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
export type KcContextLike = {
pageId: KcContextBase["pageId"];
locale?: {
currentLanguageTag: string;
};
};
assert<Extends<KcContextBase, KcContextLike>>();
/** Allow to avoid bundling the terms and download it on demand*/
export function useDownloadTerms(params: {
kcContext: KcContextLike;
downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string>;
}) {
const { kcContext } = params;
const { downloadTermMarkdownMemoized } = (function useClosure() {
const { downloadTermMarkdown } = params;
const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown);
const downloadTermMarkdownMemoized = useConst(() =>
memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag }), { "promise": true })
);
return { downloadTermMarkdownMemoized };
})();
useEffect(() => {
if (kcContext.pageId !== "terms.ftl") {
return;
}
downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then(
thermMarkdown => (evtTermMarkdown.state = thermMarkdown)
);
}, []);
}
export type TermsProps = KcProps & {
kcContext: KcContextBase.Terms;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const Terms = memo((props: TermsProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { msg, msgStr } = i18n;
useRerenderOnStateChange(evtTermMarkdown);
const { cx } = useCssAndCx();
const { url } = kcContext;
if (evtTermMarkdown.state === undefined) {
return null;
}
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
displayMessage={false}
headerNode={msg("termsTitle")}
formNode={
<>
<div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonClass,
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonLargeClass
)}
name="accept"
id="kc-accept"
type="submit"
value={msgStr("doAccept")}
/>
<input
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
name="cancel"
id="kc-decline"
type="submit"
value={msgStr("doDecline")}
/>
</form>
<div className="clearfix" />
</>
}
/>
);
});
export default Terms;

View File

@ -1,80 +0,0 @@
import React, { useState, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n } from "../i18n";
import { UserProfileFormFields } from "./shared/UserProfileCommons";
export type UpdateUserProfileProps = KcProps & {
kcContext: KcContextBase.UpdateUserProfile;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const UpdateUserProfile = memo((props: UpdateUserProfileProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { cx } = useCssAndCx();
const { msg, msgStr } = i18n;
const { url, isAppInitiatedAction } = kcContext;
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("loginProfileTitle")}
formNode={
<form id="kc-update-profile-form" className={cx(kcProps.kcFormClass)} action={url.loginAction} method="post">
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
<div className={cx(kcProps.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(kcProps.kcFormOptionsClass)}>
<div className={cx(kcProps.kcFormOptionsWrapperClass)}></div>
</div>
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
{isAppInitiatedAction ? (
<>
<input
className={cx(kcProps.kcButtonClass, kcProps.kcButtonPrimaryClass, kcProps.kcButtonLargeClass)}
type="submit"
value={msgStr("doSubmit")}
/>
<button
className={cx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
type="submit"
name="cancel-aia"
value="true"
formNoValidate
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
type="submit"
defaultValue={msgStr("doSubmit")}
disabled={!isFomSubmittable}
/>
)}
</div>
</div>
</form>
}
/>
);
});
export default UpdateUserProfile;

View File

@ -1,205 +0,0 @@
import React, { useRef, useState, memo } from "react";
import DefaultTemplate from "./Template";
import type { TemplateProps } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useCssAndCx } from "../tools/useCssAndCx";
import type { I18n, MessageKeyBase } from "../i18n";
import { base64url } from "rfc4648";
import { useConstCallback } from "powerhooks/useConstCallback";
export type WebauthnAuthenticateProps = KcProps & {
kcContext: KcContextBase.WebauthnAuthenticate;
i18n: I18n;
doFetchDefaultThemeResources?: boolean;
Template?: (props: TemplateProps) => JSX.Element | null;
};
const WebauthnAuthenticate = memo((props: WebauthnAuthenticateProps) => {
const { kcContext, i18n, doFetchDefaultThemeResources = true, Template = DefaultTemplate, ...kcProps } = props;
const { url } = kcContext;
const { msg, msgStr } = i18n;
const { authenticators, challenge, shouldDisplayAuthenticators, userVerification, rpId } = kcContext;
const createTimeout = Number(kcContext.createTimeout);
const isUserIdentified = kcContext.isUserIdentified == "true";
const { cx } = useCssAndCx();
const webAuthnAuthenticate = useConstCallback(async () => {
if (!isUserIdentified) {
return;
}
const allowCredentials = authenticators.authenticators.map(
authenticator =>
({
id: base64url.parse(authenticator.credentialId, { loose: true }),
type: "public-key"
} as PublicKeyCredentialDescriptor)
);
// Check if WebAuthn is supported by this browser
if (!window.PublicKeyCredential) {
setError(msgStr("webauthn-unsupported-browser-text"));
submitForm();
return;
}
const publicKey: PublicKeyCredentialRequestOptions = {
rpId,
challenge: base64url.parse(challenge, { loose: true })
};
if (createTimeout !== 0) {
publicKey.timeout = createTimeout * 1000;
}
if (allowCredentials.length) {
publicKey.allowCredentials = allowCredentials;
}
if (userVerification !== "not specified") {
publicKey.userVerification = userVerification;
}
try {
const resultRaw = await navigator.credentials.get({ publicKey });
if (!resultRaw || resultRaw.type != "public-key") return;
const result = resultRaw as PublicKeyCredential;
if (!("authenticatorData" in result.response)) return;
const response = result.response as AuthenticatorAssertionResponse;
const clientDataJSON = response.clientDataJSON;
const authenticatorData = response.authenticatorData;
const signature = response.signature;
setClientDataJSON(base64url.stringify(new Uint8Array(clientDataJSON), { pad: false }));
setAuthenticatorData(base64url.stringify(new Uint8Array(authenticatorData), { pad: false }));
setSignature(base64url.stringify(new Uint8Array(signature), { pad: false }));
setCredentialId(result.id);
setUserHandle(base64url.stringify(new Uint8Array(response.userHandle!), { pad: false }));
submitForm();
} catch (err) {
setError(String(err));
submitForm();
}
});
const webAuthForm = useRef<HTMLFormElement>(null);
const submitForm = useConstCallback(() => {
webAuthForm.current!.submit();
});
const [clientDataJSON, setClientDataJSON] = useState("");
const [authenticatorData, setAuthenticatorData] = useState("");
const [signature, setSignature] = useState("");
const [credentialId, setCredentialId] = useState("");
const [userHandle, setUserHandle] = useState("");
const [error, setError] = useState("");
return (
<Template
{...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
headerNode={msg("webauthn-login-title")}
formNode={
<div id="kc-form-webauthn" className={cx(kcProps.kcFormClass)}>
<form id="webauth" action={url.loginAction} ref={webAuthForm} method="post">
<input type="hidden" id="clientDataJSON" name="clientDataJSON" value={clientDataJSON} />
<input type="hidden" id="authenticatorData" name="authenticatorData" value={authenticatorData} />
<input type="hidden" id="signature" name="signature" value={signature} />
<input type="hidden" id="credentialId" name="credentialId" value={credentialId} />
<input type="hidden" id="userHandle" name="userHandle" value={userHandle} />
<input type="hidden" id="error" name="error" value={error} />
</form>
<div className={cx(kcProps.kcFormGroupClass)}>
{authenticators &&
(() => (
<form id="authn_select" className={cx(kcProps.kcFormClass)}>
{authenticators.authenticators.map(authenticator => (
<input
type="hidden"
name="authn_use_chk"
value={authenticator.credentialId}
key={authenticator.credentialId}
/>
))}
</form>
))()}
{authenticators &&
shouldDisplayAuthenticators &&
(() => (
<>
{authenticators.authenticators.length > 1 && (
<p className={cx(kcProps.kcSelectAuthListItemTitle)}>{msg("webauthn-available-authenticators")}</p>
)}
<div className={cx(kcProps.kcFormClass)}>
{authenticators.authenticators.map(authenticator => (
<div id="kc-webauthn-authenticator" className={cx(kcProps.kcSelectAuthListItemClass)}>
<div className={cx(kcProps.kcSelectAuthListItemIconClass)}>
<i
className={cx(
kcProps[authenticator.transports.iconClass] ?? kcProps.kcWebAuthnDefaultIcon,
kcProps.kcSelectAuthListItemIconPropertyClass
)}
/>
</div>
<div className={cx(kcProps.kcSelectAuthListItemBodyClass)}>
<div
id="kc-webauthn-authenticator-label"
className={cx(kcProps.kcSelectAuthListItemHeadingClass)}
>
{authenticator.label}
</div>
{authenticator.transports && authenticator.transports.displayNameProperties.length && (
<div
id="kc-webauthn-authenticator-transport"
className={cx(kcProps.kcSelectAuthListItemDescriptionClass)}
>
{authenticator.transports.displayNameProperties.map(
(transport: MessageKeyBase, index: number) => (
<>
<span>{msg(transport)}</span>
{index < authenticator.transports.displayNameProperties.length - 1 && (
<span>{", "}</span>
)}
</>
)
)}
</div>
)}
<div className={cx(kcProps.kcSelectAuthListItemDescriptionClass)}>
<span id="kc-webauthn-authenticator-created-label">{msg("webauthn-createdAt-label")}</span>
<span id="kc-webauthn-authenticator-created">{authenticator.createdAt}</span>
</div>
</div>
<div className={cx(kcProps.kcSelectAuthListItemFillClass)} />
</div>
))}
</div>
</>
))()}
<div id="kc-form-buttons" className={cx(kcProps.kcFormButtonsClass)}>
<input
id="authenticateWebAuthnButton"
type="button"
onClick={webAuthnAuthenticate}
autoFocus={true}
value={msgStr("webauthn-doAuthenticate")}
className={cx(
kcProps.kcButtonClass,
kcProps.kcButtonPrimaryClass,
kcProps.kcButtonBlockClass,
kcProps.kcButtonLargeClass
)}
/>
</div>
</div>
</div>
}
/>
);
});
export default WebauthnAuthenticate;

View File

@ -1,170 +0,0 @@
import React, { memo, useEffect, Fragment } from "react";
import type { KcProps } from "../KcProps";
import type { Attribute } from "../../getKcContext/KcContextBase";
import { useCssAndCx } from "../../tools/useCssAndCx";
import type { ReactComponent } from "../../tools/ReactComponent";
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
import { useFormValidationSlice } from "../../useFormValidationSlice";
import type { I18n } from "../../i18n";
import type { Param0 } from "tsafe/Param0";
export type UserProfileFormFieldsProps = {
kcContext: Param0<typeof useFormValidationSlice>["kcContext"];
i18n: I18n;
} & KcProps &
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
};
export const UserProfileFormFields = memo(
({ kcContext, onIsFormSubmittableValueChange, i18n, BeforeField, AfterField, ...props }: UserProfileFormFieldsProps) => {
const { cx, css } = useCssAndCx();
const { advancedMsg } = i18n;
const {
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
formValidationReducer,
attributesWithPassword
} = useFormValidationSlice({
kcContext,
i18n
});
useEffect(() => {
onIsFormSubmittableValueChange(isFormSubmittable);
}, [isFormSubmittable]);
const onChangeFactory = useCallbackFactory(
(
[name]: [string],
[
{
target: { value }
}
]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>]
) =>
formValidationReducer({
"action": "update value",
name,
"newValue": value
})
);
const onBlurFactory = useCallbackFactory(([name]: [string]) =>
formValidationReducer({
"action": "focus lost",
name
})
);
let currentGroup = "";
return (
<>
{attributesWithPassword.map((attribute, i) => {
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
const formGroupClassName = cx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
return (
<Fragment key={i}>
{group !== currentGroup && (currentGroup = group) !== "" && (
<div className={formGroupClassName}>
<div className={cx(props.kcContentWrapperClass)}>
<label id={`header-${group}`} className={cx(props.kcFormGroupHeader)}>
{advancedMsg(groupDisplayHeader) || currentGroup}
</label>
</div>
{groupDisplayDescription !== "" && (
<div className={cx(props.kcLabelWrapperClass)}>
<label id={`description-${group}`} className={`${cx(props.kcLabelClass)}`}>
{advancedMsg(groupDisplayDescription)}
</label>
</div>
)}
</div>
)}
{BeforeField && <BeforeField attribute={attribute} />}
<div className={formGroupClassName}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor={attribute.name} className={cx(props.kcLabelClass)}>
{advancedMsg(attribute.displayName ?? "")}
</label>
{attribute.required && <>*</>}
</div>
<div className={cx(props.kcInputWrapperClass)}>
{(() => {
const { options } = attribute.validators;
if (options !== undefined) {
return (
<select
id={attribute.name}
name={attribute.name}
onChange={onChangeFactory(attribute.name)}
onBlur={onBlurFactory(attribute.name)}
value={value}
>
{options.options.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
);
}
return (
<input
type={(() => {
switch (attribute.name) {
case "password-confirm":
case "password":
return "password";
default:
return "text";
}
})()}
id={attribute.name}
name={attribute.name}
value={value}
onChange={onChangeFactory(attribute.name)}
className={cx(props.kcInputClass)}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
autoComplete={attribute.autocomplete}
onBlur={onBlurFactory(attribute.name)}
/>
);
})()}
{displayableErrors.length !== 0 && (
<span
id={`input-error-${attribute.name}`}
className={cx(
props.kcInputErrorMessageClass,
css({
"position": displayableErrors.length === 1 ? "absolute" : undefined,
"& > span": { "display": "block" }
})
)}
aria-live="polite"
>
{displayableErrors.map(({ errorMessage }) => errorMessage)}
</span>
)}
</div>
</div>
{AfterField && <AfterField attribute={attribute} />}
</Fragment>
);
})}
</>
);
}
);

View File

@ -1,11 +0,0 @@
import type { KcContextBase } from "./KcContextBase";
import type { AndByDiscriminatingKey } from "../tools/AndByDiscriminatingKey";
import { ftlValuesGlobalName } from "../../bin/keycloakify/ftlValuesGlobalName";
export type ExtendsKcContextBase<KcContextExtended extends { pageId: string }> = [KcContextExtended] extends [never]
? KcContextBase
: AndByDiscriminatingKey<"pageId", KcContextExtended & KcContextBase.Common, KcContextBase>;
export function getKcContextFromWindow<KcContextExtended extends { pageId: string } = never>(): ExtendsKcContextBase<KcContextExtended> | undefined {
return typeof window === "undefined" ? undefined : (window as any)[ftlValuesGlobalName];
}

View File

@ -1,3 +0,0 @@
export type { KcContextBase, Attribute, Validators } from "./KcContextBase";
export type { ExtendsKcContextBase } from "./getKcContextFromWindow";
export { getKcContext } from "./getKcContext";

View File

@ -1 +0,0 @@
export * from "./kcContextMocks";

View File

@ -1,137 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Desa",
"doCancel": "Cancel·la",
"doLogOutAllSessions": "Desconnecta de totes les sessions",
"doRemove": "Elimina",
"doAdd": "Afegeix",
"doSignOut": "Desconnectar",
"editAccountHtmlTitle": "Edita compte",
"federatedIdentitiesHtmlTitle": "Identitats federades",
"accountLogHtmlTitle": "Registre del compte",
"changePasswordHtmlTitle": "Canvia contrasenya",
"sessionsHtmlTitle": "Sessions",
"accountManagementTitle": "Gestió de Compte Keycloak",
"authenticatorTitle": "Autenticador",
"applicationsHtmlTitle": "Aplicacions",
"authenticatorCode": "Codi d'un sol ús",
"email": "Email",
"firstName": "Nom",
"givenName": "Nom de pila",
"fullName": "Nom complet",
"lastName": "Cognoms",
"familyName": "Cognom",
"password": "Contrasenya",
"passwordConfirm": "Confirma la contrasenya",
"passwordNew": "Nova contrasenya",
"username": "Usuari",
"address": "Adreça",
"street": "Carrer",
"locality": "Ciutat o Municipi",
"region": "Estat, Província, o Regió",
"postal_code": "Postal code",
"country": "País",
"emailVerified": "Email verificat",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "Administrador",
"role_realm-admin": "Administrador del domini",
"role_create-realm": "Crear domini",
"role_view-realm": "Veure domini",
"role_view-users": "Veure usuaris",
"role_view-applications": "Veure aplicacions",
"role_view-clients": "Veure clients",
"role_view-events": "Veure events",
"role_view-identity-providers": "Veure proveïdors d'identitat",
"role_manage-realm": "Gestionar domini",
"role_manage-users": "Gestinar usuaris",
"role_manage-applications": "Gestionar aplicacions",
"role_manage-identity-providers": "Gestionar proveïdors d'identitat",
"role_manage-clients": "Gestionar clients",
"role_manage-events": "Gestionar events",
"role_view-profile": "Veure perfil",
"role_manage-account": "Gestionar compte",
"role_read-token": "Llegir token",
"role_offline-access": "Accés sense connexió",
"client_account": "Compte",
"client_security-admin-console": "Consola d'Administració de Seguretat",
"client_realm-management": "Gestió de domini",
"client_broker": "Broker",
"requiredFields": "Camps obligatoris",
"allFieldsRequired": "Tots els camps obligatoris",
"backToApplication": "&laquo; Torna a l'aplicació",
"backTo": "Torna a {0}",
"date": "Data",
"event": "Event",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Detalls",
"started": "Iniciat",
"lastAccess": "Últim accés",
"expires": "Expira",
"applications": "Aplicacions",
"account": "Compte",
"federatedIdentity": "Identitat federada",
"authenticator": "Autenticador",
"sessions": "Sessions",
"log": "Registre",
"application": "Aplicació",
"availablePermissions": "Permisos disponibles",
"grantedPermissions": "Permisos concedits",
"grantedPersonalInfo": "Informació personal concedida",
"additionalGrants": "Permisos addicionals",
"action": "Acció",
"inResource": "a",
"fullAccess": "Accés total",
"offlineToken": "Codi d'autorització offline",
"revoke": "Revocar permís",
"configureAuthenticators": "Autenticadors configurats",
"mobile": "Mòbil",
"totpStep1":
'Instal·la <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator al teu telèfon mòbil. Les dues aplicacions estan disponibles a <a href="https://play.google.com">Google Play</a> i en l\'App Store d\'Apple.',
"totpStep2": "Obre l'aplicació i escaneja el codi o introdueix la clau.",
"totpStep3": "Introdueix el codi únic que et mostra l'aplicació d'autenticació i fes clic a Envia per finalitzar la configuració",
"missingUsernameMessage": "Si us plau indica el teu usuari.",
"missingFirstNameMessage": "Si us plau indica el nom.",
"invalidEmailMessage": "Email no vàlid",
"missingLastNameMessage": "Si us plau indica els teus cognoms.",
"missingEmailMessage": "Si us plau indica l'email.",
"missingPasswordMessage": "Si us plau indica la contrasenya.",
"notMatchPasswordMessage": "Les contrasenyes no coincideixen.",
"missingTotpMessage": "Si us plau indica el teu codi d'autenticació",
"invalidPasswordExistingMessage": "La contrasenya actual no és correcta.",
"invalidPasswordConfirmMessage": "La confirmació de contrasenya no coincideix.",
"invalidTotpMessage": "El código de autenticación no es válido.",
"usernameExistsMessage": "L'usuari ja existeix",
"emailExistsMessage": "L'email ja existeix",
"readOnlyUserMessage": "No pots actualitzar el teu usuari perquè el teu compte és de només lectura.",
"readOnlyPasswordMessage": "No pots actualitzar la contrasenya perquè el teu compte és de només lectura.",
"successTotpMessage": "Aplicació d'autenticació mòbil configurada.",
"successTotpRemovedMessage": "Aplicació d'autenticació mòbil eliminada.",
"successGrantRevokedMessage": "Permís revocat correctament",
"accountUpdatedMessage": "El teu compte s'ha actualitzat.",
"accountPasswordUpdatedMessage": "La contrasenya s'ha actualitzat.",
"missingIdentityProviderMessage": "Proveïdor d'identitat no indicat.",
"invalidFederatedIdentityActionMessage": "Acció no vàlida o no indicada.",
"identityProviderNotFoundMessage": "No s'ha trobat un proveïdor d'identitat.",
"federatedIdentityLinkNotActiveMessage": "Aquesta identitat ja no està activa",
"federatedIdentityRemovingLastProviderMessage": "No pots eliminar l'última identitat federada perquè no tens fixada una contrasenya.",
"identityProviderRedirectErrorMessage": "Error en la redirecció al proveïdor d'identitat",
"identityProviderRemovedMessage": "Proveïdor d'identitat esborrat correctament.",
"accountDisabledMessage": "El compte està desactivada, contacteu amb l'administrador.",
"accountTemporarilyDisabledMessage": "El compte està temporalment desactivat, contacta amb l'administrador o intenta-ho de nou més tard.",
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular.",
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,156 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Uložit",
"doCancel": "Zrušit",
"doLogOutAllSessions": "Odhlásit všechny relace",
"doRemove": "Odstranit",
"doAdd": "Přidat",
"doSignOut": "Odhlásit se",
"editAccountHtmlTitle": "Upravit účet",
"federatedIdentitiesHtmlTitle": "Propojené identity",
"accountLogHtmlTitle": "Log účtu",
"changePasswordHtmlTitle": "Změnit heslo",
"sessionsHtmlTitle": "Relace",
"accountManagementTitle": "Správa účtů Keycloak",
"authenticatorTitle": "Autentizátor",
"applicationsHtmlTitle": "Aplikace",
"authenticatorCode": "Jednorázový kód",
"email": "E-mail",
"firstName": "První křestní jméno",
"givenName": "Křestní jména",
"fullName": "Celé jméno",
"lastName": "Příjmení",
"familyName": "Rodinné jméno",
"password": "Heslo",
"passwordConfirm": "Nové heslo (znovu)",
"passwordNew": "Nové heslo",
"username": "Uživatelské jméno",
"address": "Adresa",
"street": "Ulice",
"locality": "Město nebo lokalita",
"region": "Kraj",
"postal_code": "PSČ",
"country": "Stát",
"emailVerified": "E-mail ověřen",
"gssDelegationCredential": "GSS delegované oprávnění",
"role_admin": "Správce",
"role_realm-admin": "Správce realmu",
"role_create-realm": "Vytvořit realm",
"role_view-realm": "Zobrazit realm",
"role_view-users": "Zobrazit uživatele",
"role_view-applications": "Zobrazit aplikace",
"role_view-clients": "Zobrazit klienty",
"role_view-events": "Zobrazit události",
"role_view-identity-providers": "Zobrazit poskytovatele identity",
"role_manage-realm": "Spravovat realm",
"role_manage-users": "Spravovat uživatele",
"role_manage-applications": "Spravovat aplikace",
"role_manage-identity-providers": "Spravovat poskytovatele identity",
"role_manage-clients": "Spravovat klienty",
"role_manage-events": "Spravovat události",
"role_view-profile": "Zobrazit profil",
"role_manage-account": "Spravovat účet",
"role_manage-account-links": "Spravovat odkazy na účet",
"role_read-token": "Číst token",
"role_offline-access": "Přístup offline",
"role_uma_authorization": "Získání oprávnění",
"client_account": "Účet",
"client_security-admin-console": "Administrátorská bezpečnostní konzole",
"client_admin-cli": "Administrátorské CLI",
"client_realm-management": "Správa realmů",
"client_broker": "Broker",
"requiredFields": "Požadovaná pole",
"allFieldsRequired": "Všechna pole vyžadovaná",
"backToApplication": "&laquo; Zpět na aplikaci",
"backTo": "Zpět na {0}",
"date": "Datum",
"event": "Událost",
"ip": "IP",
"client": "Klient",
"clients": "Klienti",
"details": "Podrobnosti",
"started": "Zahájeno",
"lastAccess": "Poslední přístup",
"expires": "Vyprší",
"applications": "Aplikace",
"account": "Účet",
"federatedIdentity": "Propojená identita",
"authenticator": "Autentizátor",
"sessions": "Relace",
"log": "Log",
"application": "Aplikace",
"availablePermissions": "Dostupná oprávnění",
"grantedPermissions": "Udělené oprávnění",
"grantedPersonalInfo": "Poskytnuté osobní informace",
"additionalGrants": "Dodatečné oprávnění",
"action": "Akce",
"inResource": "v",
"fullAccess": "Úplný přístup",
"offlineToken": "Offline Token",
"revoke": "Zrušit oprávnění",
"configureAuthenticators": "Konfigurované autentizátory",
"mobile": "Mobilní",
"totpStep1": "Nainstalujte jednu z následujících aplikací",
"totpStep2": "Otevřete aplikaci a naskenujte čárový kód",
"totpStep3": "Zadejte jednorázový kód poskytnutý aplikací a klepnutím na tlačítko Uložit dokončete nastavení.",
"totpManualStep2": "Otevřete aplikaci a zadejte klíč",
"totpManualStep3": "Použijte následující hodnoty konfigurace, pokud aplikace umožňuje jejich nastavení",
"totpUnableToScan": "Nelze skenovat?",
"totpScanBarcode": "Skenovat čárový kód?",
"totp.totp": "Založeno na čase",
"totp.hotp": "Založeno na čítači",
"totpType": "Typ",
"totpAlgorithm": "Algoritmus",
"totpDigits": "Číslice",
"totpInterval": "Interval",
"totpCounter": "Čítač",
"missingUsernameMessage": "Zadejte uživatelské jméno.",
"missingFirstNameMessage": "Zadejte prosím křestní jméno.",
"invalidEmailMessage": "Neplatná e-mailová adresa.",
"missingLastNameMessage": "Zadejte prosím příjmení.",
"missingEmailMessage": "Zadejte prosím e-mail.",
"missingPasswordMessage": "Zadejte prosím heslo.",
"notMatchPasswordMessage": "Hesla se neshodují.",
"missingTotpMessage": "Zadejte prosím kód autentizátoru.",
"invalidPasswordExistingMessage": "Neplatné stávající heslo.",
"invalidPasswordConfirmMessage": "Nová hesla se neshodují.",
"invalidTotpMessage": "Neplatný kód autentizátoru.",
"usernameExistsMessage": "Uživatelské jméno již existuje.",
"emailExistsMessage": "E-mail již existuje.",
"readOnlyUserMessage": "Nemůžete svůj účet aktualizovat, protože je pouze pro čtení.",
"readOnlyUsernameMessage": "Nemůžete aktualizovat své uživatelské jméno, protože je pouze pro čtení.",
"readOnlyPasswordMessage": "Nemůžete aktualizovat své heslo, protože váš účet je jen pro čtení.",
"successTotpMessage": "Ověření pomocí OTP úspěšně konfigurováno.",
"successTotpRemovedMessage": "Ověření pomocí OTP úspěšně odstraněno.",
"successGrantRevokedMessage": "Oprávnění bylo úspěšně zrušeno.",
"accountUpdatedMessage": "Váš účet byl aktualizován.",
"accountPasswordUpdatedMessage": "Vaše heslo bylo aktualizováno.",
"missingIdentityProviderMessage": "Chybějící poskytovatel identity.",
"invalidFederatedIdentityActionMessage": "Neplatná nebo chybějící akce.",
"identityProviderNotFoundMessage": "Poskytovatel identity nenalezen.",
"federatedIdentityLinkNotActiveMessage": "Tato identita již není aktivní.",
"federatedIdentityRemovingLastProviderMessage": "Nemůžete odstranit poslední propojenou identitu, protože nemáte heslo.",
"identityProviderRedirectErrorMessage": "Nepodařilo se přesměrovat na poskytovatele identity.",
"identityProviderRemovedMessage": "Poskytovatel identity byl úspěšně odstraněn.",
"identityProviderAlreadyLinkedMessage": "Propojená identita vrácená uživatelem {0} je již propojena s jiným uživatelem.",
"staleCodeAccountMessage": "Platnost vypršela. Zkuste to ještě jednou.",
"consentDenied": "Souhlas byl zamítnut.",
"accountDisabledMessage": "Účet je zakázán, kontaktujte správce.",
"accountTemporarilyDisabledMessage": "Účet je dočasně zakázán, kontaktujte správce nebo zkuste to později.",
"invalidPasswordMinLengthMessage": "Neplatné heslo: musí obsahovat minimálně {0} malých znaků.",
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovat minimálně {0} malé znaky.",
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovat nejméně {0} číslic.",
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} velkých písmenen.",
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} speciálních znaků.",
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmí být totožné s uživatelským jménem.",
"invalidPasswordRegexPatternMessage": "Neplatné heslo: neshoduje se zadaným regulárním výrazem.",
"invalidPasswordHistoryMessage": "Neplatné heslo: Nesmí se opakovat žádné z posledních {0} hesel.",
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na černé listině.",
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo neodpovídá pravidlům hesla."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,156 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Speichern",
"doCancel": "Abbrechen",
"doLogOutAllSessions": "Alle Sitzungen abmelden",
"doRemove": "Entfernen",
"doAdd": "Hinzufügen",
"doSignOut": "Abmelden",
"editAccountHtmlTitle": "Benutzerkonto bearbeiten",
"federatedIdentitiesHtmlTitle": "Föderierte Identitäten",
"accountLogHtmlTitle": "Benutzerkonto Log",
"changePasswordHtmlTitle": "Passwort Ändern",
"sessionsHtmlTitle": "Sitzungen",
"accountManagementTitle": "Keycloak Benutzerkontoverwaltung",
"authenticatorTitle": "Mehrfachauthentifizierung",
"applicationsHtmlTitle": "Applikationen",
"authenticatorCode": "One-time Code",
"email": "E-Mail",
"firstName": "Vorname",
"givenName": "Vorname",
"fullName": "Voller Name",
"lastName": "Nachname",
"familyName": "Nachname",
"password": "Passwort",
"passwordConfirm": "Passwort bestätigen",
"passwordNew": "Neues Passwort",
"username": "Benutzername",
"address": "Adresse",
"street": "Straße",
"region": "Staat, Provinz, Region",
"postal_code": "PLZ",
"locality": "Stadt oder Ortschaft",
"country": "Land",
"emailVerified": "E-Mail verifiziert",
"gssDelegationCredential": "GSS delegierte Berechtigung",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Realm erstellen",
"role_view-realm": "Realm ansehen",
"role_view-users": "Benutzer ansehen",
"role_view-applications": "Applikationen ansehen",
"role_view-clients": "Clients ansehen",
"role_view-events": "Events ansehen",
"role_view-identity-providers": "Identity Provider ansehen",
"role_manage-realm": "Realm verwalten",
"role_manage-users": "Benutzer verwalten",
"role_manage-applications": "Applikationen verwalten",
"role_manage-identity-providers": "Identity Provider verwalten",
"role_manage-clients": "Clients verwalten",
"role_manage-events": "Events verwalten",
"role_view-profile": "Profile ansehen",
"role_manage-account": "Profile verwalten",
"role_manage-account-links": "Profil-Links verwalten",
"role_read-token": "Token lesen",
"role_offline-access": "Offline-Zugriff",
"role_uma_authorization": "Berechtigungen einholen",
"client_account": "Clientkonto",
"client_security-admin-console": "Security Adminkonsole",
"client_realm-management": "Realm-Management",
"client_broker": "Broker",
"requiredFields": "Erforderliche Felder",
"allFieldsRequired": "Alle Felder sind erforderlich",
"backToApplication": "&laquo; Zurück zur Applikation",
"backTo": "Zurück zu {0}",
"date": "Datum",
"event": "Ereignis",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Details",
"started": "Startdatum",
"lastAccess": "Letzter Zugriff",
"expires": "Ablaufdatum",
"applications": "Applikationen",
"account": "Benutzerkonto",
"federatedIdentity": "Föderierte Identität",
"authenticator": "Mehrfachauthentifizierung",
"sessions": "Sitzungen",
"log": "Log",
"application": "Applikation",
"availablePermissions": "verfügbare Berechtigungen",
"grantedPermissions": "gewährte Berechtigungen",
"grantedPersonalInfo": "gewährte persönliche Informationen",
"additionalGrants": "zusätzliche Berechtigungen",
"action": "Aktion",
"inResource": "in",
"fullAccess": "Vollzugriff",
"offlineToken": "Offline-Token",
"revoke": "Berechtigung widerrufen",
"configureAuthenticators": "Mehrfachauthentifizierung konfigurieren",
"mobile": "Mobil",
"totpStep1": "Installieren Sie eine der folgenden Applikationen auf Ihrem Smartphone:",
"totpStep2": "Öffnen Sie die Applikation und scannen Sie den Barcode.",
"totpStep3": "Geben Sie den von der Applikation generierten One-time Code ein und klicken Sie auf Speichern.",
"totpManualStep2": "Öffnen Sie die Applikation und geben Sie den folgenden Schlüssel ein.",
"totpManualStep3": "Verwenden Sie die folgenden Konfigurationswerte, falls Sie diese für die Applikation anpassen können:",
"totpUnableToScan": "Sie können den Barcode nicht scannen?",
"totpScanBarcode": "Barcode scannen?",
"totp.totp": "zeitbasiert (time-based)",
"totp.hotp": "zählerbasiert (counter-based)",
"totpType": "Typ",
"totpAlgorithm": "Algorithmus",
"totpDigits": "Ziffern",
"totpInterval": "Intervall",
"totpCounter": "Zähler",
"missingUsernameMessage": "Bitte geben Sie einen Benutzernamen ein.",
"missingFirstNameMessage": "Bitte geben Sie einen Vornamen ein.",
"invalidEmailMessage": "Ungültige E-Mail Adresse.",
"missingLastNameMessage": "Bitte geben Sie einen Nachnamen ein.",
"missingEmailMessage": "Bitte geben Sie eine E-Mail Adresse ein.",
"missingPasswordMessage": "Bitte geben Sie ein Passwort ein.",
"notMatchPasswordMessage": "Die Passwörter sind nicht identisch.",
"missingTotpMessage": "Bitte geben Sie den One-time Code ein.",
"invalidPasswordExistingMessage": "Das aktuelle Passwort ist ungültig.",
"invalidPasswordConfirmMessage": "Die Passwortbestätigung ist nicht identisch.",
"invalidTotpMessage": "Ungültiger One-time Code.",
"usernameExistsMessage": "Der Benutzername existiert bereits.",
"emailExistsMessage": "Die E-Mail-Adresse existiert bereits.",
"readOnlyUserMessage": "Sie können Ihr Benutzerkonto nicht ändern, da es schreibgeschützt ist.",
"readOnlyUsernameMessage": "Sie können Ihren Benutzernamen nicht ändern, da er schreibgeschützt ist.",
"readOnlyPasswordMessage": "Sie können Ihr Passwort nicht ändern, da es schreibgeschützt ist.",
"successTotpMessage": "Mehrfachauthentifizierung erfolgreich konfiguriert.",
"successTotpRemovedMessage": "Mehrfachauthentifizierung erfolgreich entfernt.",
"successGrantRevokedMessage": "Berechtigung erfolgreich widerrufen.",
"accountUpdatedMessage": "Ihr Benutzerkonto wurde aktualisiert.",
"accountPasswordUpdatedMessage": "Ihr Passwort wurde aktualisiert.",
"missingIdentityProviderMessage": "Identity Provider nicht angegeben.",
"invalidFederatedIdentityActionMessage": "Ungültige oder fehlende Aktion.",
"identityProviderNotFoundMessage": "Angegebener Identity Provider nicht gefunden.",
"federatedIdentityLinkNotActiveMessage": "Diese Identität ist nicht mehr aktiv.",
"federatedIdentityRemovingLastProviderMessage": "Sie können den letzten Eintrag nicht entfernen, da Sie kein Passwort haben.",
"identityProviderRedirectErrorMessage": "Fehler bei der Weiterleitung zum Identity Provider.",
"identityProviderRemovedMessage": "Identity Provider erfolgreich entfernt.",
"identityProviderAlreadyLinkedMessage": "Die föderierte Identität von {0} ist bereits einem anderen Benutzer zugewiesen.",
"staleCodeAccountMessage": "Diese Seite ist nicht mehr gültig, bitte versuchen Sie es noch einmal.",
"consentDenied": "Einverständnis verweigert.",
"accountDisabledMessage": "Ihr Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.",
"accountTemporarilyDisabledMessage":
"Ihr Benutzerkonto ist temporär gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es später noch einmal.",
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: Es muss mindestens {0} Zeichen lang sein.",
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Kleinbuchstaben beinhalten.",
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: Es muss mindestens {0} Zahl(en) beinhalten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Großbuchstaben beinhalten.",
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Sonderzeichen beinhalten.",
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: Es darf nicht gleich sein wie der Benutzername.",
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: Es entspricht nicht dem Regex-Muster.",
"invalidPasswordHistoryMessage": "Ungültiges Passwort: Es darf nicht einem der letzten {0} Passwörter entsprechen.",
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Das Passwort steht auf der Blocklist (schwarzen Liste).",
"invalidPasswordGenericMessge": "Ungültiges Passwort: Das neue Passwort verletzt die Passwort-Richtlinien."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,325 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Save",
"doCancel": "Cancel",
"doLogOutAllSessions": "Log out all sessions",
"doRemove": "Remove",
"doAdd": "Add",
"doSignOut": "Sign Out",
"doLogIn": "Log In",
"doLink": "Link",
"editAccountHtmlTitle": "Edit Account",
"personalInfoHtmlTitle": "Personal Info",
"federatedIdentitiesHtmlTitle": "Federated Identities",
"accountLogHtmlTitle": "Account Log",
"changePasswordHtmlTitle": "Change Password",
"deviceActivityHtmlTitle": "Device Activity",
"sessionsHtmlTitle": "Sessions",
"accountManagementTitle": "Keycloak Account Management",
"authenticatorTitle": "Authenticator",
"applicationsHtmlTitle": "Applications",
"linkedAccountsHtmlTitle": "Linked Accounts",
"accountManagementWelcomeMessage": "Welcome to Keycloak Account Management",
"personalInfoIntroMessage": "Manage your basic information",
"accountSecurityTitle": "Account Security",
"accountSecurityIntroMessage": "Control your password and account access",
"applicationsIntroMessage": "Track and manage your app permission to access your account",
"resourceIntroMessage": "Share your resources among team members",
"passwordLastUpdateMessage": "Your password was updated at",
"updatePasswordTitle": "Update Password",
"updatePasswordMessageTitle": "Make sure you choose a strong password",
"updatePasswordMessage":
"A strong password contains a mix of numbers, letters, and symbols. It is hard to guess, does not resemble a real word, and is only used for this account.",
"personalSubTitle": "Your Personal Info",
"personalSubMessage": "Manage this basic information: your first name, last name and email",
"authenticatorCode": "One-time code",
"email": "Email",
"firstName": "First name",
"givenName": "Given name",
"fullName": "Full name",
"lastName": "Last name",
"familyName": "Family name",
"password": "Password",
"currentPassword": "Current Password",
"passwordConfirm": "Confirmation",
"passwordNew": "New Password",
"username": "Username",
"address": "Address",
"street": "Street",
"locality": "City or Locality",
"region": "State, Province, or Region",
"postal_code": "Zip or Postal code",
"country": "Country",
"emailVerified": "Email verified",
"gssDelegationCredential": "GSS Delegation Credential",
"profileScopeConsentText": "User profile",
"emailScopeConsentText": "Email address",
"addressScopeConsentText": "Address",
"phoneScopeConsentText": "Phone number",
"offlineAccessScopeConsentText": "Offline Access",
"samlRoleListScopeConsentText": "My Roles",
"rolesScopeConsentText": "User roles",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Create realm",
"role_view-realm": "View realm",
"role_view-users": "View users",
"role_view-applications": "View applications",
"role_view-clients": "View clients",
"role_view-events": "View events",
"role_view-identity-providers": "View identity providers",
"role_view-consent": "View consents",
"role_manage-realm": "Manage realm",
"role_manage-users": "Manage users",
"role_manage-applications": "Manage applications",
"role_manage-identity-providers": "Manage identity providers",
"role_manage-clients": "Manage clients",
"role_manage-events": "Manage events",
"role_view-profile": "View profile",
"role_manage-account": "Manage account",
"role_manage-account-links": "Manage account links",
"role_manage-consent": "Manage consents",
"role_read-token": "Read token",
"role_offline-access": "Offline access",
"role_uma_authorization": "Obtain permissions",
"client_account": "Account",
"client_account-console": "Account Console",
"client_security-admin-console": "Security Admin Console",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Realm Management",
"client_broker": "Broker",
"requiredFields": "Required fields",
"allFieldsRequired": "All fields required",
"backToApplication": "&laquo; Back to application",
"backTo": "Back to {0}",
"date": "Date",
"event": "Event",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Details",
"started": "Started",
"lastAccess": "Last Access",
"expires": "Expires",
"applications": "Applications",
"account": "Account",
"federatedIdentity": "Federated Identity",
"authenticator": "Authenticator",
"device-activity": "Device Activity",
"sessions": "Sessions",
"log": "Log",
"application": "Application",
"availableRoles": "Available Roles",
"grantedPermissions": "Granted Permissions",
"grantedPersonalInfo": "Granted Personal Info",
"additionalGrants": "Additional Grants",
"action": "Action",
"inResource": "in",
"fullAccess": "Full Access",
"offlineToken": "Offline Token",
"revoke": "Revoke Grant",
"configureAuthenticators": "Configured Authenticators",
"mobile": "Mobile",
"totpStep1": "Install one of the following applications on your mobile:",
"totpStep2": "Open the application and scan the barcode:",
"totpStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
"totpStep3DeviceName": "Provide a Device Name to help you manage your OTP devices.",
"totpManualStep2": "Open the application and enter the key:",
"totpManualStep3": "Use the following configuration values if the application allows setting them:",
"totpUnableToScan": "Unable to scan?",
"totpScanBarcode": "Scan barcode?",
"totp.totp": "Time-based",
"totp.hotp": "Counter-based",
"totpType": "Type",
"totpAlgorithm": "Algorithm",
"totpDigits": "Digits",
"totpInterval": "Interval",
"totpCounter": "Counter",
"totpDeviceName": "Device Name",
"missingUsernameMessage": "Please specify username.",
"missingFirstNameMessage": "Please specify first name.",
"invalidEmailMessage": "Invalid email address.",
"missingLastNameMessage": "Please specify last name.",
"missingEmailMessage": "Please specify email.",
"missingPasswordMessage": "Please specify password.",
"notMatchPasswordMessage": "Passwords don't match.",
"invalidUserMessage": "Invalid user",
"missingTotpMessage": "Please specify authenticator code.",
"missingTotpDeviceNameMessage": "Please specify device name.",
"invalidPasswordExistingMessage": "Invalid existing password.",
"invalidPasswordConfirmMessage": "Password confirmation doesn't match.",
"invalidTotpMessage": "Invalid authenticator code.",
"usernameExistsMessage": "Username already exists.",
"emailExistsMessage": "Email already exists.",
"readOnlyUserMessage": "You can't update your account as it is read-only.",
"readOnlyUsernameMessage": "You can't update your username as it is read-only.",
"readOnlyPasswordMessage": "You can't update your password as your account is read-only.",
"successTotpMessage": "Mobile authenticator configured.",
"successTotpRemovedMessage": "Mobile authenticator removed.",
"successGrantRevokedMessage": "Grant revoked successfully.",
"accountUpdatedMessage": "Your account has been updated.",
"accountPasswordUpdatedMessage": "Your password has been updated.",
"missingIdentityProviderMessage": "Identity provider not specified.",
"invalidFederatedIdentityActionMessage": "Invalid or missing action.",
"identityProviderNotFoundMessage": "Specified identity provider not found.",
"federatedIdentityLinkNotActiveMessage": "This identity is not active anymore.",
"federatedIdentityRemovingLastProviderMessage": "You can't remove last federated identity as you don't have a password.",
"identityProviderRedirectErrorMessage": "Failed to redirect to identity provider.",
"identityProviderRemovedMessage": "Identity provider removed successfully.",
"identityProviderAlreadyLinkedMessage": "Federated identity returned by {0} is already linked to another user.",
"staleCodeAccountMessage": "The page expired. Please try one more time.",
"consentDenied": "Consent denied.",
"accountDisabledMessage": "Account is disabled, contact your administrator.",
"accountTemporarilyDisabledMessage": "Account is temporarily disabled, contact your administrator or try again later.",
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
"invalidPasswordGenericMessage": "Invalid password: new password doesn't match password policies.",
"myResources": "My Resources",
"myResourcesSub": "My resources",
"doDeny": "Deny",
"doRevoke": "Revoke",
"doApprove": "Approve",
"doRemoveSharing": "Remove Sharing",
"doRemoveRequest": "Remove Request",
"peopleAccessResource": "People with access to this resource",
"resourceManagedPolicies": "Permissions granting access to this resource",
"resourceNoPermissionsGrantingAccess": "No permissions granting access to this resource",
"anyAction": "Any action",
"description": "Description",
"name": "Name",
"scopes": "Scopes",
"resource": "Resource",
"user": "User",
"peopleSharingThisResource": "People sharing this resource",
"shareWithOthers": "Share with others",
"needMyApproval": "Need my approval",
"requestsWaitingApproval": "Your requests waiting approval",
"icon": "Icon",
"requestor": "Requestor",
"owner": "Owner",
"resourcesSharedWithMe": "Resources shared with me",
"permissionRequestion": "Permission Requestion",
"permission": "Permission",
"shares": "share(s)",
"notBeingShared": "This resource is not being shared.",
"notHaveAnyResource": "You don't have any resources",
"noResourcesSharedWithYou": "There are no resources shared with you",
"havePermissionRequestsWaitingForApproval": "You have {0} permission request(s) waiting for approval.",
"clickHereForDetails": "Click here for details.",
"resourceIsNotBeingShared": "The resource is not being shared",
"locale_ca": "Català",
"locale_cs": "Čeština",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_nl": "Nederlands",
"locale_no": "Norsk",
"locale_lt": "Lietuvių",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_sk": "Slovenčina",
"locale_sv": "Svenska",
"locale_tr": "Turkish",
"locale_zh-CN": "中文简体",
"applicaitonName": "Name",
"applicationType": "Application Type",
"applicationInUse": "In-use app only",
"clearAllFilter": "Clear all filters",
"activeFilters": "Active filters",
"filterByName": "Filter By Name ...",
"allApps": "All applications",
"internalApps": "Internal applications",
"thirdpartyApps": "Third-Party applications",
"appResults": "Results",
"clientNotFoundMessage": "Client not found.",
"authorizedProvider": "Authorized Provider",
"authorizedProviderMessage": "Authorized Providers linked with your account",
"identityProvider": "Identity Provider",
"identityProviderMessage": "To link your account with identity providers you have configured",
"socialLogin": "Social Login",
"userDefined": "User Defined",
"removeAccess": "Remove Access",
"removeAccessMessage": "You will need to grant access again, if you want to use this app account.",
"authenticatorStatusMessage": "Two-factor authentication is currently",
"authenticatorFinishSetUpTitle": "Your Two-Factor Authentication",
"authenticatorFinishSetUpMessage":
"Each time you sign in to your Keycloak account, you will be asked to provide a two-factor authentication code.",
"authenticatorSubTitle": "Set Up Two-Factor Authentication",
"authenticatorSubMessage": "To enhance the security of your account, enable at least one of the available two-factor authentication methods.",
"authenticatorMobileTitle": "Mobile Authenticator",
"authenticatorMobileMessage": "Use mobile Authenticator to get Verification codes as the two-factor authentication.",
"authenticatorMobileFinishSetUpMessage": "The authenticator has been bound to your phone.",
"authenticatorActionSetup": "Set up",
"authenticatorSMSTitle": "SMS Code",
"authenticatorSMSMessage": "Keycloak will send the Verification code to your phone as the two-factor authentication.",
"authenticatorSMSFinishSetUpMessage": "Text messages are sent to",
"authenticatorDefaultStatus": "Default",
"authenticatorChangePhone": "Change Phone Number",
"authenticatorBackupCodesTitle": "Backup Codes",
"authenticatorBackupCodesMessage": "Get your 8-digit backup codes",
"authenticatorBackupCodesFinishSetUpMessage": "12 backup codes were generated at this time. Each one can be used once.",
"authenticatorMobileSetupTitle": "Mobile Authenticator Setup",
"smscodeIntroMessage": "Enter your phone number and a verification code will be sent to your phone.",
"mobileSetupStep1": "Install an authenticator application on your phone. The applications listed here are supported.",
"mobileSetupStep2": "Open the application and scan the barcode:",
"mobileSetupStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
"scanBarCode": "Want to scan the barcode?",
"enterBarCode": "Enter the one-time code",
"doCopy": "Copy",
"doFinish": "Finish",
"authenticatorSMSCodeSetupTitle": "SMS Code Setup",
"chooseYourCountry": "Choose your country",
"enterYourPhoneNumber": "Enter your phone number",
"sendVerficationCode": "Send Verification Code",
"enterYourVerficationCode": "Enter your verification code",
"authenticatorBackupCodesSetupTitle": "Backup Codes Setup",
"backupcodesIntroMessage":
"If you lose access to your phone, you can still log into your account through backup codes. Keep them somewhere safe and accessible.",
"realmName": "Realm",
"doDownload": "Download",
"doPrint": "Print",
"backupCodesTips-1": "Each backup code can be used once.",
"backupCodesTips-2": "These codes were generated on",
"generateNewBackupCodes": "Generate New Backup Codes",
"backupCodesTips-3": "When you generate new backup codes, the current codes will not work anymore.",
"backtoAuthenticatorPage": "Back to Authenticator Page",
"resources": "Resources",
"sharedwithMe": "Shared with Me",
"share": "Share",
"sharedwith": "Shared with",
"accessPermissions": "Access Permissions",
"permissionRequests": "Permission Requests",
"approve": "Approve",
"approveAll": "Approve all",
"people": "people",
"perPage": "per page",
"currentPage": "Current Page",
"sharetheResource": "Share the resource",
"group": "Group",
"selectPermission": "Select Permission",
"addPeople": "Add people to share your resource with",
"addTeam": "Add team to share your resource with",
"myPermissions": "My Permissions",
"waitingforApproval": "Waiting for approval",
"anyPermission": "Any Permission",
"openshift.scope.user_info": "User information",
"openshift.scope.user_check-access": "User access information",
"openshift.scope.user_full": "Full Access",
"openshift.scope.list-projects": "List projects"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,137 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Guardar",
"doCancel": "Cancelar",
"doLogOutAllSessions": "Desconectar de todas las sesiones",
"doRemove": "Eliminar",
"doAdd": "Añadir",
"doSignOut": "Desconectar",
"editAccountHtmlTitle": "Editar cuenta",
"federatedIdentitiesHtmlTitle": "Identidades federadas",
"accountLogHtmlTitle": "Registro de la cuenta",
"changePasswordHtmlTitle": "Cambiar contraseña",
"sessionsHtmlTitle": "Sesiones",
"accountManagementTitle": "Gestión de Cuenta Keycloak",
"authenticatorTitle": "Autenticador",
"applicationsHtmlTitle": "Aplicaciones",
"authenticatorCode": "Código de un solo uso",
"email": "Email",
"firstName": "Nombre",
"givenName": "Nombre de pila",
"fullName": "Nombre completo",
"lastName": "Apellidos",
"familyName": "Apellido",
"password": "Contraseña",
"passwordConfirm": "Confirma la contraseña",
"passwordNew": "Nueva contraseña",
"username": "Usuario",
"address": "Dirección",
"street": "Calle",
"locality": "Ciudad o Municipio",
"region": "Estado, Provincia, o Región",
"postal_code": "Código Postal",
"country": "País",
"emailVerified": "Email verificado",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "Administrador",
"role_realm-admin": "Administrador del dominio",
"role_create-realm": "Crear dominio",
"role_view-realm": "Ver dominio",
"role_view-users": "Ver usuarios",
"role_view-applications": "Ver aplicaciones",
"role_view-clients": "Ver clientes",
"role_view-events": "Ver eventos",
"role_view-identity-providers": "Ver proveedores de identidad",
"role_manage-realm": "Gestionar dominio",
"role_manage-users": "Gestionar usuarios",
"role_manage-applications": "Gestionar aplicaciones",
"role_manage-identity-providers": "Gestionar proveedores de identidad",
"role_manage-clients": "Gestionar clientes",
"role_manage-events": "Gestionar eventos",
"role_view-profile": "Ver perfil",
"role_manage-account": "Gestionar cuenta",
"role_read-token": "Leer token",
"role_offline-access": "Acceso sin conexión",
"client_account": "Cuenta",
"client_security-admin-console": "Consola de Administración de Seguridad",
"client_realm-management": "Gestión de dominio",
"client_broker": "Broker",
"requiredFields": "Campos obligatorios",
"allFieldsRequired": "Todos los campos obligatorios",
"backToApplication": "&laquo; Volver a la aplicación",
"backTo": "Volver a {0}",
"date": "Fecha",
"event": "Evento",
"ip": "IP",
"client": "Cliente",
"clients": "Clientes",
"details": "Detalles",
"started": "Iniciado",
"lastAccess": "Último acceso",
"expires": "Expira",
"applications": "Aplicaciones",
"account": "Cuenta",
"federatedIdentity": "Identidad federada",
"authenticator": "Autenticador",
"sessions": "Sesiones",
"log": "Regisro",
"application": "Aplicación",
"availablePermissions": "Permisos disponibles",
"grantedPermissions": "Permisos concedidos",
"grantedPersonalInfo": "Información personal concedida",
"additionalGrants": "Permisos adicionales",
"action": "Acción",
"inResource": "en",
"fullAccess": "Acceso total",
"offlineToken": "Código de autorización offline",
"revoke": "Revocar permiso",
"configureAuthenticators": "Autenticadores configurados",
"mobile": "Móvil",
"totpStep1":
'Instala <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator en tu teléfono móvil. Ambas aplicaciones están disponibles en <a href="https://play.google.com">Google Play</a> y en la App Store de Apple.',
"totpStep2": "Abre la aplicación y escanea el código o introduce la clave.",
"totpStep3": "Introduce el código único que te muestra la aplicación de autenticación y haz clic en Enviar para finalizar la configuración",
"missingUsernameMessage": "Por favor indica tu usuario.",
"missingFirstNameMessage": "Por favor indica el nombre.",
"invalidEmailMessage": "Email no válido",
"missingLastNameMessage": "Por favor indica tus apellidos.",
"missingEmailMessage": "Por favor indica el email.",
"missingPasswordMessage": "Por favor indica tu contraseña.",
"notMatchPasswordMessage": "Las contraseñas no coinciden.",
"missingTotpMessage": "Por favor indica tu código de autenticación",
"invalidPasswordExistingMessage": "La contraseña actual no es correcta.",
"invalidPasswordConfirmMessage": "La confirmación de contraseña no coincide.",
"invalidTotpMessage": "El código de autenticación no es válido.",
"usernameExistsMessage": "El usuario ya existe",
"emailExistsMessage": "El email ya existe",
"readOnlyUserMessage": "No puedes actualizar tu usuario porque tu cuenta es de solo lectura.",
"readOnlyPasswordMessage": "No puedes actualizar tu contraseña porque tu cuenta es de solo lectura.",
"successTotpMessage": "Aplicación de autenticación móvil configurada.",
"successTotpRemovedMessage": "Aplicación de autenticación móvil eliminada.",
"successGrantRevokedMessage": "Permiso revocado correctamente",
"accountUpdatedMessage": "Tu cuenta se ha actualizado.",
"accountPasswordUpdatedMessage": "Tu contraseña se ha actualizado.",
"missingIdentityProviderMessage": "Proveedor de identidad no indicado.",
"invalidFederatedIdentityActionMessage": "Acción no válida o no indicada.",
"identityProviderNotFoundMessage": "No se encontró un proveedor de identidad.",
"federatedIdentityLinkNotActiveMessage": "Esta identidad ya no está activa",
"federatedIdentityRemovingLastProviderMessage": "No puedes eliminar la última identidad federada porque no tienes fijada una contraseña.",
"identityProviderRedirectErrorMessage": "Error en la redirección al proveedor de identidad",
"identityProviderRemovedMessage": "Proveedor de identidad borrado correctamente.",
"accountDisabledMessage": "La cuenta está desactivada, contacta con el administrador.",
"accountTemporarilyDisabledMessage": "La cuenta está temporalmente desactivada, contacta con el administrador o inténtalo de nuevo más tarde.",
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,150 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Sauvegarder",
"doCancel": "Annuler",
"doLogOutAllSessions": "Déconnexion de toutes les sessions",
"doRemove": "Supprimer",
"doAdd": "Ajouter",
"doSignOut": "Déconnexion",
"editAccountHtmlTitle": "Édition du compte",
"federatedIdentitiesHtmlTitle": "Identités fédérées",
"accountLogHtmlTitle": "Accès au compte",
"changePasswordHtmlTitle": "Changer de mot de passe",
"sessionsHtmlTitle": "Sessions",
"accountManagementTitle": "Gestion du compte Keycloak",
"authenticatorTitle": "Authentification",
"applicationsHtmlTitle": "Applications",
"authenticatorCode": "Mot de passe unique",
"email": "Courriel",
"firstName": "Prénom",
"givenName": "Prénom",
"fullName": "Nom complet",
"lastName": "Nom",
"familyName": "Nom de famille",
"password": "Mot de passe",
"passwordConfirm": "Confirmation",
"passwordNew": "Nouveau mot de passe",
"username": "Compte",
"address": "Adresse",
"street": "Rue",
"locality": "Ville ou Localité",
"region": "État, Province ou Région",
"postal_code": "Code Postal",
"country": "Pays",
"emailVerified": "Courriel vérifié",
"gssDelegationCredential": "Accréditation de délégation GSS",
"role_admin": "Administrateur",
"role_realm-admin": "Administrateur du domaine",
"role_create-realm": "Créer un domaine",
"role_view-realm": "Voir un domaine",
"role_view-users": "Voir les utilisateurs",
"role_view-applications": "Voir les applications",
"role_view-clients": "Voir les clients",
"role_view-events": "Voir les événements",
"role_view-identity-providers": "Voir les fournisseurs d'identités",
"role_manage-realm": "Gérer le domaine",
"role_manage-users": "Gérer les utilisateurs",
"role_manage-applications": "Gérer les applications",
"role_manage-identity-providers": "Gérer les fournisseurs d'identités",
"role_manage-clients": "Gérer les clients",
"role_manage-events": "Gérer les événements",
"role_view-profile": "Voir le profil",
"role_manage-account": "Gérer le compte",
"role_read-token": "Lire le jeton d'authentification",
"role_offline-access": "Accès hors-ligne",
"client_account": "Compte",
"client_security-admin-console": "Console d'administration de la sécurité",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Gestion du domaine",
"client_broker": "Broker",
"requiredFields": "Champs obligatoires",
"allFieldsRequired": "Tous les champs sont obligatoires",
"backToApplication": "&laquo; Revenir à l'application",
"backTo": "Revenir à {0}",
"date": "Date",
"event": "Evénement",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Détails",
"started": "Début",
"lastAccess": "Dernier accès",
"expires": "Expiration",
"applications": "Applications",
"account": "Compte",
"federatedIdentity": "Identité fédérée",
"authenticator": "Authentification",
"sessions": "Sessions",
"log": "Connexion",
"application": "Application",
"availablePermissions": "Permissions disponibles",
"grantedPermissions": "Permissions accordées",
"grantedPersonalInfo": "Informations personnelles accordées",
"additionalGrants": "Droits additionnels",
"action": "Action",
"inResource": "dans",
"fullAccess": "Accès complet",
"offlineToken": "Jeton d'authentification hors-ligne",
"revoke": "Révoquer un droit",
"configureAuthenticators": "Authentifications configurées.",
"mobile": "Téléphone mobile",
"totpStep1": "Installez une des applications suivantes sur votre mobile",
"totpStep2": "Ouvrez l'application et scannez le code-barres ou entrez la clef.",
"totpStep3": "Entrez le code à usage unique fourni par l'application et cliquez sur Sauvegarder pour terminer.",
"totpManualStep2": "Ouvrez l'application et entrez la clef",
"totpManualStep3": "Utilisez les valeurs de configuration suivante si l'application les autorise",
"totpUnableToScan": "Impossible de scanner ?",
"totpScanBarcode": "Scanner le code-barres ?",
"totp.totp": "Basé sur le temps",
"totp.hotp": "Basé sur un compteur",
"totpType": "Type",
"totpAlgorithm": "Algorithme",
"totpDigits": "Chiffres",
"totpInterval": "Intervalle",
"totpCounter": "Compteur",
"missingUsernameMessage": "Veuillez entrer votre nom d'utilisateur.",
"missingFirstNameMessage": "Veuillez entrer votre prénom.",
"invalidEmailMessage": "Courriel invalide.",
"missingLastNameMessage": "Veuillez entrer votre nom.",
"missingEmailMessage": "Veuillez entrer votre courriel.",
"missingPasswordMessage": "Veuillez entrer votre mot de passe.",
"notMatchPasswordMessage": "Les mots de passe ne sont pas identiques",
"missingTotpMessage": "Veuillez entrer le code d'authentification.",
"invalidPasswordExistingMessage": "Mot de passe existant invalide.",
"invalidPasswordConfirmMessage": "Le mot de passe de confirmation ne correspond pas.",
"invalidTotpMessage": "Le code d'authentification est invalide.",
"usernameExistsMessage": "Le nom d'utilisateur existe déjà.",
"emailExistsMessage": "Le courriel existe déjà.",
"readOnlyUserMessage": "Vous ne pouvez pas mettre à jour votre compte car il est en lecture seule.",
"readOnlyPasswordMessage": "Vous ne pouvez pas mettre à jour votre mot de passe car votre compte est en lecture seule.",
"successTotpMessage": "L'authentification via téléphone mobile est configurée.",
"successTotpRemovedMessage": "L'authentification via téléphone mobile est supprimée.",
"successGrantRevokedMessage": "Droit révoqué avec succès.",
"accountUpdatedMessage": "Votre compte a été mis à jour.",
"accountPasswordUpdatedMessage": "Votre mot de passe a été mis à jour.",
"missingIdentityProviderMessage": "Le fournisseur d'identité n'est pas spécifié.",
"invalidFederatedIdentityActionMessage": "Action manquante ou invalide.",
"identityProviderNotFoundMessage": "Le fournisseur d'identité spécifié n'est pas trouvé.",
"federatedIdentityLinkNotActiveMessage": "Cette identité n'est plus active dorénavant.",
"federatedIdentityRemovingLastProviderMessage":
"Vous ne pouvez pas supprimer votre dernière fédération d'identité sans avoir de mot de passe spécifié.",
"identityProviderRedirectErrorMessage": "Erreur de redirection vers le fournisseur d'identité.",
"identityProviderRemovedMessage": "Le fournisseur d'identité a été supprimé correctement.",
"identityProviderAlreadyLinkedMessage": "Le fournisseur d'identité retourné par {0} est déjà lié à un autre utilisateur.",
"accountDisabledMessage": "Ce compte est désactivé, veuillez contacter votre administrateur.",
"accountTemporarilyDisabledMessage": "Ce compte est temporairement désactivé, veuillez contacter votre administrateur ou réessayez plus tard.",
"invalidPasswordMinLengthMessage": "Mot de passe invalide: longueur minimale {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.",
"invalidPasswordMinDigitsMessage": "Mot de passe invalide: doit contenir au moins {0} chiffre(s).",
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.",
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide: doit contenir au moins {0} caractère(s) spéciaux.",
"invalidPasswordNotUsernameMessage": "Mot de passe invalide: ne doit pas être identique au nom d'utilisateur.",
"invalidPasswordRegexPatternMessage": "Mot de passe invalide: ne valide pas l'expression rationnelle.",
"invalidPasswordHistoryMessage": "Mot de passe invalide: ne doit pas être égal aux {0} derniers mots de passe."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,310 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Salva",
"doCancel": "Annulla",
"doLogOutAllSessions": "Effettua il logout da tutte le sessioni",
"doRemove": "Elimina",
"doAdd": "Aggiungi",
"doSignOut": "Esci",
"doLogIn": "Log In",
"doLink": "Link",
"editAccountHtmlTitle": "Modifica Account",
"personalInfoHtmlTitle": "Informazioni personali",
"federatedIdentitiesHtmlTitle": "Identità federate",
"accountLogHtmlTitle": "Log dell'account",
"changePasswordHtmlTitle": "Cambia password",
"deviceActivityHtmlTitle": "Attività dei dispositivi",
"sessionsHtmlTitle": "Sessioni",
"accountManagementTitle": "Gestione degli account di Keycloak",
"authenticatorTitle": "Autenticatore",
"applicationsHtmlTitle": "Applicazioni",
"linkedAccountsHtmlTitle": "Account collegati",
"accountManagementWelcomeMessage": "Benvenuto nella gestione degli account di Keycloak",
"personalInfoIntroMessage": "Gestisci le tue informazioni di base",
"accountSecurityTitle": "Sicurezza dell'account",
"accountSecurityIntroMessage": "Controlla la tua password e gli accessi dell'account",
"applicationsIntroMessage": "Traccia e gestisci i permessi delle applicazioni nell'accesso al tuo account",
"resourceIntroMessage": "Condividi le tue risorse tra i membri del team",
"passwordLastUpdateMessage": "La tua password è stata aggiornata il",
"updatePasswordTitle": "Aggiornamento password",
"updatePasswordMessageTitle": "Assicurati di scegliere una password robusta",
"updatePasswordMessage":
"Una password robusta contiene un misto di numeri, lettere, e simboli. È difficile da indovinare, non assomiglia a una parola reale, ed è utilizzata solo per questo account.",
"personalSubTitle": "Le tue informazioni personali",
"personalSubMessage": "Gestisce queste informazioni di base: il tuo nome, cognome, e indirizzo email",
"authenticatorCode": "Codice monouso",
"email": "Email",
"firstName": "Nome",
"givenName": "Nome",
"fullName": "Nome completo",
"lastName": "Cognome",
"familyName": "Cognome",
"password": "Password",
"currentPassword": "Password attuale",
"passwordConfirm": "Conferma password",
"passwordNew": "Nuova password",
"username": "Username",
"address": "Indirizzo",
"street": "Via",
"locality": "Città o località",
"region": "Stato, Provincia, o Regione",
"postal_code": "CAP",
"country": "Paese",
"emailVerified": "Email verificata",
"gssDelegationCredential": "Credenziali delega GSS",
"profileScopeConsentText": "Profilo utente",
"emailScopeConsentText": "Indirizzo email",
"addressScopeConsentText": "Indirizzo",
"phoneScopeConsentText": "Numero di telefono",
"offlineAccessScopeConsentText": "Accesso offline",
"samlRoleListScopeConsentText": "I miei ruoli",
"rolesScopeConsentText": "Ruoli utente",
"role_admin": "Admin",
"role_realm-admin": "Realm admin",
"role_create-realm": "Crea realm",
"role_view-realm": "Visualizza realm",
"role_view-users": "Visualizza utenti",
"role_view-applications": "Visualizza applicazioni",
"role_view-clients": "Visualizza client",
"role_view-events": "Visualizza eventi",
"role_view-identity-providers": "Visualizza identity provider",
"role_view-consent": "Visualizza consensi",
"role_manage-realm": "Gestisci realm",
"role_manage-users": "Gestisci utenti",
"role_manage-applications": "Gestisci applicazioni",
"role_manage-identity-providers": "Gestisci identity provider",
"role_manage-clients": "Gestisci client",
"role_manage-events": "Gestisci eventi",
"role_view-profile": "Visualizza profilo",
"role_manage-account": "Gestisci account",
"role_manage-account-links": "Gestisci i link dell'account",
"role_manage-consent": "Gestisci consensi",
"role_read-token": "Leggi token",
"role_offline-access": "Accesso offline",
"role_uma_authorization": "Ottieni permessi",
"client_account": "Account",
"client_account-console": "Console account",
"client_security-admin-console": "Console di amministrazione di sicurezza",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Gestione realm",
"client_broker": "Broker",
"requiredFields": "Campi obbligatori",
"allFieldsRequired": "Tutti campi obbligatori",
"backToApplication": "&laquo; Torna all'applicazione",
"backTo": "Torna a {0}",
"date": "Data",
"event": "Evento",
"ip": "IP",
"client": "Client",
"clients": "Client",
"details": "Dettagli",
"started": "Iniziato",
"lastAccess": "Ultimo accesso",
"expires": "Scade",
"applications": "Applicazioni",
"account": "Account",
"federatedIdentity": "Identità federate",
"authenticator": "Autenticatore",
"device-activity": "Attività dei dispositivi",
"sessions": "Sessioni",
"log": "Log",
"application": "Applicazione",
"availablePermissions": "Autorizzazioni disponibili",
"grantedPermissions": "Autorizzazioni concesse",
"grantedPersonalInfo": "Informazioni personali concesse",
"additionalGrants": "Ulteriori concessioni",
"action": "Azione",
"inResource": "in",
"fullAccess": "Accesso completo",
"offlineToken": "Token offline",
"revoke": "Revoca concessione",
"configureAuthenticators": "Autenticatori configurati",
"mobile": "Dispositivo mobile",
"totpStep1": "Installa una delle seguenti applicazioni sul tuo dispositivo mobile",
"totpStep2": "Apri l'applicazione e scansiona il codice QR",
"totpStep3": "Scrivi il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
"totpStep3DeviceName": "Fornisci il nome del dispositivo per aiutarti a gestire i dispositivi di autenticazione.",
"totpManualStep2": "Apri l'applicazione e scrivi la chiave",
"totpManualStep3": "Usa le seguenti impostazioni se l'applicazione lo consente",
"totpUnableToScan": "Non riesci a scansionare il codice QR?",
"totpScanBarcode": "Vuoi scansionare il codice QR?",
"totp.totp": "Basato sull'ora",
"totp.hotp": "Basato sul contatore",
"totpType": "Tipo",
"totpAlgorithm": "Algoritmo",
"totpDigits": "Cifre",
"totpInterval": "Intervallo",
"totpCounter": "Contatore",
"totpDeviceName": "Nome dispositivo",
"missingUsernameMessage": "Inserisci lo username.",
"missingFirstNameMessage": "Inserisci il nome.",
"invalidEmailMessage": "Indirizzo email non valido.",
"missingLastNameMessage": "Inserisci il cognome.",
"missingEmailMessage": "Inserisci l'indirizzo email.",
"missingPasswordMessage": "Inserisci la password.",
"notMatchPasswordMessage": "Le password non coincidono.",
"invalidUserMessage": "Utente non valido",
"missingTotpMessage": "Inserisci il codice di autenticazione.",
"missingTotpDeviceNameMessage": "Inserisci il nome del dispositivo di autenticazione.",
"invalidPasswordExistingMessage": "Password esistente non valida.",
"invalidPasswordConfirmMessage": "La password di conferma non coincide.",
"invalidTotpMessage": "Codice di autenticazione non valido.",
"usernameExistsMessage": "Username già esistente.",
"emailExistsMessage": "Email già esistente.",
"readOnlyUserMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
"readOnlyUsernameMessage": "Non puoi aggiornare il tuo nome utente poiché è in modalità sola lettura.",
"readOnlyPasswordMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
"successTotpMessage": "Autenticatore mobile configurato.",
"successTotpRemovedMessage": "Autenticatore mobile eliminato.",
"successGrantRevokedMessage": "Concessione revocata con successo.",
"accountUpdatedMessage": "Il tuo account è stato aggiornato.",
"accountPasswordUpdatedMessage": "La tua password è stata aggiornata.",
"missingIdentityProviderMessage": "Identity provider non specificato.",
"invalidFederatedIdentityActionMessage": "Azione non valida o mancante.",
"identityProviderNotFoundMessage": "L'identity provider specificato non è stato trovato.",
"federatedIdentityLinkNotActiveMessage": "Questo identity non è più attivo.",
"federatedIdentityRemovingLastProviderMessage": "Non puoi rimuovere l'ultima identità federata poiché non hai più la password.",
"identityProviderRedirectErrorMessage": "Il reindirizzamento all'identity provider è fallito.",
"identityProviderRemovedMessage": "Identity provider eliminato correttamente.",
"identityProviderAlreadyLinkedMessage": "L'identità federata restituita da {0} è già collegata ad un altro utente.",
"staleCodeAccountMessage": "La pagina è scaduta. Prova di nuovo.",
"consentDenied": "Consenso negato.",
"accountDisabledMessage": "Account disabilitato, contatta l'amministratore.",
"accountTemporarilyDisabledMessage": "L'account è temporaneamente disabilitato, contatta l'amministratore o riprova più tardi.",
"invalidPasswordMinLengthMessage": "Password non valida: lunghezza minima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri minuscoli.",
"invalidPasswordMinDigitsMessage": "Password non valida: deve contenere almeno {0} numeri.",
"invalidPasswordMinUpperCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri maiuscoli.",
"invalidPasswordMinSpecialCharsMessage": "Password non valida: deve contenere almeno {0} caratteri speciali.",
"invalidPasswordNotUsernameMessage": "Password non valida: non deve essere uguale allo username.",
"invalidPasswordRegexPatternMessage": "Password non valida: fallito il match con una o più espressioni regolari.",
"invalidPasswordHistoryMessage": "Password non valida: non deve essere uguale a una delle ultime {0} password.",
"invalidPasswordBlacklistedMessage": "Password non valida: la password non è consentita.",
"invalidPasswordGenericMessage": "Password non valida: la nuova password non rispetta le indicazioni previste.",
"myResources": "Le mie risorse",
"myResourcesSub": "Le mie risorse",
"doDeny": "Nega",
"doRevoke": "Revoca",
"doApprove": "Approva",
"doRemoveSharing": "Rimuovi condivisione",
"doRemoveRequest": "Rimuovi richiesta",
"peopleAccessResource": "Persone che hanno accesso a questa risorsa",
"resourceManagedPolicies": "Permessi che danno accesso a questa risorsa",
"resourceNoPermissionsGrantingAccess": "Nessun permesso dà accesso a questa risorsa",
"anyAction": "Qualsiasi azione",
"description": "Descrizione",
"name": "Nome",
"scopes": "Ambito",
"resource": "Risorsa",
"user": "Utente",
"peopleSharingThisResource": "Persone che condividono questa risorsa",
"shareWithOthers": "Condividi con altri",
"needMyApproval": "Richiede la mia approvazione",
"requestsWaitingApproval": "La tua richiesta è in attesa di approvazione",
"icon": "Icona",
"requestor": "Richiedente",
"owner": "Proprietario",
"resourcesSharedWithMe": "Risorse condivise con me",
"permissionRequestion": "Richiesta di permesso",
"permission": "Permesso",
"shares": "condivisioni",
"notBeingShared": "Questa risorsa non è in condivisione.",
"notHaveAnyResource": "Non hai nessuna risorsa",
"noResourcesSharedWithYou": "Non ci sono risorse condivise con te",
"havePermissionRequestsWaitingForApproval": "Hai {0} richiesta(e) di permesso in attesa di approvazione.",
"clickHereForDetails": "Clicca qui per i dettagli.",
"resourceIsNotBeingShared": "La risorsa non è in condivisione",
"locale_it": "Italiano",
"applicaitonName": "Nome",
"applicationType": "Tipo applicazione",
"applicationInUse": "In-use app only",
"clearAllFilter": "Azzera tutti i filtri",
"activeFilters": "Filtri attivi",
"filterByName": "Filtra per nome ...",
"allApps": "Tutte le applicazioni",
"internalApps": "Applicazioni interne",
"thirdpartyApps": "Applicazioni di terze parti",
"appResults": "Risultati",
"clientNotFoundMessage": "Client non trovato.",
"authorizedProvider": "Provider autorizzato",
"authorizedProviderMessage": "Provider autorizzati collegati al tuo account",
"identityProvider": "Identity provider",
"identityProviderMessage": "Collegare il tuo account con gli identity provider che hai configurato",
"socialLogin": "Social Login",
"userDefined": "Definito dall'utente",
"removeAccess": "Rimuovi accesso",
"removeAccessMessage": "Devi concedere di nuovo l'accesso, se vuoi utilizzare l'account di questa applicazione.",
"authenticatorStatusMessage": "L'autenticazione a due fattori è attualmente",
"authenticatorFinishSetUpTitle": "La tua autenticazione a due fattori",
"authenticatorFinishSetUpMessage":
"Ogni volta che effettui l'accesso al tuo account Keycloak, ti verrà richiesto di fornire il tuo codice di autenticazione a due fattori.",
"authenticatorSubTitle": "Imposta l'autenticazione a due fattori",
"authenticatorSubMessage":
"Per incrementare la sicurezza del tuo account, attiva almeno uno dei metodi disponibili per l'autenticazione a due fattori.",
"authenticatorMobileTitle": "Autenticatore mobile",
"authenticatorMobileMessage": "Utilizza l'autenticatore mobile per ottenere i codici di verifica per l'autenticazione a due fattori.",
"authenticatorMobileFinishSetUpMessage": "L'autenticatore è stato collegato al tuo telefono.",
"authenticatorActionSetup": "Set up",
"authenticatorSMSTitle": "Codice SMS",
"authenticatorSMSMessage": "Keycloak invierà il codice di verifica al tuo telefono per l'autenticazione a due fattori.",
"authenticatorSMSFinishSetUpMessage": "I messaggi di testo vengono inviati a",
"authenticatorDefaultStatus": "Default",
"authenticatorChangePhone": "Cambia numero di telefono",
"authenticatorBackupCodesTitle": "Codici di backup",
"authenticatorBackupCodesMessage": "Ottieni i tuoi codici di backup a otto cifre",
"authenticatorBackupCodesFinishSetUpMessage": "Sono stati generati dodici codici di backup. Ognuno può essere usato una sola volta.",
"authenticatorMobileSetupTitle": "Setup autenticatore mobile",
"smscodeIntroMessage": "Inserisci il tuo numero di telefono e ti verrà inviato un codice di verifica.",
"mobileSetupStep1": "Installa un'applicazione di autenticazione sul tuo telefono. Sono supportate le applicazioni qui elencate.",
"mobileSetupStep2": "Apri l'applicazione e scansiona il codice QR:",
"mobileSetupStep3": "Inserisci il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
"scanBarCode": "Vuoi scansionare il codice QR?",
"enterBarCode": "Inserisci il codice monouso",
"doCopy": "Copia",
"doFinish": "Termina",
"authenticatorSMSCodeSetupTitle": "Setup codice SMS",
"chooseYourCountry": "Scegli la tua nazione",
"enterYourPhoneNumber": "Inserisci il tuo numero di telefono",
"sendVerficationCode": "Invia il codice di verifica",
"enterYourVerficationCode": "Inserisci il codice di verifica",
"authenticatorBackupCodesSetupTitle": "Setup backup codici",
"backupcodesIntroMessage":
"Se non disponi più del tuo telefono, puoi comunque accedere al tuo account attraverso i codici di backup. Conservali in un posto sicuro e accessibile.",
"realmName": "Realm",
"doDownload": "Download",
"doPrint": "Stampa",
"backupCodesTips-1": "Ogni codice di backup può essere usato una sola volta.",
"backupCodesTips-2": "Questi codici sono stati generati il",
"generateNewBackupCodes": "Genera dei nuovi codici di backup",
"backupCodesTips-3": "Quando generi dei nuovi codici di backup, quelli attuali non funzioneranno più.",
"backtoAuthenticatorPage": "Torna alla pagina dell'autenticatore",
"resources": "Risorse",
"sharedwithMe": "Condiviso con me",
"share": "Condiviso",
"sharedwith": "Condiviso con",
"accessPermissions": "Permessi di accesso",
"permissionRequests": "Richieste di permesso",
"approve": "Approva",
"approveAll": "Approva tutti",
"people": "persone",
"perPage": "per pagina",
"currentPage": "Pagina corrente",
"sharetheResource": "Condividi la risorsa",
"group": "Gruppo",
"selectPermission": "Seleziona permessi",
"addPeople": "Aggiungi persone con le quali condividere la tua risorsa",
"addTeam": "Aggiungi gruppi con i quali condividere la tua risorsa",
"myPermissions": "Miei permessi",
"waitingforApproval": "Attesa dell'approvazione",
"anyPermission": "Qualsiasi permesso",
"openshift.scope.user_info": "Informazioni utente",
"openshift.scope.user_check-access": "Informazioni per l'accesso dell'utente",
"openshift.scope.user_full": "Accesso completo",
"openshift.scope.list-projects": "Elenca progetti"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,324 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "保存",
"doCancel": "キャンセル",
"doLogOutAllSessions": "全セッションからログアウト",
"doRemove": "削除",
"doAdd": "追加",
"doSignOut": "サインアウト",
"doLogIn": "ログイン",
"doLink": "リンク",
"editAccountHtmlTitle": "アカウントの編集",
"personalInfoHtmlTitle": "個人情報",
"federatedIdentitiesHtmlTitle": "連携済みアイデンティティー",
"accountLogHtmlTitle": "アカウントログ",
"changePasswordHtmlTitle": "パスワード変更",
"deviceActivityHtmlTitle": "デバイス・アクティビティー",
"sessionsHtmlTitle": "セッション",
"accountManagementTitle": "Keycloakアカウント管理",
"authenticatorTitle": "オーセンティケーター",
"applicationsHtmlTitle": "アプリケーション",
"linkedAccountsHtmlTitle": "リンクされたアカウント",
"accountManagementWelcomeMessage": "Keycloakアカウント管理へようこそ",
"personalInfoIntroMessage": "基本情報を管理する",
"accountSecurityTitle": "アカウント・セキュリティー",
"accountSecurityIntroMessage": "パスワードとアカウント・アクセスを制御する",
"applicationsIntroMessage": "アカウントへアクセスするためにアプリのパーミッションを追跡して管理する",
"resourceIntroMessage": "チームメンバー間でリソースを共有する",
"passwordLastUpdateMessage": "パスワードは更新されました",
"updatePasswordTitle": "パスワードの更新",
"updatePasswordMessageTitle": "強力なパスワードを選択してください",
"updatePasswordMessage":
"強力なパスワードは、数字、文字、記号を含みます。推測が難しく、実在する言葉に似ておらず、このアカウントだけで使用されています。",
"personalSubTitle": "個人情報",
"personalSubMessage": "この基本情報を管理してください:名、姓、メール",
"authenticatorCode": "ワンタイムコード",
"email": "Eメール",
"firstName": "名",
"givenName": "名",
"fullName": "氏名",
"lastName": "姓",
"familyName": "姓",
"password": "パスワード",
"currentPassword": "現在のパスワード",
"passwordConfirm": "新しいパスワード(確認)",
"passwordNew": "新しいパスワード",
"username": "ユーザー名",
"address": "住所",
"street": "番地",
"locality": "市区町村",
"region": "都道府県",
"postal_code": "郵便番号",
"country": "国",
"emailVerified": "確認済みEメール",
"gssDelegationCredential": "GSS委譲クレデンシャル",
"profileScopeConsentText": "ユーザー・プロファイル",
"emailScopeConsentText": "メールアドレス",
"addressScopeConsentText": "アドレス",
"phoneScopeConsentText": "電話番号",
"offlineAccessScopeConsentText": "オフライン・アクセス",
"samlRoleListScopeConsentText": "ロール",
"rolesScopeConsentText": "ユーザーロール",
"role_admin": "管理者",
"role_realm-admin": "レルム管理者",
"role_create-realm": "レルムの作成",
"role_view-realm": "レルムの参照",
"role_view-users": "ユーザーの参照",
"role_view-applications": "アプリケーションの参照",
"role_view-clients": "クライアントの参照",
"role_view-events": "イベントの参照",
"role_view-identity-providers": "アイデンティティー・プロバイダーの参照",
"role_view-consent": "同意の参照",
"role_manage-realm": "レルムの管理",
"role_manage-users": "ユーザーの管理",
"role_manage-applications": "アプリケーションの管理",
"role_manage-identity-providers": "アイデンティティー・プロバイダーの管理",
"role_manage-clients": "クライアントの管理",
"role_manage-events": "イベントの管理",
"role_view-profile": "プロファイルの参照",
"role_manage-account": "アカウントの管理",
"role_manage-account-links": "アカウントリンクの管理",
"role_manage-consent": "同意の管理",
"role_read-token": "トークンの参照",
"role_offline-access": "オフライン・アクセス",
"role_uma_authorization": "パーミッションの取得",
"client_account": "アカウント",
"client_account-console": "アカウント・コンソール",
"client_security-admin-console": "セキュリティー管理コンソール",
"client_admin-cli": "管理CLI",
"client_realm-management": "レルム管理",
"client_broker": "ブローカー",
"requiredFields": "必須",
"allFieldsRequired": "全ての入力項目が必須",
"backToApplication": "&laquo; アプリケーションに戻る",
"backTo": "{0}に戻る",
"date": "日付",
"event": "イベント",
"ip": "IP",
"client": "クライアント",
"clients": "クライアント",
"details": "詳細",
"started": "開始",
"lastAccess": "最終アクセス",
"expires": "有効期限",
"applications": "アプリケーション",
"account": "アカウント",
"federatedIdentity": "連携済みアイデンティティー",
"authenticator": "オーセンティケーター",
"device-activity": "デバイス・アクティビティー",
"sessions": "セッション",
"log": "ログ",
"application": "アプリケーション",
"availableRoles": "利用可能なロール",
"grantedPermissions": "許可されたパーミッション",
"grantedPersonalInfo": "許可された個人情報",
"additionalGrants": "追加の許可",
"action": "アクション",
"inResource": "in",
"fullAccess": "フルアクセス",
"offlineToken": "オフライン・トークン",
"revoke": "許可の取り消し",
"configureAuthenticators": "設定済みのオーセンティケーター",
"mobile": "モバイル",
"totpStep1": "モバイルに以下のアプリケーションのいずれかをインストールしてください。",
"totpStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
"totpStep3": "アプリケーションで提供されたワンタイムコードを入力して保存をクリックし、セットアップを完了してください。",
"totpStep3DeviceName": "OTPデバイスの管理に役立つようなデバイス名を指定してください。",
"totpManualStep2": "アプリケーションを開き、キーを入力してください。",
"totpManualStep3": "アプリケーションが設定できる場合は、次の設定値を使用してください。",
"totpUnableToScan": "スキャンできませんか?",
"totpScanBarcode": "バーコードをスキャンしますか?",
"totp.totp": "時間ベース",
"totp.hotp": "カウンターベース",
"totpType": "タイプ",
"totpAlgorithm": "アルゴリズム",
"totpDigits": "数字",
"totpInterval": "間隔",
"totpCounter": "カウンター",
"totpDeviceName": "デバイス名",
"missingUsernameMessage": "ユーザー名を入力してください。",
"missingFirstNameMessage": "名を入力してください。",
"invalidEmailMessage": "無効なメールアドレスです。",
"missingLastNameMessage": "姓を入力してください。",
"missingEmailMessage": "Eメールを入力してください。",
"missingPasswordMessage": "パスワードを入力してください。",
"notMatchPasswordMessage": "パスワードが一致していません。",
"invalidUserMessage": "無効なユーザーです。",
"missingTotpMessage": "オーセンティケーター・コードを入力してください。",
"missingTotpDeviceNameMessage": "デバイス名を指定してください。",
"invalidPasswordExistingMessage": "既存のパスワードが不正です。",
"invalidPasswordConfirmMessage": "新しいパスワード(確認)と一致していません。",
"invalidTotpMessage": "無効なオーセンティケーター・コードです。",
"usernameExistsMessage": "既に存在するユーザー名です。",
"emailExistsMessage": "既に存在するEメールです。",
"readOnlyUserMessage": "読み取り専用のため、アカウントを更新することはできません。",
"readOnlyUsernameMessage": "読み取り専用のため、ユーザー名を更新することはできません。",
"readOnlyPasswordMessage": "読み取り専用のため、パスワードを更新することはできません。",
"successTotpMessage": "モバイル・オーセンティケーターが設定されました。",
"successTotpRemovedMessage": "モバイル・オーセンティケーターが削除されました。",
"successGrantRevokedMessage": "許可が正常に取り消しされました。",
"accountUpdatedMessage": "アカウントが更新されました。",
"accountPasswordUpdatedMessage": "パスワードが更新されました。",
"missingIdentityProviderMessage": "アイデンティティー・プロバイダーが指定されていません。",
"invalidFederatedIdentityActionMessage": "無効または存在しないアクションです。",
"identityProviderNotFoundMessage": "指定されたアイデンティティー・プロバイダーが見つかりません。",
"federatedIdentityLinkNotActiveMessage": "このアイデンティティーは有効ではありません。",
"federatedIdentityRemovingLastProviderMessage": "パスワードがないため、最後の連携済みアイデンティティーが削除できません。",
"identityProviderRedirectErrorMessage": "アイデンティティー・プロバイダーへのリダイレクトに失敗しました。",
"identityProviderRemovedMessage": "アイデンティティー・プロバイダーが正常に削除されました。",
"identityProviderAlreadyLinkedMessage": "{0}から返された連携済みアイデンティティーは既に他のユーザーに関連付けされています。",
"staleCodeAccountMessage": "有効期限切れです。再度お試しください。",
"consentDenied": "同意が拒否されました。",
"accountDisabledMessage": "アカウントが無効です。管理者に連絡してください。",
"accountTemporarilyDisabledMessage": "アカウントが一時的に無効です。管理者に連絡するか、しばらく時間をおいてから再度お試しください。",
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード:少なくとも{0}文字の大文字を含む必要があります。",
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
"myResources": "マイリソース",
"myResourcesSub": "マイリソース",
"doDeny": "拒否",
"doRevoke": "取り消し",
"doApprove": "承認",
"doRemoveSharing": "共有の削除",
"doRemoveRequest": "要求の削除",
"peopleAccessResource": "このリソースにアクセスできる人",
"resourceManagedPolicies": "このリソースへのアクセスを許可するパーミッション",
"resourceNoPermissionsGrantingAccess": "このリソースへのアクセスを許可する権限はありません",
"anyAction": "任意のアクション",
"description": "説明",
"name": "名前",
"scopes": "スコープ",
"resource": "リソース",
"user": "ユーザー",
"peopleSharingThisResource": "このリソースを共有している人",
"shareWithOthers": "他人と共有",
"needMyApproval": "承認が必要",
"requestsWaitingApproval": "承認待ちの要求",
"icon": "アイコン",
"requestor": "要求者",
"owner": "オーナー",
"resourcesSharedWithMe": "共有しているリソース",
"permissionRequestion": "パーミッションの要求",
"permission": "パーミッション",
"shares": "共有(複数)",
"notBeingShared": "このリソースは共有されていません。",
"notHaveAnyResource": "リソースがありません。",
"noResourcesSharedWithYou": "共有しているリソースはありません",
"havePermissionRequestsWaitingForApproval": "承認を待っている{0}個のパーミッションの要求があります。",
"clickHereForDetails": "詳細はこちらをクリックしてください。",
"resourceIsNotBeingShared": "リソースは共有されていません。",
"locale_ca": "Català",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_nl": "Nederlands",
"locale_no": "Norsk",
"locale_lt": "Lietuvių",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_sk": "Slovenčina",
"locale_sv": "Svenska",
"locale_tr": "Turkish",
"locale_zh-CN": "中文简体",
"applicaitonName": "名前",
"applicationType": "アプリケーション・タイプ",
"applicationInUse": "使用中のアプリケーションのみ",
"clearAllFilter": "すべてのフィルターをクリア",
"activeFilters": "アクティブなフィルター",
"filterByName": "名前でフィルタリング...",
"allApps": "すべてのアプリケーション",
"internalApps": "内部アプリケーション",
"thirdpartyApps": "サードパーティーのアプリケーション",
"appResults": "結果",
"clientNotFoundMessage": "クライアントが見つかりません。",
"authorizedProvider": "認可済みプロバイダー",
"authorizedProviderMessage": "アカウントにリンクされた認可済みプロバイダー",
"identityProvider": "アイデンティティー・プロバイダー",
"identityProviderMessage": "アカウントと設定したアイデンティティー・プロバイダーをリンクするには",
"socialLogin": "ソーシャル・ログイン",
"userDefined": "ユーザー定義",
"removeAccess": "アクセス権の削除",
"removeAccessMessage": "このアプリ・アカウントを使用する場合は、アクセス権を再度付与する必要があります。",
"authenticatorStatusMessage": "2要素認証は現在",
"authenticatorFinishSetUpTitle": "あなたの2要素認証",
"authenticatorFinishSetUpMessage": "Keycloakアカウントにサインインするたびに、2要素認証コードを入力するように求められます。",
"authenticatorSubTitle": "2要素認証を設定する",
"authenticatorSubMessage": "アカウントのセキュリティーを強化するには、利用可能な2要素認証の方式のうち少なくとも1つを有効にします。",
"authenticatorMobileTitle": "モバイル・オーセンティケーター",
"authenticatorMobileMessage": "モバイル・オーセンティケーターを使用して、2要素認証として確認コードを取得します。",
"authenticatorMobileFinishSetUpMessage": "オーセンティケーターはあなたの携帯電話にバインドされています。",
"authenticatorActionSetup": "セットアップ",
"authenticatorSMSTitle": "SMSコード",
"authenticatorSMSMessage": "Keycloakは、2要素認証として確認コードを携帯電話に送信します。",
"authenticatorSMSFinishSetUpMessage": "テキスト・メッセージが次の電話番号宛に送信されます:",
"authenticatorDefaultStatus": "デフォルト",
"authenticatorChangePhone": "電話番号の変更",
"authenticatorBackupCodesTitle": "バックアップ・コード",
"authenticatorBackupCodesMessage": "8桁のバックアップ・コードの入手",
"authenticatorBackupCodesFinishSetUpMessage": "この時点で12個のバックアップ・コードが生成されました。それぞれ一度だけ使用できます。",
"authenticatorMobileSetupTitle": "モバイル・オーセンティケーターのセットアップ",
"smscodeIntroMessage": "電話番号を入力すると、確認コードがあなたの電話に送信されます。",
"mobileSetupStep1":
"携帯電話にオーセンティケーター・アプリケーションをインストールします。ここにリストされているアプリケーションがサポートされています。",
"mobileSetupStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
"mobileSetupStep3": "アプリケーションから提供されたワンタイムコードを入力し、保存をクリックしてセットアップを終了します。",
"scanBarCode": "バーコードをスキャンしますか?",
"enterBarCode": "ワンタイムコードを入力してください",
"doCopy": "コピー",
"doFinish": "終了",
"authenticatorSMSCodeSetupTitle": "SMSコードのセットアップ",
"chooseYourCountry": "国を選んでください",
"enterYourPhoneNumber": "電話番号を入力してください",
"sendVerficationCode": "確認コードの送信",
"enterYourVerficationCode": "確認コードを入力してください",
"authenticatorBackupCodesSetupTitle": "バックアップ・コードのセットアップ",
"backupcodesIntroMessage":
"携帯電話にアクセスできない場合でも、バックアップ・コードを使用してアカウントにログインできます。どこか安全でアクセス可能な場所に保管してください。",
"realmName": "レルム",
"doDownload": "ダウンロード",
"doPrint": "印刷",
"backupCodesTips-1": "各バックアップ・コードは1回使用できます。",
"backupCodesTips-2": "これらのコードはこの日に生成されました:",
"generateNewBackupCodes": "新しいバックアップ・コードを生成する",
"backupCodesTips-3": "新しいバックアップ・コードを生成すると、現在のコードは機能しなくなります。",
"backtoAuthenticatorPage": "オーセンティケーター・ページに戻る",
"resources": "リソース",
"sharedwithMe": "私と共有",
"share": "共有",
"sharedwith": "共有",
"accessPermissions": "アクセス・パーミッション",
"permissionRequests": "パーミッションの要求",
"approve": "承認",
"approveAll": "すべて承認",
"people": "人",
"perPage": "1ページあたり",
"currentPage": "現在のページ",
"sharetheResource": "リソースの共有",
"group": "グループ",
"selectPermission": "パーミッションを選択",
"addPeople": "あなたのリソースを共有する人を追加",
"addTeam": "あなたのリソースを共有するチームを追加",
"myPermissions": "私のパーミッション",
"waitingforApproval": "承認待ち",
"anyPermission": "任意のパーミッション",
"openshift.scope.user_info": "ユーザー情報",
"openshift.scope.user_check-access": "ユーザーアクセス情報",
"openshift.scope.user_full": "フルアクセス",
"openshift.scope.list-projects": "プロジェクトの一覧表示"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,143 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Saugoti",
"doCancel": "Atšaukti",
"doLogOutAllSessions": "Atjungti visas sesijas",
"doRemove": "Šalinti",
"doAdd": "Pridėti",
"doSignOut": "Atsijungti",
"editAccountHtmlTitle": "Redaguoti paskyrą",
"federatedIdentitiesHtmlTitle": "Susietos paskyros",
"accountLogHtmlTitle": "Paskyros žurnalas",
"changePasswordHtmlTitle": "Keisti slaptažodį",
"sessionsHtmlTitle": "Prisijungimo sesijos",
"accountManagementTitle": "Keycloak Naudotojų Administravimas",
"authenticatorTitle": "Autentifikatorius",
"applicationsHtmlTitle": "Programos",
"authenticatorCode": "Vienkartinis kodas",
"email": "El. paštas",
"firstName": "Vardas",
"givenName": "Pavardė",
"fullName": "Pilnas vardas",
"lastName": "Pavardė",
"familyName": "Pavardė",
"password": "Slaptažodis",
"passwordConfirm": "Pakartotas slaptažodis",
"passwordNew": "Naujas slaptažodis",
"username": "Naudotojo vardas",
"address": "Adresas",
"street": "Gatvė",
"locality": "Miestas arba vietovė",
"region": "Rajonas",
"postal_code": "Pašto kodas",
"country": "Šalis",
"emailVerified": "El. pašto adresas patvirtintas",
"gssDelegationCredential": "GSS prisijungimo duomenų delegavimas",
"role_admin": "Administratorius",
"role_realm-admin": "Srities administravimas",
"role_create-realm": "Kurti sritį",
"role_view-realm": "Peržiūrėti sritį",
"role_view-users": "Peržiūrėti naudotojus",
"role_view-applications": "Peržiūrėti programas",
"role_view-clients": "Peržiūrėti klientines programas",
"role_view-events": "Peržiūrėti įvykių žurnalą",
"role_view-identity-providers": "Peržiūrėti tapatybės teikėjus",
"role_manage-realm": "Valdyti sritis",
"role_manage-users": "Valdyti naudotojus",
"role_manage-applications": "Valdyti programas",
"role_manage-identity-providers": "Valdyti tapatybės teikėjus",
"role_manage-clients": "Valdyti programas",
"role_manage-events": "Valdyti įvykius",
"role_view-profile": "Peržiūrėti paskyrą",
"role_manage-account": "Valdyti paskyrą",
"role_read-token": "Skaityti prieigos rakšą",
"role_offline-access": "Darbas neprisijungus",
"role_uma_authorization": "Įgauti UMA autorizavimo teises",
"client_account": "Paskyra",
"client_security-admin-console": "Saugumo administravimo konsolė",
"client_admin-cli": "Administravimo CLI",
"client_realm-management": "Srities valdymas",
"client_broker": "Tarpininkas",
"requiredFields": "Privalomi laukai",
"allFieldsRequired": "Visi laukai yra privalomi",
"backToApplication": "&laquo; Grįžti į programą",
"backTo": "Atgal į {0}",
"date": "Data",
"event": "Įvykis",
"ip": "IP",
"client": "Klientas",
"clients": "Klientai",
"details": "Detaliau",
"started": "Sukūrimo laikas",
"lastAccess": "Vėliausia prieiga",
"expires": "Galioja iki",
"applications": "Programos",
"account": "Paskyra",
"federatedIdentity": "Susieta tapatybė",
"authenticator": "Autentifikatorius",
"sessions": "Sesijos",
"log": "Įvykiai",
"application": "Programa",
"availablePermissions": "Galimos teisės",
"grantedPermissions": "Įgalintos teisės",
"grantedPersonalInfo": "Įgalinta asmeninė informacija",
"additionalGrants": "Papildomi įgaliojimai",
"action": "Veiksmas",
"inResource": "yra",
"fullAccess": "Pilna prieiga",
"offlineToken": "Režimo neprisijungus raktas (token)",
"revoke": "Atšaukti įgaliojimą",
"configureAuthenticators": "Sukonfigūruotas autentifikatorius",
"mobile": "Mobilus",
"totpStep1":
'Įdiekite <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> arba Google Authenticator savo įrenginyje. Programėlės prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store.',
"totpStep2": "Atidarykite programėlę ir nuskenuokite barkodą arba įveskite kodą.",
"totpStep3": "Įveskite programėlėje sugeneruotą vieną kartą galiojantį kodą ir paspauskite Saugoti norėdami prisijungti.",
"missingUsernameMessage": "Prašome įvesti naudotojo vardą.",
"missingFirstNameMessage": "Prašome įvesti vardą.",
"invalidEmailMessage": "Neteisingas el. pašto adresas.",
"missingLastNameMessage": "Prašome įvesti pavardę.",
"missingEmailMessage": "Prašome įvesti el. pašto adresą.",
"missingPasswordMessage": "Prašome įvesti slaptažodį.",
"notMatchPasswordMessage": "Slaptažodžiai nesutampa.",
"missingTotpMessage": "Prašome įvesti autentifikacijos kodą.",
"invalidPasswordExistingMessage": "Neteisingas dabartinis slaptažodis.",
"invalidPasswordConfirmMessage": "Pakartotas slaptažodis nesutampa.",
"invalidTotpMessage": "Neteisingas autentifikacijos kodas.",
"usernameExistsMessage": "Toks naudotojas jau egzistuoja.",
"emailExistsMessage": "El. pašto adresas jau egzistuoja.",
"readOnlyUserMessage": "Tik skaitymui sukonfigūruotos paskyros duomenų atnaujinti neleidžiama.",
"readOnlyPasswordMessage": "Tik skaitymui sukonfigūruotos paskyros slaptažodžio atnaujinti neleidžiama.",
"successTotpMessage": "Mobilus autentifikatorius sukonfigūruotas.",
"successTotpRemovedMessage": "Mobilus autentifikatorius pašalintas.",
"successGrantRevokedMessage": "Įgalinimas pašalintas sėkmingai.",
"accountUpdatedMessage": "Jūsų paskyros duomenys sėkmingai atnaujinti.",
"accountPasswordUpdatedMessage": "Jūsų paskyros slaptažodis pakeistas.",
"missingIdentityProviderMessage": "Nenurodytas tapatybės teikėjas.",
"invalidFederatedIdentityActionMessage": "Neteisingas arba nežinomas veiksmas.",
"identityProviderNotFoundMessage": "Nurodytas tapatybės teikėjas nerastas.",
"federatedIdentityLinkNotActiveMessage": "Nurodyta susieta tapatybė neaktyvi.",
"federatedIdentityRemovingLastProviderMessage":
"Jūs negalite pašalinti paskutinio tapatybės teikėjo sąsajos, nes Jūs neturite nusistatę paskyros slaptažodžio.",
"identityProviderRedirectErrorMessage": "Klaida nukreipiant į tapatybės teikėjo puslapį.",
"identityProviderRemovedMessage": "Tapatybės teikėjas sėkmingai pašalintas.",
"identityProviderAlreadyLinkedMessage": "Susieta tapatybė iš {0} jau susieta su kita paskyra.",
"staleCodeAccountMessage": "Puslapio galiojimas baigėsi. Bandykite dar kartą.",
"consentDenied": "Prieiga draudžiama.",
"accountDisabledMessage": "Paskyros galiojimas sustabdytas, kreipkitės į administratorių.",
"accountTemporarilyDisabledMessage": "Paskyros galiojimas laikinai sustabdytas. Kreipkitės į administratorių arba pabandykite vėliau.",
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,143 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Opslaan",
"doCancel": "Annuleer",
"doLogOutAllSessions": "Alle sessies uitloggen",
"doRemove": "Verwijder",
"doAdd": "Voeg toe",
"doSignOut": "Afmelden",
"editAccountHtmlTitle": "Bewerk account",
"federatedIdentitiesHtmlTitle": "Federated Identities",
"accountLogHtmlTitle": "Account log",
"changePasswordHtmlTitle": "Verander wachtwoord",
"sessionsHtmlTitle": "Sessies",
"accountManagementTitle": "Keycloak Accountbeheer",
"authenticatorTitle": "Authenticator",
"applicationsHtmlTitle": "Toepassingen",
"authenticatorCode": "Eenmalige code",
"email": "E-mailadres",
"firstName": "Voornaam",
"givenName": "Voornaam",
"fullName": "Volledige naam",
"lastName": "Achternaam",
"familyName": "Achternaam",
"password": "Wachtwoord",
"passwordConfirm": "Bevestiging",
"passwordNew": "Nieuw Wachtwoord",
"username": "Gebruikersnaam",
"address": "Adres",
"street": "Straat",
"locality": "Stad of plaats",
"region": "Staat, provincie of regio",
"postal_code": "Postcode",
"country": "Land",
"emailVerified": "E-mailadres geverifieerd",
"gssDelegationCredential": "GSS gedelegeerde aanmeldgegevens",
"role_admin": "Beheer",
"role_realm-admin": "Realmbeheer",
"role_create-realm": "Creëer realm",
"role_view-realm": "Bekijk realm",
"role_view-users": "Bekijk gebruikers",
"role_view-applications": "Bekijk toepassingen",
"role_view-clients": "Bekijk clients",
"role_view-events": "Bekijk gebeurtenissen",
"role_view-identity-providers": "Bekijk identity providers",
"role_manage-realm": "Beheer realm",
"role_manage-users": "Beheer gebruikers",
"role_manage-applications": "Beheer toepassingen",
"role_manage-identity-providers": "Beheer identity providers",
"role_manage-clients": "Beheer clients",
"role_manage-events": "Beheer gebeurtenissen",
"role_view-profile": "Bekijk profiel",
"role_manage-account": "Beheer account",
"role_manage-account-links": "Beheer accountkoppelingen",
"role_read-token": "Lees token",
"role_offline-access": "Offline toegang",
"role_uma_authorization": "Verkrijg UMA rechten",
"client_account": "Account",
"client_security-admin-console": "Console Veligheidsbeheer",
"client_admin-cli": "Beheer CLI",
"client_realm-management": "Realmbeheer",
"client_broker": "Broker",
"requiredFields": "Verplichte velden",
"allFieldsRequired": "Alle velden verplicht",
"backToApplication": "&laquo; Terug naar toepassing",
"backTo": "Terug naar {0}",
"date": "Datum",
"event": "Gebeurtenis",
"ip": "IP",
"client": "Client",
"clients": "Clients",
"details": "Details",
"started": "Gestart",
"lastAccess": "Laatste toegang",
"expires": "Vervalt",
"applications": "Toepassingen",
"account": "Account",
"federatedIdentity": "Federated Identity",
"authenticator": "Authenticator",
"sessions": "Sessies",
"log": "Log",
"application": "Toepassing",
"availablePermissions": "Beschikbare rechten",
"grantedPermissions": "Gegunde rechten",
"grantedPersonalInfo": "Gegunde Persoonsgegevens",
"additionalGrants": "Verdere vergunningen",
"action": "Actie",
"inResource": "in",
"fullAccess": "Volledige toegang",
"offlineToken": "Offline Token",
"revoke": "Vergunning intrekken",
"configureAuthenticators": "Ingestelde authenticators",
"mobile": "Mobiel nummer",
"totpStep1": "Installeer een van de onderstaande applicaties op uw mobiele apparaat:",
"totpStep2": "Open de toepassing en scan de QR-code of voer de sleutel in.",
"totpStep3": "Voer de door de toepassing gegeven eenmalige code in en klik op Opslaan om de configuratie af te ronden.",
"missingUsernameMessage": "Gebruikersnaam ontbreekt.",
"missingFirstNameMessage": "Voornaam onbreekt.",
"invalidEmailMessage": "Ongeldig e-mailadres.",
"missingLastNameMessage": "Achternaam ontbreekt.",
"missingEmailMessage": "E-mailadres ontbreekt.",
"missingPasswordMessage": "Wachtwoord ontbreekt.",
"notMatchPasswordMessage": "Wachtwoorden komen niet overeen.",
"missingTotpMessage": "Authenticatiecode ontbreekt.",
"invalidPasswordExistingMessage": "Ongeldig bestaand wachtwoord.",
"invalidPasswordConfirmMessage": "Wachtwoordbevestiging komt niet overeen.",
"invalidTotpMessage": "Ongeldige authenticatiecode.",
"emailExistsMessage": "E-mailadres bestaat reeds.",
"readOnlyUserMessage": "U kunt uw account niet bijwerken aangezien het account alleen-lezen is.",
"readOnlyPasswordMessage": "U kunt uw wachtwoord niet wijzigen omdat uw account alleen-lezen is.",
"successTotpMessage": "Mobiele authenticator geconfigureerd.",
"successTotpRemovedMessage": "Mobiele authenticator verwijderd.",
"successGrantRevokedMessage": "Vergunning succesvol ingetrokken",
"accountUpdatedMessage": "Uw account is gewijzigd.",
"accountPasswordUpdatedMessage": "Uw wachtwoord is gewijzigd.",
"missingIdentityProviderMessage": "Geen identity provider aangegeven.",
"invalidFederatedIdentityActionMessage": "Ongeldige of ontbrekende actie op federated identity.",
"identityProviderNotFoundMessage": "Gespecificeerde identity provider niet gevonden.",
"federatedIdentityLinkNotActiveMessage": "Deze federated identity is niet langer geldig.",
"federatedIdentityRemovingLastProviderMessage":
"U kunt de laatste federated identity provider niet verwijderen aangezien u dan niet langer zou kunnen inloggen.",
"identityProviderRedirectErrorMessage": "Kon niet herverwijzen naar identity provider.",
"identityProviderRemovedMessage": "Identity provider met succes verwijderd.",
"identityProviderAlreadyLinkedMessage": "Door {0} teruggegeven federated identity is al gekoppeld aan een andere gebruiker.",
"staleCodeAccountMessage": "De pagina is verlopen. Probeer het nogmaals.",
"consentDenied": "Toestemming geweigerd",
"accountDisabledMessage": "Account is gedeactiveerd. Contacteer de beheerder.",
"accountTemporarilyDisabledMessage": "Account is tijdelijk deactiveerd, neem contact op met de beheerder of probeer het later opnieuw.",
"invalidPasswordMinLengthMessage": "Ongeldig wachtwoord: de minimale lengte is {0} karakters.",
"invalidPasswordMinLowerCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.",
"invalidPasswordMinDigitsMessage": "Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.",
"invalidPasswordMinSpecialCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.",
"invalidPasswordNotUsernameMessage": "Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.",
"invalidPasswordRegexPatternMessage": "Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.",
"invalidPasswordHistoryMessage": "Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.",
"invalidPasswordGenericMessage": "Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,153 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Lagre",
"doCancel": "Avbryt",
"doLogOutAllSessions": "Logg ut av alle sesjoner",
"doRemove": "Fjern",
"doAdd": "Legg til",
"doSignOut": "Logg ut",
"editAccountHtmlTitle": "Rediger konto",
"federatedIdentitiesHtmlTitle": "Federerte identiteter",
"accountLogHtmlTitle": "Kontologg",
"changePasswordHtmlTitle": "Endre passord",
"sessionsHtmlTitle": "Sesjoner",
"accountManagementTitle": "Keycloak kontoadministrasjon",
"authenticatorTitle": "Autentikator",
"applicationsHtmlTitle": "Applikasjoner",
"authenticatorCode": "Engangskode",
"email": "E-post",
"firstName": "Fornavn",
"givenName": "Fornavn",
"fullName": "Fullt navn",
"lastName": "Etternavn",
"familyName": "Etternavn",
"password": "Passord",
"passwordConfirm": "Bekreftelse",
"passwordNew": "Nytt passord",
"username": "Brukernavn",
"address": "Adresse",
"street": "Gate-/veinavn + husnummer",
"locality": "By",
"region": "Fylke",
"postal_code": "Postnummer",
"country": "Land",
"emailVerified": "E-post bekreftet",
"gssDelegationCredential": "GSS legitimasjonsdelegering",
"role_admin": "Administrator",
"role_realm-admin": "Administrator for sikkerhetsdomene",
"role_create-realm": "Opprette sikkerhetsdomene",
"role_view-realm": "Se sikkerhetsdomene",
"role_view-users": "Se brukere",
"role_view-applications": "Se applikasjoner",
"role_view-clients": "Se klienter",
"role_view-events": "Se hendelser",
"role_view-identity-providers": "Se identitetsleverandører",
"role_manage-realm": "Administrere sikkerhetsdomene",
"role_manage-users": "Administrere brukere",
"role_manage-applications": "Administrere applikasjoner",
"role_manage-identity-providers": "Administrere identitetsleverandører",
"role_manage-clients": "Administrere klienter",
"role_manage-events": "Administrere hendelser",
"role_view-profile": "Se profil",
"role_manage-account": "Administrere konto",
"role_read-token": "Lese token",
"role_offline-access": "Frakoblet tilgang",
"role_uma_authorization": "Skaffe tillatelser",
"client_account": "Konto",
"client_security-admin-console": "Sikkerhetsadministrasjonskonsoll",
"client_admin-cli": "Kommandolinje-grensesnitt for administrator",
"client_realm-management": "Sikkerhetsdomene-administrasjon",
"client_broker": "Broker",
"requiredFields": "Obligatoriske felt",
"allFieldsRequired": "Alle felt må fylles ut",
"backToApplication": "&laquo; Tilbake til applikasjonen",
"backTo": "Tilbake til {0}",
"date": "Dato",
"event": "Hendelse",
"ip": "IP",
"client": "Klient",
"clients": "Klienter",
"details": "Detaljer",
"started": "Startet",
"lastAccess": "Sist benyttet",
"expires": "Utløper",
"applications": "Applikasjoner",
"account": "Konto",
"federatedIdentity": "Federert identitet",
"authenticator": "Autentikator",
"sessions": "Sesjoner",
"log": "Logg",
"application": "Applikasjon",
"availablePermissions": "Tilgjengelige rettigheter",
"grantedPermissions": "Innvilgede rettigheter",
"grantedPersonalInfo": "Innvilget personlig informasjon",
"additionalGrants": "Ekstra rettigheter",
"action": "Handling",
"inResource": "i",
"fullAccess": "Full tilgang",
"offlineToken": "Offline token",
"revoke": "Opphev rettighet",
"configureAuthenticators": "Konfigurerte autentikatorer",
"mobile": "Mobiltelefon",
"totpStep1": "Installer ett av følgende programmer på mobilen din.",
"totpStep2": "Åpne applikasjonen og skann strekkoden eller skriv inn koden.",
"totpStep3": "Skriv inn engangskoden gitt av applikasjonen og klikk Lagre for å fullføre.",
"missingUsernameMessage": "Vennligst oppgi brukernavn.",
"missingFirstNameMessage": "Vennligst oppgi fornavn.",
"invalidEmailMessage": "Ugyldig e-postadresse.",
"missingLastNameMessage": "Vennligst oppgi etternavn.",
"missingEmailMessage": "Vennligst oppgi e-postadresse.",
"missingPasswordMessage": "Vennligst oppgi passord.",
"notMatchPasswordMessage": "Passordene er ikke like.",
"missingTotpMessage": "Vennligst oppgi engangskode.",
"invalidPasswordExistingMessage": "Ugyldig eksisterende passord.",
"invalidPasswordConfirmMessage": "Passordene er ikke like.",
"invalidTotpMessage": "Ugyldig engangskode.",
"usernameExistsMessage": "Brukernavnet finnes allerede.",
"emailExistsMessage": "E-postadressen finnes allerede.",
"readOnlyUserMessage": "Du kan ikke oppdatere kontoen din ettersom den er skrivebeskyttet.",
"readOnlyPasswordMessage": "Du kan ikke oppdatere passordet ditt ettersom kontoen din er skrivebeskyttet.",
"successTotpMessage": "Autentikator for mobiltelefon er konfigurert.",
"successTotpRemovedMessage": "Autentikator for mobiltelefon er fjernet.",
"successGrantRevokedMessage": "Vellykket oppheving av rettighet.",
"accountUpdatedMessage": "Kontoen din har blitt oppdatert.",
"accountPasswordUpdatedMessage": "Ditt passord har blitt oppdatert.",
"missingIdentityProviderMessage": "Identitetsleverandør er ikke spesifisert.",
"invalidFederatedIdentityActionMessage": "Ugyldig eller manglende handling.",
"identityProviderNotFoundMessage": "Spesifisert identitetsleverandør ikke funnet.",
"federatedIdentityLinkNotActiveMessage": "Denne identiteten er ikke lenger aktiv.",
"federatedIdentityRemovingLastProviderMessage": "Du kan ikke fjerne siste federerte identitet ettersom du ikke har et passord.",
"identityProviderRedirectErrorMessage": "Redirect til identitetsleverandør feilet.",
"identityProviderRemovedMessage": "Fjerning av identitetsleverandør var vellykket.",
"identityProviderAlreadyLinkedMessage": "Federert identitet returnert av {0} er allerede koblet til en annen bruker.",
"staleCodeAccountMessage": "Siden har utløpt. Vennligst prøv en gang til.",
"consentDenied": "Samtykke avslått.",
"accountDisabledMessage": "Konto er deaktivert, kontakt administrator.",
"accountTemporarilyDisabledMessage": "Konto er midlertidig deaktivert, kontakt administrator eller prøv igjen senere.",
"invalidPasswordMinLengthMessage": "Ugyldig passord: minimum lengde {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} små bokstaver.",
"invalidPasswordMinDigitsMessage": "Ugyldig passord: må inneholde minimum {0} sifre.",
"invalidPasswordMinUpperCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} store bokstaver.",
"invalidPasswordMinSpecialCharsMessage": "Ugyldig passord: må inneholde minimum {0} spesialtegn.",
"invalidPasswordNotUsernameMessage": "Ugyldig passord: kan ikke være likt brukernavn.",
"invalidPasswordRegexPatternMessage": "Ugyldig passord: tilfredsstiller ikke kravene for passord-mønster.",
"invalidPasswordHistoryMessage": "Ugyldig passord: kan ikke være likt noen av de {0} foregående passordene.",
"locale_ca": "Català",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_no": "Norsk",
"locale_nl": "Nederlands",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_zh-CN": "中文简体"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,8 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {};
export default messages;
/* spell-checker: enable */

View File

@ -1,140 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Salvar",
"doCancel": "Cancelar",
"doLogOutAllSessions": "Sair de todas as sessões",
"doRemove": "Remover",
"doAdd": "Adicionar",
"doSignOut": "Sair",
"editAccountHtmlTitle": "Editar Conta",
"federatedIdentitiesHtmlTitle": "Identidades Federadas",
"accountLogHtmlTitle": "Log da conta",
"changePasswordHtmlTitle": "Alterar senha",
"sessionsHtmlTitle": "Sessões",
"accountManagementTitle": "Gerenciamento de Conta",
"authenticatorTitle": "Autenticator",
"applicationsHtmlTitle": "Aplicativos",
"authenticatorCode": "Código autenticador",
"email": "E-mail",
"firstName": "Primeiro nome",
"givenName": "Primeiro nome",
"fullName": "Nome completo",
"lastName": "Sobrenome",
"familyName": "Sobrenome",
"password": "Senha",
"passwordConfirm": "Confirmação",
"passwordNew": "Nova senha",
"username": "Nome de usúario",
"address": "Endereço",
"street": "Logradouro",
"locality": "Cidade ou Localidade",
"region": "Estado",
"postal_code": "CEP",
"country": "País",
"emailVerified": "E-mail verificado",
"gssDelegationCredential": "GSS Delegação de Credencial",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Cria realm",
"role_view-realm": "Visualiza realm",
"role_view-users": "Visualiza usuários",
"role_view-applications": "Visualiza aplicações",
"role_view-clients": "Visualiza clientes",
"role_view-events": "Visualiza eventos",
"role_view-identity-providers": "Visualiza provedores de identidade",
"role_manage-realm": "Gerencia realm",
"role_manage-users": "Gerencia usuários",
"role_manage-applications": "Gerencia aplicações",
"role_manage-identity-providers": "Gerencia provedores de identidade",
"role_manage-clients": "Gerencia clientes",
"role_manage-events": "Gerencia eventos",
"role_view-profile": "Visualiza perfil",
"role_manage-account": "Gerencia conta",
"role_read-token": "Lê token",
"role_offline-access": "Acesso Offline",
"role_uma_authorization": "Obter permissões",
"client_account": "Conta",
"client_security-admin-console": "Console de Administração de Segurança",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Gerenciamento de Realm",
"client_broker": "Broker",
"requiredFields": "Campos obrigatórios",
"allFieldsRequired": "Todos os campos são obrigatórios",
"backToApplication": "&laquo; Voltar para aplicação",
"backTo": "Voltar para {0}",
"date": "Data",
"event": "Evento",
"ip": "IP",
"client": "Cliente",
"clients": "Clientes",
"details": "Detalhes",
"started": "Iniciado",
"lastAccess": "Último acesso",
"expires": "Expira",
"applications": "Aplicativos",
"account": "Conta",
"federatedIdentity": "Identidade Federada",
"authenticator": "Autenticador",
"sessions": "Sessões",
"log": "Log",
"application": "Aplicativo",
"availablePermissions": "Permissões Disponíveis",
"grantedPermissions": "Permissões Concedidas",
"grantedPersonalInfo": "Informações Pessoais Concedidas",
"additionalGrants": "Concessões Adicionais",
"action": "Ação",
"inResource": "em",
"fullAccess": "Acesso Completo",
"offlineToken": "Offline Token",
"revoke": "Revogar Concessões",
"configureAuthenticators": "Autenticadores Configurados",
"mobile": "Mobile",
"totpStep1":
'Instalar <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou Google Authenticator em seu dispositivo. Ambas aplicações estão disponíveis no <a href="https://play.google.com">Google Play</a> e na Apple App Store.',
"totpStep2": "Abra o aplicativo e escaneie o código de barras ou entre com o código.",
"totpStep3": "Digite o código fornecido pelo aplicativo e clique em Salvar para concluir a configuração.",
"missingUsernameMessage": "Por favor, especifique o nome de usuário.",
"missingFirstNameMessage": "Por favor, informe o primeiro nome.",
"invalidEmailMessage": "E-mail inválido.",
"missingLastNameMessage": "Por favor, informe o sobrenome.",
"missingEmailMessage": "Por favor, informe o e-mail.",
"missingPasswordMessage": "Por favor, informe a senha.",
"notMatchPasswordMessage": "As senhas não coincidem.",
"missingTotpMessage": "Por favor, informe o código autenticador.",
"invalidPasswordExistingMessage": "Senha atual inválida.",
"invalidPasswordConfirmMessage": "A senha de confirmação não coincide.",
"invalidTotpMessage": "Código autenticador inválido.",
"usernameExistsMessage": "Este nome de usuário já existe.",
"emailExistsMessage": "Este e-mail já existe.",
"readOnlyUserMessage": "Você não pode atualizar sua conta, uma vez que é apenas de leitura",
"readOnlyPasswordMessage": "Você não pode atualizar sua senha, sua conta é somente leitura",
"successTotpMessage": "Autenticador mobile configurado.",
"successTotpRemovedMessage": "Autenticador mobile removido.",
"successGrantRevokedMessage": "Concessões revogadas com sucesso.",
"accountUpdatedMessage": "Sua conta foi atualizada",
"accountPasswordUpdatedMessage": "Sua senha foi atualizada",
"missingIdentityProviderMessage": "Provedor de identidade não especificado",
"invalidFederatedIdentityActionMessage": "Ação inválida ou ausente",
"identityProviderNotFoundMessage": "O provedor de identidade especificado não foi encontrado",
"federatedIdentityLinkNotActiveMessage": "Esta identidade não está mais em atividade",
"federatedIdentityRemovingLastProviderMessage": "Você não pode remover a última identidade federada como você não tem senha",
"identityProviderRedirectErrorMessage": "Falha ao redirecionar para o provedor de identidade",
"identityProviderRemovedMessage": "Provedor de identidade removido com sucesso",
"identityProviderAlreadyLinkedMessage": "Identidade federada retornado por {0} já está ligado a outro usuário.",
"accountDisabledMessage": "Conta desativada, contate o administrador",
"accountTemporarilyDisabledMessage": "A conta está temporariamente indisponível, contate o administrador ou tente novamente mais tarde",
"invalidPasswordMinLengthMessage": "Senha inválida: comprimento mínimo {0}",
"invalidPasswordMinLowerCaseCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) minúsculo",
"invalidPasswordMinDigitsMessage": "Senha inválida: deve conter pelo menos {0} número(s)",
"invalidPasswordMinUpperCaseCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) maiúsculo",
"invalidPasswordMinSpecialCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) especial",
"invalidPasswordNotUsernameMessage": "Senha inválida: não deve ser igual ao nome de usuário",
"invalidPasswordRegexPatternMessage": "Senha inválida: não corresponde ao padrão da expressão regular.",
"invalidPasswordHistoryMessage": "Senha inválida: não pode ser igual a qualquer uma das {0} últimas senhas."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,143 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Сохранить",
"doCancel": "Отмена",
"doLogOutAllSessions": "Выйти из всех сессий",
"doRemove": "Удалить",
"doAdd": "Добавить",
"doSignOut": "Выход",
"editAccountHtmlTitle": "Изменение учетной записи",
"federatedIdentitiesHtmlTitle": "Федеративные идентификаторы",
"accountLogHtmlTitle": "Лог учетной записи",
"changePasswordHtmlTitle": "Смена пароля",
"sessionsHtmlTitle": "Сессии",
"accountManagementTitle": "Управление учетной записью",
"authenticatorTitle": "Аутентификатор",
"applicationsHtmlTitle": "Приложения",
"authenticatorCode": "Одноразовый код",
"email": "E-mail",
"firstName": "Имя",
"givenName": "Имя",
"fullName": "Полное имя",
"lastName": "Фамилия",
"familyName": "Фамилия",
"password": "Пароль",
"passwordConfirm": "Подтверждение пароля",
"passwordNew": "Новый пароль",
"username": "Имя пользователя",
"address": "Адрес",
"street": "Улица",
"locality": "Город",
"region": "Регион",
"postal_code": "Почтовый индекс",
"country": "Страна",
"emailVerified": "E-mail подтвержден",
"gssDelegationCredential": "Делегирование учетных данных через GSS",
"role_admin": "Администратор",
"role_realm-admin": "Администратор realm",
"role_create-realm": "Создать realm",
"role_view-realm": "Просмотр realm",
"role_view-users": "Просмотр пользователей",
"role_view-applications": "Просмотр приложений",
"role_view-clients": "Просмотр клиентов",
"role_view-events": "Просмотр событий",
"role_view-identity-providers": "Просмотр провайдеров учетных записей",
"role_manage-realm": "Управление realm",
"role_manage-users": "Управление пользователями",
"role_manage-applications": "Управление приложениями",
"role_manage-identity-providers": "Управление провайдерами учетных записей",
"role_manage-clients": "Управление клиентами",
"role_manage-events": "Управление событиями",
"role_view-profile": "Просмотр профиля",
"role_manage-account": "Управление учетной записью",
"role_read-token": "Чтение токена",
"role_offline-access": "Доступ оффлайн",
"role_uma_authorization": "Получение разрешений",
"client_account": "Учетная запись",
"client_security-admin-console": "Консоль администратора безопасности",
"client_admin-cli": "Командный интерфейс администратора",
"client_realm-management": "Управление Realm",
"client_broker": "Брокер",
"requiredFields": "Обязательные поля",
"allFieldsRequired": "Все поля обязательны",
"backToApplication": "&laquo; Назад в приложение",
"backTo": "Назад в {0}",
"date": "Дата",
"event": "Событие",
"ip": "IP",
"client": "Клиент",
"clients": "Клиенты",
"details": "Детали",
"started": "Начата",
"lastAccess": "Последний доступ",
"expires": "Истекает",
"applications": "Приложения",
"account": "Учетная запись",
"federatedIdentity": "Федеративный идентификатор",
"authenticator": "Аутентификатор",
"sessions": "Сессии",
"log": "Журнал",
"application": "Приложение",
"availablePermissions": "Доступные разрешения",
"grantedPermissions": "Согласованные разрешения",
"grantedPersonalInfo": "Согласованная персональная информация",
"additionalGrants": "Дополнительные согласования",
"action": "Действие",
"inResource": "в",
"fullAccess": "Полный доступ",
"offlineToken": "Оффлайн токен",
"revoke": "Отозвать согласование",
"configureAuthenticators": "Сконфигурированные аутентификаторы",
"mobile": "Мобильное приложение",
"totpStep1":
'Установите <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> или Google Authenticator. Оба приложения доступны на <a href="https://play.google.com">Google Play</a> и в Apple App Store.',
"totpStep2": "Откройте приложение и просканируйте баркод, либо введите ключ.",
"totpStep3": "Введите одноразовый код, выданный приложением, и нажмите сохранить для завершения установки.",
"missingUsernameMessage": "Введите имя пользователя.",
"missingFirstNameMessage": "Введите имя.",
"invalidEmailMessage": "Введите корректный E-mail.",
"missingLastNameMessage": "Введите фамилию.",
"missingEmailMessage": "Введите E-mail.",
"missingPasswordMessage": "Введите пароль.",
"notMatchPasswordMessage": "Пароли не совпадают.",
"missingTotpMessage": "Введите код аутентификатора.",
"invalidPasswordExistingMessage": "Существующий пароль неверный.",
"invalidPasswordConfirmMessage": "Подтверждение пароля не совпадает.",
"invalidTotpMessage": "Неверный код аутентификатора.",
"usernameExistsMessage": "Имя пользователя уже существует.",
"emailExistsMessage": "E-mail уже существует.",
"readOnlyUserMessage": "Вы не можете обновить информацию вашей учетной записи, т.к. она доступна только для чтения.",
"readOnlyPasswordMessage": "Вы не можете обновить пароль вашей учетной записи, т.к. он доступен только для чтения.",
"successTotpMessage": "Аутентификатор в мобильном приложении сконфигурирован.",
"successTotpRemovedMessage": "Аутентификатор в мобильном приложении удален.",
"successGrantRevokedMessage": "Согласование отозвано успешно.",
"accountUpdatedMessage": "Ваша учетная запись обновлена.",
"accountPasswordUpdatedMessage": "Ваш пароль обновлен.",
"missingIdentityProviderMessage": "Провайдер учетных записей не задан.",
"invalidFederatedIdentityActionMessage": "Некорректное или недопустимое действие.",
"identityProviderNotFoundMessage": "Заданный провайдер учетных записей не найден.",
"federatedIdentityLinkNotActiveMessage": "Идентификатор больше не активен.",
"federatedIdentityRemovingLastProviderMessage": "Вы не можете удалить последний федеративный идентификатор, т.к. Вы не имеете пароля.",
"identityProviderRedirectErrorMessage": "Ошибка перенаправления в провайдер учетных записей.",
"identityProviderRemovedMessage": "Провайдер учетных записей успешно удален.",
"identityProviderAlreadyLinkedMessage": "Федеративный идентификатор, возвращенный {0} уже используется другим пользователем.",
"staleCodeAccountMessage": "Страница устарела. Попробуйте еще раз.",
"consentDenied": "В согласовании отказано.",
"accountDisabledMessage": "Учетная запись заблокирована, обратитесь к администратору.",
"accountTemporarilyDisabledMessage": "Учетная запись временно заблокирована, обратитесь к администратору или попробуйте позже.",
"invalidPasswordMinLengthMessage": "Некорректный пароль: длина пароля должна быть не менее {0} символа(ов).",
"invalidPasswordMinLowerCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в нижнем регистре.",
"invalidPasswordMinDigitsMessage": "Некорректный пароль: пароль должен содержать не менее {0} цифр(ы).",
"invalidPasswordMinUpperCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в верхнем регистре.",
"invalidPasswordMinSpecialCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} спецсимвола(ов).",
"invalidPasswordNotUsernameMessage": "Некорректный пароль: пароль не должен совпадать с именем пользователя.",
"invalidPasswordRegexPatternMessage": "Некорректный пароль: пароль не удовлетворяет регулярному выражению.",
"invalidPasswordHistoryMessage": "Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролями.",
"invalidPasswordGenericMessage": "Некорректный пароль: новый пароль не соответствует правилам пароля."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,180 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Uložiť",
"doCancel": "Zrušiť",
"doLogOutAllSessions": "Odhlásenie všetkých relácií",
"doRemove": "Odstrániť",
"doAdd": "Pridať",
"doSignOut": "Odhlásiť",
"editAccountHtmlTitle": "Upraviť účet",
"federatedIdentitiesHtmlTitle": "Prepojená identita",
"accountLogHtmlTitle": "Denník zmien užívateľských účtov",
"changePasswordHtmlTitle": "Zmena hesla",
"sessionsHtmlTitle": "Relácie",
"accountManagementTitle": "Správa účtu Keycloak",
"authenticatorTitle": "Autentifikátor",
"applicationsHtmlTitle": "Aplikácie",
"authenticatorCode": "Jednorázový kód",
"email": "E-mail",
"firstName": "Meno",
"givenName": "Meno pri narodení",
"fullName": "Celé meno",
"lastName": "Priezvisko",
"familyName": "Rodné meno",
"password": "Heslo",
"passwordConfirm": "Potrvrdenie hesla",
"passwordNew": "Nové heslo",
"username": "Meno používateľa",
"address": "Adresa",
"street": "Ulica",
"locality": "Mesto alebo lokalita",
"region": "Kraj",
"postal_code": "PSČ",
"country": "Štát",
"emailVerified": "E-mail overený",
"gssDelegationCredential": "GSS delegované oprávnenie",
"role_admin": "Administrátor",
"role_realm-admin": "Administrátor realmu",
"role_create-realm": "Vytvoriť realm",
"role_view-realm": "Zobraziť realm",
"role_view-users": "Zobraziť používateľov",
"role_view-applications": "Zobraziť aplikácie",
"role_view-clients": "Zobraziť klientov",
"role_view-events": "Zobraziť udalosti",
"role_view-identity-providers": "Zobraziť klientov poskytovateľov identity",
"role_manage-realm": "Spravovať realm",
"role_manage-users": "Spravovať používateľov",
"role_manage-applications": "Spravovať aplikácie",
"role_manage-identity-providers": "Spravovať poskytovateľov identity",
"role_manage-clients": "Spravovať klientov",
"role_manage-events": "Spravovať udalosti",
"role_view-profile": "Zobraziť profil",
"role_manage-account": "Spravovať účet",
"role_manage-account-links": "Spravovať odkazy na účet",
"role_read-token": "Čítať token",
"role_offline-access": "Offline prístup",
"role_uma_authorization": "Autorizácia používateľom riadeného prístupu",
"client_account": "Účet klienta",
"client_security-admin-console": "Bezpečnostná administrátorská konzola",
"client_admin-cli": "Spravovať CLI klienta",
"client_realm-management": "Spravovať realmy klienta",
"client_broker": "Broker",
"requiredFields": "Povinné polia",
"allFieldsRequired": "Všetky požadované polia",
"backToApplication": "&laquo; Späť na aplikáciu",
"backTo": "Späť na {0}",
"date": "Dátum",
"event": "Udalosť",
"ip": "IP",
"client": "Klient",
"clients": "Klienti",
"details": "Podrobnosti",
"started": "Začíname",
"lastAccess": "Posledný prístup",
"expires": "Vyprší",
"applications": "Aplikácie",
"account": "Účet",
"federatedIdentity": "Prepojená identita",
"authenticator": "Autentifikátor",
"sessions": "Relácie",
"log": "Denník",
"application": "Aplikácia",
"availablePermissions": "Dostupné oprávnenia",
"grantedPermissions": "Pridelené oprávnenia",
"grantedPersonalInfo": "Poskytnuté osobné informácie",
"additionalGrants": "Dodatočné oprávnenia",
"action": "Akcia",
"inResource": "v",
"fullAccess": "Úplný prístup",
"offlineToken": "Offline token",
"revoke": "Zrušiť oprávnenie",
"configureAuthenticators": "Nakonfigurované autentifikátory",
"mobile": "Mobilný",
"totpStep1":
'Nainštalujte vo svojom zariadení <a href="https://freeotp.github.io/" target="_blank"> FreeOTP </a> alebo Google Authenticator. Obidve aplikácie sú k dispozícii v <a href="https://play.google.com"> Google Play </a> a Apple App Store.',
"totpStep2": "Otvorte aplikáciu a naskenujte čiarový kód alebo zadajte kľúč.",
"totpStep3": "Zadajte jednorazový kód poskytnutý aplikáciou a kliknutím na tlačidlo Uložiť dokončíte nastavenie.",
"totpManualStep2": "Otvorte aplikáciu a zadajte kľúč",
"totpManualStep3": "Použite nasledujúce hodnoty konfigurácie, ak aplikácia umožňuje ich nastavenie",
"totpUnableToScan": "Nemožno skenovať?",
"totpScanBarcode": "Skenovanie čiarového kódu?",
"totp.totp": "Založené na čase",
"totp.hotp": "Založené na počítadle",
"totpType": "Typ",
"totpAlgorithm": "Algoritmus",
"totpDigits": "Číslica",
"totpInterval": "Interval",
"totpCounter": "Počítadlo",
"missingUsernameMessage": "Zadajte používateľské meno.",
"missingFirstNameMessage": "Zadajte meno.",
"invalidEmailMessage": "Neplatná e-mailová adresa.",
"missingLastNameMessage": "Zadajte priezvisko.",
"missingEmailMessage": "Zadajte e-mail.",
"missingPasswordMessage": "Zadajte heslo, prosím.",
"notMatchPasswordMessage": "Heslá sa nezhodujú.",
"missingTotpMessage": "Zadajte jednorazový kód, prosím",
"invalidPasswordExistingMessage": "Neplatné existujúce heslo.",
"invalidPasswordConfirmMessage": "Potvrdenie hesla sa nezhoduje.",
"invalidTotpMessage": "Neplatný jednorazový kód.",
"usernameExistsMessage": "Užívateľské meno už existuje.",
"emailExistsMessage": "E-mail už existuje.",
"readOnlyUserMessage": "Váš účet nemôžete aktualizovať, pretože je iba na čítanie.",
"readOnlyUsernameMessage": "Nemôžete aktualizovať svoje používateľské meno, pretože je iba na čítanie.",
"readOnlyPasswordMessage": "Heslo nemôžete aktualizovať, pretože váš účet je iba na čítanie.",
"successTotpMessage": "Konfigurácia mobilného autentifikátora.",
"successTotpRemovedMessage": "Mobilný autentifikátor bol odstránený.",
"successGrantRevokedMessage": "Oprávnenie bolo úspešne zrušené.",
"accountUpdatedMessage": "Váš účet bol aktualizovaný.",
"accountPasswordUpdatedMessage": "Vaše heslo bolo aktualizované.",
"missingIdentityProviderMessage": "Poskytovateľ identity nie je zadaný.",
"invalidFederatedIdentityActionMessage": "Neplatná alebo chýbajúca akcia.",
"identityProviderNotFoundMessage": "Zadaný poskytovateľ identity nenájdený.",
"federatedIdentityLinkNotActiveMessage": "Identita už nie je aktívna.",
"federatedIdentityRemovingLastProviderMessage": "Nemôžete odstrániť poslednú spojenú identitu, pretože nemáte heslo.",
"identityProviderRedirectErrorMessage": "Nepodarilo sa presmerovať na poskytovateľa identity.",
"identityProviderRemovedMessage": "Poskytovateľ identity bol úspešne odstránený.",
"identityProviderAlreadyLinkedMessage": "Spojená identita vrátená {0} je už prepojená s iným používateľom.",
"staleCodeAccountMessage": "Platnosť vypršala. Skúste ešte raz.",
"consentDenied": "Súhlas bol zamietnutý.",
"accountDisabledMessage": "Účet je zakázaný, kontaktujte správcu.",
"accountTemporarilyDisabledMessage": "Účet je dočasne zakázaný, kontaktujte administrátora alebo skúste neskôr.",
"invalidPasswordMinLengthMessage": "Neplatné heslo: minimálna dĺžka {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovať minimálne {0} malé písmená.",
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovať aspoň {0} číslic.",
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovať aspoň {0} veľké písmená.",
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovať aspoň {0} špeciálne znaky.",
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmie byť rovnaké ako používateľské meno.",
"invalidPasswordRegexPatternMessage": "Neplatné heslo: nezodpovedá regulárnemu výrazu.",
"invalidPasswordHistoryMessage": "Neplatné heslo: nesmie sa rovnať žiadnemu z posledných {0} hesiel.",
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na čiernej listine.",
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo nezodpovedá pravidlám hesiel.",
"myResources": "Moje Zdroje",
"myResourcesSub": "Moje zdroje",
"doDeny": "Zakázať",
"doRevoke": "Odvolať",
"doApprove": "Schváliť",
"doRemoveSharing": "Odstránenie zdieľania",
"doRemoveRequest": "Odstrániť požiadavku",
"peopleAccessResource": "Ľudia s prístupom k tomuto zdroju",
"name": "Názov",
"scopes": "Rozsahy",
"resource": "Zdroj",
"user": "Používateľ",
"peopleSharingThisResource": "Ľudia zdieľajúci tento zdroj",
"shareWithOthers": "Zdieľať s ostatnými",
"needMyApproval": "Potrebuje môj súhlas",
"requestsWaitingApproval": "Vaše požiadavky čakajú na schválenie",
"icon": "Ikona",
"requestor": "Žiadateľ",
"owner": "Vlastník",
"resourcesSharedWithMe": "Zdroje zdieľané so mnou",
"permissionRequestion": "Žiadosti o povolenie",
"permission": "Oprávnenie",
"shares": "podiel (y)"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,139 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Spara",
"doCancel": "Avbryt",
"doLogOutAllSessions": "Logga ut från samtliga sessioner",
"doRemove": "Ta bort",
"doAdd": "Lägg till",
"doSignOut": "Logga ut",
"editAccountHtmlTitle": "Redigera konto",
"federatedIdentitiesHtmlTitle": "Federerade identiteter",
"accountLogHtmlTitle": "Kontologg",
"changePasswordHtmlTitle": "Byt lösenord",
"sessionsHtmlTitle": "Sessioner",
"accountManagementTitle": "Kontohantering för Keycloak",
"authenticatorTitle": "Autentiserare",
"applicationsHtmlTitle": "Applikationer",
"authenticatorCode": "Engångskod",
"email": "E-post",
"firstName": "Förnamn",
"lastName": "Efternamn",
"password": "Lösenord",
"passwordConfirm": "Bekräftelse",
"passwordNew": "Nytt lösenord",
"username": "Användarnamn",
"address": "Adress",
"street": "Gata",
"locality": "Postort",
"region": "Stat, Provins eller Region",
"postal_code": "Postnummer",
"country": "Land",
"emailVerified": "E-post verifierad",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "Administratör",
"role_realm-admin": "Realm-administratör",
"role_create-realm": "Skapa realm",
"role_view-realm": "Visa realm",
"role_view-users": "Visa användare",
"role_view-applications": "Visa applikationer",
"role_view-clients": "Visa klienter",
"role_view-events": "Visa event",
"role_view-identity-providers": "Visa identitetsleverantörer",
"role_manage-realm": "Hantera realm",
"role_manage-users": "Hantera användare",
"role_manage-applications": "Hantera applikationer",
"role_manage-identity-providers": "Hantera identitetsleverantörer",
"role_manage-clients": "Hantera klienter",
"role_manage-events": "Hantera event",
"role_view-profile": "Visa profil",
"role_manage-account": "Hantera konto",
"role_read-token": "Läs element",
"role_offline-access": "Åtkomst offline",
"role_uma_authorization": "Erhåll tillstånd",
"client_account": "Konto",
"client_security-admin-console": "Säkerhetsadministratörskonsol",
"client_admin-cli": "Administratörs-CLI",
"client_realm-management": "Realmhantering",
"requiredFields": "Obligatoriska fält",
"allFieldsRequired": "Samtliga fält krävs",
"backToApplication": "&laquo; Tillbaka till applikationen",
"backTo": "Tillbaka till {0}",
"date": "Datum",
"event": "Event",
"ip": "IP",
"client": "Klient",
"clients": "Klienter",
"details": "Detaljer",
"started": "Startade",
"lastAccess": "Senast åtkomst",
"expires": "Upphör",
"applications": "Applikationer",
"account": "Konto",
"federatedIdentity": "Federerad identitet",
"authenticator": "Autentiserare",
"sessions": "Sessioner",
"log": "Logg",
"application": "Applikation",
"availablePermissions": "Tillgängliga rättigheter",
"grantedPermissions": "Beviljade rättigheter",
"grantedPersonalInfo": "Medgiven personlig information",
"additionalGrants": "Ytterligare medgivanden",
"action": "Åtgärd",
"inResource": "i",
"fullAccess": "Fullständig åtkomst",
"offlineToken": "Offline token",
"revoke": "Upphäv rättighet",
"configureAuthenticators": "Konfigurerade autentiserare",
"mobile": "Mobil",
"totpStep1":
'Installera <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> eller Google Authenticator på din enhet. Båda applikationerna finns tillgängliga på <a href="https://play.google.com">Google Play</a> och Apple App Store.',
"totpStep2": "Öppna applikationen och skanna streckkoden eller skriv i nyckeln.",
"totpStep3": "Fyll i engångskoden som tillhandahålls av applikationen och klicka på Spara för att avsluta inställningarna.",
"missingUsernameMessage": "Vänligen ange användarnamn.",
"missingFirstNameMessage": "Vänligen ange förnamn.",
"invalidEmailMessage": "Ogiltig e-postadress.",
"missingLastNameMessage": "Vänligen ange efternamn.",
"missingEmailMessage": "Vänligen ange e-post.",
"missingPasswordMessage": "Vänligen ange lösenord.",
"notMatchPasswordMessage": "Lösenorden matchar inte.",
"missingTotpMessage": "Vänligen ange autentiseringskoden.",
"invalidPasswordExistingMessage": "Det nuvarande lösenordet är ogiltigt.",
"invalidPasswordConfirmMessage": "Lösenordsbekräftelsen matchar inte.",
"invalidTotpMessage": "Autentiseringskoden är ogiltig.",
"usernameExistsMessage": "Användarnamnet finns redan.",
"emailExistsMessage": "E-posten finns redan.",
"readOnlyUserMessage": "Du kan inte uppdatera ditt konto eftersom det är skrivskyddat.",
"readOnlyPasswordMessage": "Du kan inte uppdatera ditt lösenord eftersom ditt konto är skrivskyddat.",
"successTotpMessage": "Mobilautentiseraren är inställd.",
"successTotpRemovedMessage": "Mobilautentiseraren är borttagen.",
"successGrantRevokedMessage": "Upphävandet av rättigheten lyckades.",
"accountUpdatedMessage": "Ditt konto har uppdaterats.",
"accountPasswordUpdatedMessage": "Ditt lösenord har uppdaterats.",
"missingIdentityProviderMessage": "Identitetsleverantör är inte angiven.",
"invalidFederatedIdentityActionMessage": "Åtgärden är ogiltig eller saknas.",
"identityProviderNotFoundMessage": "Angiven identitetsleverantör hittas inte.",
"federatedIdentityLinkNotActiveMessage": "Den här identiteten är inte längre aktiv.",
"federatedIdentityRemovingLastProviderMessage": "Du kan inte ta bort senaste federerade identiteten eftersom du inte har ett lösenord.",
"identityProviderRedirectErrorMessage": "Misslyckades med att omdirigera till identitetsleverantör.",
"identityProviderRemovedMessage": "Borttagningen av identitetsleverantören lyckades.",
"identityProviderAlreadyLinkedMessage": "Den federerade identiteten som returnerades av {0} är redan länkad till en annan användare.",
"staleCodeAccountMessage": "Sidan har upphört att gälla. Vänligen försök igen.",
"consentDenied": "Samtycket förnekades.",
"accountDisabledMessage": "Kontot är inaktiverat, kontakta administratör.",
"accountTemporarilyDisabledMessage": "Kontot är tillfälligt inaktiverat, kontakta administratör eller försök igen senare.",
"invalidPasswordMinLengthMessage": "Ogiltigt lösenord. Minsta längd är {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} små bokstäver.",
"invalidPasswordMinDigitsMessage": "Ogiltigt lösenord: måste innehålla minst {0} siffror.",
"invalidPasswordMinUpperCaseCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} stora bokstäver.",
"invalidPasswordMinSpecialCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} specialtecken.",
"invalidPasswordNotUsernameMessage": "Ogiltigt lösenord: Får inte vara samma som användarnamnet.",
"invalidPasswordRegexPatternMessage": "Ogiltigt lösenord: matchar inte kravet för lösenordsmönster.",
"invalidPasswordHistoryMessage": "Ogiltigt lösenord: Får inte vara samma som de senaste {0} lösenorden.",
"invalidPasswordGenericMessage": "Ogiltigt lösenord: Det nya lösenordet stämmer inte med lösenordspolicyn."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,309 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "Kaydet",
"doCancel": "İptal",
"doLogOutAllSessions": "Tüm Oturumları Kapat",
"doRemove": "Sil",
"doAdd": "Ekle",
"doSignOut": ıkış",
"doLogIn": "Oturum aç",
"doLink": "Bağlantı",
"editAccountHtmlTitle": "Hesabım",
"personalInfoHtmlTitle": "Kişisel bilgi",
"federatedIdentitiesHtmlTitle": "Değiştirilen Kimlikler",
"accountLogHtmlTitle": "Kullanıcı Logları",
"changePasswordHtmlTitle": "Şifre Değiştirme",
"deviceActivityHtmlTitle": "Cihaz Etkinliği",
"sessionsHtmlTitle": "Oturum",
"accountManagementTitle": "Keycloak Kullanıcı Hesabı Yönetimi",
"authenticatorTitle": "Kimlik Doğrulama",
"applicationsHtmlTitle": "Uygulama",
"linkedAccountsHtmlTitle": "Bağlantılı Hesaplar",
"accountManagementWelcomeMessage": "Keycloak Hesap Yönetimine Hoş Geldiniz",
"personalInfoIntroMessage": "Temel bilgilerinizi yönetin",
"accountSecurityTitle": "Hesap Güvenliği",
"accountSecurityIntroMessage": "Şifrenizi ve hesap erişiminizi kontrol edin",
"applicationsIntroMessage": "Hesabınıza erişmek için uygulama izninizi takip edin ve yönetin",
"resourceIntroMessage": "Kaynaklarınızı ekip üyeleri arasında paylaşın",
"passwordLastUpdateMessage": "Şifreniz güncellendi",
"updatePasswordTitle": "Şifre güncelle",
"updatePasswordMessageTitle": "Güçlü bir şifre seçtiğinizden emin olun",
"updatePasswordMessage":
"Güçlü bir şifre, sayılar, harfler ve sembollerin karışımından oluşmalıdır. Tahmin etmesi zor ve gerçek bir kelimeye benzemeyen şifre sadece bu hesap için kullanılır.",
"personalSubTitle": "Kişisel Bilgileriniz",
"personalSubMessage": "Bu temel bilgileri yönetin: adınız, soyadınız ve e-posta adresiniz",
"authenticatorCode": "Kimlik Doğrulama Kodu",
"email": "E-Mail",
"firstName": "Ad",
"givenName": "Ad",
"fullName": "Ad Soyad",
"lastName": "Soyad",
"familyName": "Soyad",
"password": "Şifre",
"currentPassword": "Şimdiki Şifre",
"passwordConfirm": "Şifre Doğrulama",
"passwordNew": "Yeni Şifre",
"username": "Kullanıcı Adı",
"address": "Adres",
"street": "Cadde",
"region": "Bölge",
"postal_code": "Posta Kodu",
"locality": "Şehir",
"country": "Ülke",
"emailVerified": "E-Mail Doğrulandı",
"gssDelegationCredential": "GSS Yetki Bilgisi",
"profileScopeConsentText": "Kullanıcı profili",
"emailScopeConsentText": "Email adresi",
"addressScopeConsentText": "Adres",
"phoneScopeConsentText": "Telefon numarası",
"offlineAccessScopeConsentText": "Çevrimdışı Erişim",
"samlRoleListScopeConsentText": "Rollerim",
"rolesScopeConsentText": "Kullanıcı rolleri",
"role_admin": "Admin",
"role_realm-admin": "Realm Admin",
"role_create-realm": "Realm Oluştur",
"role_view-realm": "Realm görüntüle",
"role_view-users": "Kullanıcıları görüntüle",
"role_view-applications": "Uygulamaları görüntüle",
"role_view-clients": "İstemci görüntüle",
"role_view-events": "Olay görüntüle",
"role_view-identity-providers": "Kimlik Sağlayıcılar",
"role_manage-realm": "Realm yönet",
"role_manage-users": "Kullanıcıları yönet",
"role_manage-applications": "Uygulamaları yönet",
"role_manage-identity-providers": "Kimlik Sağlayıcıları Yönet",
"role_manage-clients": "İstemci yönet",
"role_manage-events": "Olay yönet",
"role_view-profile": "Profilleri görüntüle",
"role_manage-account": "Profilleri Yönet",
"role_manage-account-links": "Profil bağlantılarını yönet",
"role_read-token": "Token oku",
"role_offline-access": "Çevirimdışı Yetki",
"role_uma_authorization": "İzinleri Al",
"client_account": "Müşteri Hesabı",
"client_security-admin-console": "Güvenlik Yönetici Konsolu",
"client_admin-cli": "Admin CLI",
"client_realm-management": "Realm-Management",
"client_broker": "Broker",
"requiredFields": "Zorunlu Alanlar",
"allFieldsRequired": "Tüm Alanlar Zorunlu",
"backToApplication": "&laquo; Uygulamaya Dön",
"backTo": "Geri Dön {0}",
"date": "Gün",
"event": "Olay",
"ip": "IP",
"client": "İstemci",
"clients": "İstemciler",
"details": "Detaylar",
"started": "Başlangıç Tarihi",
"lastAccess": "Son Erişim Tarihi",
"expires": "Son Kullanma Tarihi",
"applications": "Uygulama",
"account": "Hesap",
"federatedIdentity": "Federal Kimlik",
"authenticator": "Kimlik Doğrulama",
"device-activity": "Cihaz Etkinliği",
"sessions": "Oturum",
"log": "Log",
"application": "Uygulama",
"availablePermissions": "Kullanılabilir İzinler",
"availableRoles": "Kullanılabilir Roller",
"grantedPermissions": "Verilen İzinler",
"grantedPersonalInfo": "İzin Verilen Kişisel Bilgiler",
"additionalGrants": "Ek İzinler",
"action": "Aksiyon",
"inResource": "Kaynak",
"fullAccess": "Tam Yetki",
"offlineToken": "Çevirimdışı-Token",
"revoke": "İzni İptal et",
"configureAuthenticators": "Çoklu Kimlik Doğrulama",
"mobile": "Mobil",
"totpStep1": "Akıllı Telefonunuza aşağıdaki uygulamalardan birini yükleyin:",
"totpStep2": "Uygulamayıın ve barkodu okutun.",
"totpStep3": "Uygulama tarafından oluşturulan tek seferlik kodu girin ve Kaydet'i tıklayın.",
"totpManualStep2": "Uygulamayıın ve aşağıdaki anahtarı girin.",
"totpManualStep3": "Bunları uygulama için özelleştirebilirseniz aşağıdaki yapılandırma değerlerini kullanın:",
"totpUnableToScan": "Barkodu tarayamıyor musunuz?",
"totpScanBarcode": "Barkod Tara?",
"totp.totp": "Zaman bazlı (time-based)",
"totp.hotp": "Sayaç tabanlı (counter-based)",
"totpType": "Tip",
"totpAlgorithm": "Algoritma",
"totpDigits": "Basamak",
"totpInterval": "Aralık",
"totpCounter": "Sayaç",
"missingUsernameMessage": "Lütfen bir kullanıcı adı giriniz.",
"missingFirstNameMessage": "Lütfen bir ad girin.",
"invalidEmailMessage": "Geçersiz e-posta adresi.",
"missingLastNameMessage": "Lütfen bir soyadı giriniz.",
"missingEmailMessage": "Lütfen bir e-mail adresi giriniz.",
"missingPasswordMessage": "Lütfen bir şifre giriniz.",
"notMatchPasswordMessage": "Şifreler aynı değil.",
"missingTotpMessage": "Lütfen tek seferlik kodu girin.",
"invalidPasswordExistingMessage": "Mevcut şifre geçersiz.",
"invalidPasswordConfirmMessage": "Şifre onayı aynı değil.",
"invalidTotpMessage": "Geçersiz tek seferlik kod.",
"usernameExistsMessage": "Kullanıcı adı zaten mevcut.",
"emailExistsMessage": "E-posta adresi zaten mevcut.",
"readOnlyUserMessage": "Yazma korumalı olduğundan kullanıcı hesabınızı değiştiremezsiniz.",
"readOnlyUsernameMessage": "Yazma korumalı olduğundan kullanıcı adınızı değiştiremezsiniz.",
"readOnlyPasswordMessage": "Yazma korumalı olduğundan şifrenizi değiştiremezsiniz.",
"successTotpMessage": "Çoklu kimlik doğrulaması başarıyla yapılandırıldı.",
"successTotpRemovedMessage": "Çoklu kimlik doğrulama başarıyla kaldırıldı.",
"successGrantRevokedMessage": "İzin başarıyla iptal edildi.",
"accountUpdatedMessage": "Kullanıcı hesabınız güncellendi.",
"accountPasswordUpdatedMessage": "Şifreniz güncellendi.",
"missingIdentityProviderMessage": "Kimlik Sağlayıcısı belirtilmemiş.",
"invalidFederatedIdentityActionMessage": "Geçersiz veya eksik eylem.",
"identityProviderNotFoundMessage": "Belirtilen Kimlik Sağlayıcı bulunamadı.",
"federatedIdentityLinkNotActiveMessage": "Bu kimlik artık aktif değil.",
"federatedIdentityRemovingLastProviderMessage": "Şifreniz olmadığı için son girişi kaldıramazsınız.",
"identityProviderRedirectErrorMessage": "Kimlik sağlayıcıya iletilirken hata oluştu.",
"identityProviderRemovedMessage": "Kimlik Sağlayıcısı başarıyla kaldırıldı.",
"identityProviderAlreadyLinkedMessage": "Değiştirilmiş {0} kimliği başka bir kullanıcıya atanmış.",
"staleCodeAccountMessage": "Bu sayfa artık geçerli değil, lütfen tekrar deneyin.",
"consentDenied": "Onay reddedildi.",
"accountDisabledMessage": "Hesabınız kilitlendi, lütfen yöneticiyle iletişime geçin.",
"accountTemporarilyDisabledMessage": "Hesabınız geçici olarak kilitlendi, lütfen yöneticiyle iletişime geçin veya daha sonra tekrar deneyin.",
"invalidPasswordMinLengthMessage": "Geçersiz Şifre: En az {0} karakter uzunluğunda olmalı.",
"invalidPasswordMinLowerCaseCharsMessage": "Geçersiz Şifre : En az {0} küçük harf içermelidir.",
"invalidPasswordMinDigitsMessage": "Geçersiz Şifre: En az {0} sayı(lar) içermelidir.",
"invalidPasswordMinUpperCaseCharsMessage": "Geçersiz Şifre: En az {0} büyük harf içermelidir.",
"invalidPasswordMinSpecialCharsMessage": "Geçersiz Şifre: En az {0} özel karakter içermelidir.",
"invalidPasswordNotUsernameMessage": "Geçersiz Şifre: Kullanıcı adıyla aynı olamaz.",
"invalidPasswordRegexPatternMessage": "Geçersiz Şifre: Regex Patternine uygun değil.",
"invalidPasswordHistoryMessage": "Geçersiz Şifre: Son {0} şifreden biri olamaz.",
"invalidPasswordBlacklistedMessage": "Geçersiz Şifre: Şifre bloklanmış şifreler listesindedir (kara liste).",
"invalidPasswordGenericMessge": "Geçersiz Şifre: Yeni şifre, şifre kurallarını ihlal ediyor.",
"myResources": "Kaynaklarım",
"myResourcesSub": "Kaynaklarım",
"doDeny": "Reddet",
"doRevoke": "Geri al",
"doApprove": "Onayla",
"doRemoveSharing": "Paylaşımı Kaldır",
"doRemoveRequest": "İsteği Kaldır",
"peopleAccessResource": "Bu kaynağa erişimi olan kişiler",
"resourceManagedPolicies": "Bu kaynağa erişim izni veren izinler",
"resourceNoPermissionsGrantingAccess": "Bu kaynağa erişim izni verilmeyen izin yok",
"anyAction": "Herhangi bir eylem",
"description": "Açıklama",
"name": "İsim",
"scopes": "Kapsam",
"resource": "Kaynak",
"user": "Kullanıcı",
"peopleSharingThisResource": "Bu kaynağı paylaşan kullanıcılar",
"shareWithOthers": "Başkalarıyla paylaş",
"needMyApproval": "Onayım gerekli",
"requestsWaitingApproval": "Talepleriniz onay bekliyor",
"icon": "Icon",
"requestor": "Talep eden",
"owner": "Sahip",
"resourcesSharedWithMe": "Kaynaklar benimle paylaşıldı",
"permissionRequestion": "İzin Talepleri",
"permission": "İzin",
"shares": "Paylaşım(lar)",
"locale_ca": "Katalanca",
"locale_de": "Almanca",
"locale_en": "İngilizce",
"locale_es": "İspanyolca",
"locale_fr": "Fransızca",
"locale_it": "İtalyanca",
"locale_ja": "Japonca",
"locale_nl": "Felemenkçe",
"locale_no": "Norveçce",
"locale_pl": "Lehçe",
"locale_pt_BR": "Portekizce",
"locale_pt-BR": "Portekizce",
"locale_ru": "Rusça",
"locale_lt": "Litvanca",
"locale_zh-CN": "Çince",
"locale_sk": "Slovakça",
"locale_sv": "İsveççe",
"locale_tr": "Türkçe",
"applicaitonName": "İsim",
"applicationType": "Uygulama Tipi",
"applicationInUse": "Yalnızca uygulama içi kullanım",
"clearAllFilter": "Tüm filtreleri temizle",
"activeFilters": "Aktif filtreler",
"filterByName": "İsme Göre Filtrele ...",
"allApps": "Bütün uygulamalar",
"internalApps": "İç uygulamalar",
"thirdpartyApps": "Üçüncü parti uygulamalar",
"appResults": "Sonuçlar",
"authorizedProvider": "Yetkili Tedarikçi",
"authorizedProviderMessage": "Yetkili Sağlayıcılar hesabınızla bağlantılı",
"identityProvider": "Kimlik Sağlayıcısı",
"identityProviderMessage": "Hesabınızı yapılandırdığınız kimlik sağlayıcılarıyla bağlamak için",
"socialLogin": "Sosyal Giriş",
"userDefined": "Kullanıcı tanımlı",
"removeAccess": "Erişimi Kaldır",
"removeAccessMessage": "Bu uygulama hesabını kullanmak istiyorsanız tekrar erişim vermeniz gerekir.",
"authenticatorStatusMessage": "İki faktörlü kimlik doğrulama aktif",
"authenticatorFinishSetUpTitle": "İki Faktörlü Doğrulama",
"authenticatorFinishSetUpMessage": "Keycloak hesabınızda her oturum açtığınızda, iki faktörlü bir doğrulama kodu girmeniz istenecektir.",
"authenticatorSubTitle": "İki Faktörlü Kimlik Doğrulamayı Ayarlama",
"authenticatorSubMessage":
"Hesabınızın güvenliğini artırmak için mevcut iki faktörlü kimlik doğrulama yöntemlerinden en az birini etkinleştirin.",
"authenticatorMobileTitle": "Mobil Kimlik Doğrulayıcı",
"authenticatorMobileMessage": "Doğrulama kodlarını iki faktörlü kimlik doğrulama olarak almak için mobil Doğrulayıcı'yı kullanın.",
"authenticatorMobileFinishSetUpMessage": "Doğrulayıcı, telefonunuza bağlı.",
"authenticatorActionSetup": "Kur",
"authenticatorSMSTitle": "SMS Kodu",
"authenticatorSMSMessage": "Keycloak, doğrulama kodunu telefonunuza iki faktörlü kimlik doğrulaması olarak gönderecektir.",
"authenticatorSMSFinishSetUpMessage": "Kısa mesajlar gönderilir",
"authenticatorDefaultStatus": "Varsayılan",
"authenticatorChangePhone": "Telefon Numarasını Değiştir",
"authenticatorBackupCodesTitle": "Yedekleme Kodları",
"authenticatorBackupCodesMessage": "8 haneli yedek kodlarınızı alın",
"authenticatorBackupCodesFinishSetUpMessage": "Şu anda 12 haneli yedek kod oluşturuldu. Her biri bir kez kullanılabilir.",
"authenticatorMobileSetupTitle": "Mobil Kimlik Doğrulama Kurulumu",
"smscodeIntroMessage": "Telefon numaranızı girin ve telefonunuza bir doğrulama kodu gönderilecektir.",
"mobileSetupStep1": "Telefonunuza bir kimlik doğrulama uygulaması yükleyin. Burada listelenen uygulamalar desteklenmektedir.",
"mobileSetupStep2": "Uygulamayıın ve barkodu tarayın.",
"mobileSetupStep3": "Uygulama tarafından sağlanan tek seferlik kodu girin ve kurulumu tamamlamak için Kaydet'e tıklayın.",
"scanBarCode": "Barkodu taramak ister misiniz?",
"enterBarCode": "Tek seferlik kodu girin",
"doCopy": "Kopyala",
"doFinish": "Bitir",
"authenticatorSMSCodeSetupTitle": "SMS Kodu Kurulumu",
"chooseYourCountry": "Ülkenizi seçin",
"enterYourPhoneNumber": "Telefon numaranızı girin",
"sendVerficationCode": "Doğrulama kodu Gönder",
"enterYourVerficationCode": "Onaylama kodunu girin",
"authenticatorBackupCodesSetupTitle": "Yedekleme Kodları Kurulumu",
"backupcodesIntroMessage":
"Telefonunuza erişimi kaybederseniz, yine de yedek kodlar aracılığıyla hesabınıza giriş yapabilirsiniz. Onları güvenli ve erişilebilir bir yerde saklayın.",
"realmName": "Realm",
"doDownload": "İndir",
"doPrint": "Yazdır",
"backupCodesTips-1": "Her yedek kod bir kez kullanılabilir.",
"backupCodesTips-2": "Bu kodlar üzerinde oluşturuldu",
"generateNewBackupCodes": "Yeni Yedekleme Kodları Oluştur",
"backupCodesTips-3": "Yeni yedek kodlar oluşturduğunuzda, mevcut kodlar artık çalışmayacaktır.",
"backtoAuthenticatorPage": "Kimlik Doğrulayıcı Sayfasına Geri Dön",
"resources": "Kaynaklar",
"sharedwithMe": "Benimle paylaştı",
"share": "Paylaşım",
"sharedwith": "İle paylaştı",
"accessPermissions": "Erişim İzinleri",
"permissionRequests": "İzin İstekleri",
"approve": "Onayla",
"approveAll": "Tümünü onayla",
"people": "İnsanlar",
"perPage": "Sayfa başına",
"currentPage": "Geçerli sayfa",
"sharetheResource": "Kaynağı paylaş",
"group": "Grup",
"selectPermission": "İzin Seç",
"addPeople": "Kaynağınızı paylaşmak için kullanıcı ekleyin",
"addTeam": "Kaynağınızı paylaşmak için ekip ekleyin",
"myPermissions": "İzinlerim",
"waitingforApproval": "Onay bekleniyor",
"anyPermission": "Herhangi bir izin"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,155 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"doSave": "保存",
"doCancel": "取消",
"doLogOutAllSessions": "登出所有会话",
"doRemove": "删除",
"doAdd": "添加",
"doSignOut": "登出",
"editAccountHtmlTitle": "编辑账户",
"federatedIdentitiesHtmlTitle": "链接的身份",
"accountLogHtmlTitle": "账户日志",
"changePasswordHtmlTitle": "更改密码",
"sessionsHtmlTitle": "会话",
"accountManagementTitle": "Keycloak账户管理",
"authenticatorTitle": "认证者",
"applicationsHtmlTitle": "应用",
"authenticatorCode": "一次性认证码",
"email": "电子邮件",
"firstName": "名",
"givenName": "姓",
"fullName": "全名",
"lastName": "姓",
"familyName": "姓",
"password": "密码",
"passwordConfirm": "确认",
"passwordNew": "新密码",
"username": "用户名",
"address": "地址",
"street": "街道",
"locality": "城市住所",
"region": "省,自治区,直辖市",
"postal_code": "邮政编码",
"country": "国家",
"emailVerified": "验证过的Email",
"gssDelegationCredential": "GSS Delegation Credential",
"role_admin": "管理员",
"role_realm-admin": "域管理员",
"role_create-realm": "创建域",
"role_view-realm": "查看域",
"role_view-users": "查看用户",
"role_view-applications": "查看应用",
"role_view-clients": "查看客户",
"role_view-events": "查看事件",
"role_view-identity-providers": "查看身份提供者",
"role_manage-realm": "管理域",
"role_manage-users": "管理用户",
"role_manage-applications": "管理应用",
"role_manage-identity-providers": "管理身份提供者",
"role_manage-clients": "管理客户",
"role_manage-events": "管理事件",
"role_view-profile": "查看用户信息",
"role_manage-account": "管理账户",
"role_read-token": "读取 token",
"role_offline-access": "离线访问",
"role_uma_authorization": "获取授权",
"client_account": "账户",
"client_security-admin-console": "安全管理终端",
"client_admin-cli": "管理命令行",
"client_realm-management": "域管理",
"client_broker": "代理",
"requiredFields": "必填项",
"allFieldsRequired": "所有项必填",
"backToApplication": "« 回到应用",
"backTo": "回到 {0}",
"date": "日期",
"event": "事件",
"ip": "IP",
"client": "客户端",
"clients": "客户端",
"details": "详情",
"started": "开始",
"lastAccess": "最后一次访问",
"expires": "过期时间",
"applications": "应用",
"account": "账户",
"federatedIdentity": "关联身份",
"authenticator": "认证方",
"sessions": "会话",
"log": "日志",
"application": "应用",
"availablePermissions": "可用权限",
"grantedPermissions": "授予权限",
"grantedPersonalInfo": "授权的个人信息",
"additionalGrants": "可授予的权限",
"action": "操作",
"inResource": "in",
"fullAccess": "所有权限",
"offlineToken": "离线 token",
"revoke": "收回授权",
"configureAuthenticators": "配置的认证者",
"mobile": "手机",
"totpStep1":
'在你的设备上安装 <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> 或者 Google Authenticator.两个应用可以从 <a href="https://play.google.com">Google Play</a> 和 Apple App Store下载。',
"totpStep2": "打开应用扫描二维码输入验证码",
"totpStep3": "输入应用提供的一次性验证码单击保存",
"missingUsernameMessage": "请指定用户名",
"missingFirstNameMessage": "请指定名",
"invalidEmailMessage": "无效的电子邮箱地址",
"missingLastNameMessage": "请指定姓",
"missingEmailMessage": "请指定邮件地址",
"missingPasswordMessage": "请输入密码",
"notMatchPasswordMessage": "密码不匹配",
"missingTotpMessage": "请指定认证者代码",
"invalidPasswordExistingMessage": "无效的旧密码",
"invalidPasswordConfirmMessage": "确认密码不相符",
"invalidTotpMessage": "无效的认证码",
"usernameExistsMessage": "用户名已经存在",
"emailExistsMessage": "电子邮箱已经存在",
"readOnlyUserMessage": "无法修改账户,因为它是只读的。",
"readOnlyPasswordMessage": "不可以更该账户因为它是只读的。",
"successTotpMessage": "手机认证者配置完毕",
"successTotpRemovedMessage": "手机认证者已删除",
"successGrantRevokedMessage": "授权成功回收",
"accountUpdatedMessage": "您的账户已经更新",
"accountPasswordUpdatedMessage": "您的密码已经修改",
"missingIdentityProviderMessage": "身份提供者未指定",
"invalidFederatedIdentityActionMessage": "无效或者缺少操作",
"identityProviderNotFoundMessage": "指定的身份提供者未找到",
"federatedIdentityLinkNotActiveMessage": "这个身份不再使用了。",
"federatedIdentityRemovingLastProviderMessage": "你不可以移除最后一个身份提供者因为你没有设置密码",
"identityProviderRedirectErrorMessage": "尝试重定向到身份提供商失败",
"identityProviderRemovedMessage": "身份提供商成功删除",
"identityProviderAlreadyLinkedMessage": "链接的身份 {0} 已经连接到已有用户。",
"staleCodeAccountMessage": "页面过期。请再试一次。",
"consentDenied": "不同意",
"accountDisabledMessage": "账户已经关闭,请联系管理员",
"accountTemporarilyDisabledMessage": "账户暂时关闭,请联系管理员或稍后再试。",
"invalidPasswordMinLengthMessage": "无效的密码:最短长度 {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "无效的密码: 至少包含 {0} 小写字母。",
"invalidPasswordMinDigitsMessage": "无效的密码: 至少包含 {0} 数字。",
"invalidPasswordMinUpperCaseCharsMessage": "无效的密码: 至少包含 {0} 大写字母",
"invalidPasswordMinSpecialCharsMessage": "无效的密码: 至少包含 {0} 个特殊字符",
"invalidPasswordNotUsernameMessage": "无效的密码: 不能与用户名相同",
"invalidPasswordRegexPatternMessage": "无效的密码: 无法与正则表达式匹配",
"invalidPasswordHistoryMessage": "无效的密码: 不能与之前的{0} 个旧密码相同",
"locale_ca": "Català",
"locale_de": "Deutsch",
"locale_en": "English",
"locale_es": "Español",
"locale_fr": "Français",
"locale_it": "Italian",
"locale_ja": "日本語",
"locale_nl": "Nederlands",
"locale_no": "Norsk",
"locale_lt": "Lietuvių",
"locale_pt-BR": "Português (Brasil)",
"locale_ru": "Русский",
"locale_zh-CN": "中文简体"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,17 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,19 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: muss mindestens {0} Zeichen beinhalten.",
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.",
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: muss mindestens {0} Ziffern beinhalten.",
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Großbuchstaben beinhalten.",
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: muss mindestens {0} Sonderzeichen beinhalten.",
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: darf nicht identisch mit dem Benutzernamen sein.",
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: stimmt nicht mit Regex-Muster überein.",
"invalidPasswordHistoryMessage": "Ungültiges Passwort: darf nicht identisch mit einem der letzten {0} Passwörter sein.",
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Passwort ist zu bekannt und auf der schwarzen Liste.",
"invalidPasswordGenericMessage": "Ungültiges Passwort: neues Passwort erfüllt die Passwort-Anforderungen nicht."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,44 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
"invalidPasswordGenericMessage": "Invalid password: new password does not match password policies.",
"ldapErrorInvalidCustomFilter": 'Custom configured LDAP filter does not start with "(" or does not end with ")".',
"ldapErrorConnectionTimeoutNotNumber": "Connection Timeout must be a number",
"ldapErrorReadTimeoutNotNumber": "Read Timeout must be a number",
"ldapErrorMissingClientId": "Client ID needs to be provided in config when Realm Roles Mapping is not used.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Not possible to preserve group inheritance and use UID membership type together.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Can not set write only when LDAP provider mode is not WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Can not set write-only and read-only together",
"ldapErrorCantEnableStartTlsAndConnectionPooling": "Can not enable both StartTLS and connection pooling.",
"ldapErrorCantEnableUnsyncedAndImportOff": "Can not disable Importing users when LDAP provider mode is UNSYNCED",
"ldapErrorMissingGroupsPathGroup": "Groups path group does not exist - please create the group on specified path first",
"clientRedirectURIsFragmentError": "Redirect URIs must not contain an URI fragment",
"clientRootURLFragmentError": "Root URL must not contain an URL fragment",
"clientRootURLIllegalSchemeError": "Root URL uses an illegal scheme",
"clientBaseURLIllegalSchemeError": "Base URL uses an illegal scheme",
"clientRedirectURIsIllegalSchemeError": "A redirect URI uses an illegal scheme",
"clientBaseURLInvalid": "Base URL is not a valid URL",
"clientRootURLInvalid": "Root URL is not a valid URL",
"clientRedirectURIsInvalid": "A redirect URI is not a valid URI",
"pairwiseMalformedClientRedirectURI": "Client contained an invalid redirect URI.",
"pairwiseClientRedirectURIsMissingHost": "Client redirect URIs must contain a valid host component.",
"pairwiseClientRedirectURIsMultipleHosts":
"Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.",
"pairwiseMalformedSectorIdentifierURI": "Malformed Sector Identifier URI.",
"pairwiseFailedToGetRedirectURIs": "Failed to get redirect URIs from the Sector Identifier URI.",
"pairwiseRedirectURIsMismatch": "Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,17 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,17 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordMinLengthMessage": "Mot de passe invalide : longueur minimale requise de {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.",
"invalidPasswordMinDigitsMessage": "Mot de passe invalide : doit contenir au moins {0} chiffre(s).",
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.",
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide : doit contenir au moins {0} caractère(s) spéciaux.",
"invalidPasswordNotUsernameMessage": "Mot de passe invalide : ne doit pas être identique au nom d'utilisateur.",
"invalidPasswordRegexPatternMessage": "Mot de passe invalide : ne valide pas l'expression rationnelle.",
"invalidPasswordHistoryMessage": "Mot de passe invalide : ne doit pas être égal aux {0} derniers mot de passe."
};
export default messages;
/* spell-checker: enable */

View File

@ -1,8 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {};
export default messages;
/* spell-checker: enable */

View File

@ -1,37 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の大文字を含む必要があります。",
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
"ldapErrorInvalidCustomFilter": "LDAPフィルターのカスタム設定が、「(」から開始または「)」で終了となっていません。",
"ldapErrorConnectionTimeoutNotNumber": "接続タイムアウトは数字でなければなりません",
"ldapErrorReadTimeoutNotNumber": "読み取りタイムアウトは数字でなければなりません",
"ldapErrorMissingClientId": "レルムロール・マッピングを使用しない場合は、クライアントIDは設定内で提供される必要があります。",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType":
"グループの継承を維持することと、UIDメンバーシップ・タイプを使用することは同時にできません。",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "LDAPプロバイダー・モードがWRITABLEではない場合は、write onlyを設定することはできません。",
"ldapErrorCantWriteOnlyAndReadOnly": "write-onlyとread-onlyを一緒に設定することはできません。",
"ldapErrorCantEnableStartTlsAndConnectionPooling": "StartTLSと接続プーリングの両方を有効にできません。",
"clientRedirectURIsFragmentError": "リダイレクトURIにURIフラグメントを含めることはできません。",
"clientRootURLFragmentError": "ルートURLにURLフラグメントを含めることはできません。",
"pairwiseMalformedClientRedirectURI": "クライアントに無効なリダイレクトURIが含まれていました。",
"pairwiseClientRedirectURIsMissingHost": "クライアントのリダイレクトURIには有効なホスト・コンポーネントが含まれている必要があります。",
"pairwiseClientRedirectURIsMultipleHosts":
"設定されたセレクター識別子URIがない場合は、クライアントのリダイレクトURIは複数のホスト・コンポーネントを含むことはできません。",
"pairwiseMalformedSectorIdentifierURI": "不正なセレクター識別子URIです。",
"pairwiseFailedToGetRedirectURIs": "セクター識別子URIからリダイレクトURIを取得できませんでした。",
"pairwiseRedirectURIsMismatch": "クライアントのリダイレクトURIは、セクター識別子URIからフェッチされたリダイレクトURIと一致しません。"
};
export default messages;
/* spell-checker: enable */

View File

@ -1,31 +0,0 @@
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
const messages = {
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais.",
"ldapErrorInvalidCustomFilter": 'Sukonfigūruotas LDAP filtras neprasideda "(" ir nesibaigia ")" simboliais.',
"ldapErrorMissingClientId": "Privaloma nurodyti kliento ID kai srities rolių susiejimas nėra nenaudojamas.",
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Grupių paveldėjimo ir UID narystės tipas kartu negali būti naudojami.",
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Negalima nustatyti rašymo rėžimo kuomet LDAP teikėjo rėžimas ne WRITABLE",
"ldapErrorCantWriteOnlyAndReadOnly": "Negalima nustatyti tik rašyti ir tik skaityti kartu",
"clientRedirectURIsFragmentError": "Nurodykite URI fragmentą, kurio negali būti peradresuojamuose URI adresuose",
"clientRootURLFragmentError": "Nurodykite URL fragmentą, kurio negali būti šakniniame URL adrese",
"pairwiseMalformedClientRedirectURI": "Klientas pateikė neteisingą nukreipimo nuorodą.",
"pairwiseClientRedirectURIsMissingHost": "Kliento nukreipimo nuorodos privalo būti nurodytos su serverio vardo komponentu.",
"pairwiseClientRedirectURIsMultipleHosts":
"Kuomet nesukonfigūruotas sektoriaus identifikatoriaus URL, kliento nukreipimo nuorodos privalo talpinti ne daugiau kaip vieną skirtingą serverio vardo komponentą.",
"pairwiseMalformedSectorIdentifierURI": "Neteisinga sektoriaus identifikatoriaus URI.",
"pairwiseFailedToGetRedirectURIs": "Nepavyko gauti nukreipimo nuorodų iš sektoriaus identifikatoriaus URI.",
"pairwiseRedirectURIsMismatch": "Kliento nukreipimo nuoroda neatitinka nukreipimo nuorodų iš sektoriaus identifikatoriaus URI."
};
export default messages;
/* spell-checker: enable */

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