From 5c037c7d8df43629ae6bb1c59dfaed3fbb1442a7 Mon Sep 17 00:00:00 2001 From: Dmitriy Sim Date: Thu, 8 Apr 2021 12:56:47 +0300 Subject: [PATCH] initial implementation --- Dockerfile | 3 + README.md | 45 +- build.sh | 63 + juice/account/resources/css/account.css | 277 ++ .../resources/img/icon-sidebar-active.png | Bin 0 -> 202 bytes juice/account/resources/img/keycloak-logo.png | Bin 0 -> 5213 bytes juice/account/resources/img/logo.png | Bin 0 -> 4156 bytes juice/account/theme.properties | 14 + juice/admin/resources/css/styles.css | 480 +++ juice/admin/resources/img/keyclok-logo.png | Bin 0 -> 5268 bytes juice/admin/resources/img/keyclok-logo.svg | 194 + juice/admin/resources/img/select-arrow.png | Bin 0 -> 1154 bytes juice/admin/theme.properties | 5 + juice/email/theme.properties | 1 + juice/login/error.ftl | 13 + juice/login/login-page-expired.ftl | 13 + juice/login/login-reset-password.ftl | 61 + juice/login/login-verify-email.ftl | 14 + juice/login/login.ftl | 122 + juice/login/messages/messages_de.properties | 35 + juice/login/messages/messages_en.properties | 33 + juice/login/register.ftl | 139 + juice/login/resources/css/styles.css | 758 ++++ juice/login/resources/fonts/Roboto-Medium.ttf | Bin 0 -> 171656 bytes .../resources/fonts/Swiss721MediumBT.ttf | Bin 0 -> 36400 bytes .../login/resources/fonts/Swiss721ThinBT.ttf | Bin 0 -> 35264 bytes juice/login/resources/img/apple.svg | 3 + juice/login/resources/img/clock.svg | 3 + juice/login/resources/img/deutsch.svg | 9 + juice/login/resources/img/english.svg | 7 + juice/login/resources/img/error.svg | 3 + juice/login/resources/img/facebook.svg | 3 + juice/login/resources/img/google.svg | 7 + juice/login/resources/img/juice-bc copy.svg | 35 + juice/login/resources/img/juice-bc.svg | 35 + juice/login/resources/img/juice.svg | 9 + juice/login/resources/img/linkedin.svg | 3 + juice/login/resources/img/locale.svg | 3 + juice/login/resources/img/twitter.svg | 3 + juice/login/resources/img/verify-email.svg | 3 + juice/login/resources/js/script.js | 5 + juice/login/template.ftl | 134 + juice/login/theme.properties | 8 + juice/welcome/index.ftl | 68 + juice/welcome/messages/messages_en.properties | 20 + juice/welcome/messages/messages_ru.properties | 20 + juice/welcome/resources/apple.svg | 3 + juice/welcome/resources/css/styles.css | 330 ++ juice/welcome/resources/facebook.svg | 3 + juice/welcome/resources/google.svg | 7 + juice/welcome/resources/js/realm.js | 3219 +++++++++++++++++ juice/welcome/resources/juice-bc.svg | 35 + juice/welcome/resources/juice.svg | 9 + juice/welcome/resources/linkedin.svg | 3 + juice/welcome/resources/twitter.svg | 3 + juice/welcome/theme.properties | 11 + restart.sh | 16 + test/url.test.js | 110 + version.ini | 1 + 59 files changed, 6400 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 build.sh create mode 100644 juice/account/resources/css/account.css create mode 100644 juice/account/resources/img/icon-sidebar-active.png create mode 100644 juice/account/resources/img/keycloak-logo.png create mode 100644 juice/account/resources/img/logo.png create mode 100644 juice/account/theme.properties create mode 100644 juice/admin/resources/css/styles.css create mode 100644 juice/admin/resources/img/keyclok-logo.png create mode 100644 juice/admin/resources/img/keyclok-logo.svg create mode 100644 juice/admin/resources/img/select-arrow.png create mode 100644 juice/admin/theme.properties create mode 100644 juice/email/theme.properties create mode 100644 juice/login/error.ftl create mode 100644 juice/login/login-page-expired.ftl create mode 100644 juice/login/login-reset-password.ftl create mode 100644 juice/login/login-verify-email.ftl create mode 100644 juice/login/login.ftl create mode 100644 juice/login/messages/messages_de.properties create mode 100644 juice/login/messages/messages_en.properties create mode 100644 juice/login/register.ftl create mode 100644 juice/login/resources/css/styles.css create mode 100644 juice/login/resources/fonts/Roboto-Medium.ttf create mode 100644 juice/login/resources/fonts/Swiss721MediumBT.ttf create mode 100644 juice/login/resources/fonts/Swiss721ThinBT.ttf create mode 100644 juice/login/resources/img/apple.svg create mode 100644 juice/login/resources/img/clock.svg create mode 100644 juice/login/resources/img/deutsch.svg create mode 100644 juice/login/resources/img/english.svg create mode 100644 juice/login/resources/img/error.svg create mode 100644 juice/login/resources/img/facebook.svg create mode 100644 juice/login/resources/img/google.svg create mode 100644 juice/login/resources/img/juice-bc copy.svg create mode 100644 juice/login/resources/img/juice-bc.svg create mode 100644 juice/login/resources/img/juice.svg create mode 100644 juice/login/resources/img/linkedin.svg create mode 100644 juice/login/resources/img/locale.svg create mode 100644 juice/login/resources/img/twitter.svg create mode 100644 juice/login/resources/img/verify-email.svg create mode 100644 juice/login/resources/js/script.js create mode 100644 juice/login/template.ftl create mode 100644 juice/login/theme.properties create mode 100644 juice/welcome/index.ftl create mode 100644 juice/welcome/messages/messages_en.properties create mode 100644 juice/welcome/messages/messages_ru.properties create mode 100644 juice/welcome/resources/apple.svg create mode 100644 juice/welcome/resources/css/styles.css create mode 100644 juice/welcome/resources/facebook.svg create mode 100644 juice/welcome/resources/google.svg create mode 100644 juice/welcome/resources/js/realm.js create mode 100644 juice/welcome/resources/juice-bc.svg create mode 100644 juice/welcome/resources/juice.svg create mode 100644 juice/welcome/resources/linkedin.svg create mode 100644 juice/welcome/resources/twitter.svg create mode 100644 juice/welcome/theme.properties create mode 100755 restart.sh create mode 100644 test/url.test.js create mode 100644 version.ini diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7559e10 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM quay.io/keycloak/keycloak:latest + +COPY ["./juice", "/opt/jboss/keycloak/themes/juice/"] \ No newline at end of file diff --git a/README.md b/README.md index 0435704..549eeb5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,45 @@ -# keycloak-themed +# Keycloak image with juice theme inside +A short project that inject juice theme into a docker image. +Juice theme should be located in `./juice` folder. + +!!!IMPORTANT!!!: IF YOU MAKE AN UPDATE, PLEASE UPDATE THE VERSION.INI FILE ACORDING TO STANDARD VERSIONING RULES (NUMBERS ONLY). + +## Docker commands + +### Build the image +Build an image with tag juice-keycloak. + +```bash +docker build -t juice-keycloak . +``` + +### Stop previously running container +```bash +docker stop juice-keycloak +``` + +### Start a container +```bash +docker run --rm -it -d \ + -p 8080:8080 \ + -e KEYCLOAK_USER=admin \ + -e KEYCLOAK_PASSWORD=admin \ + -e DB_VENDOR=postgres \ + -e DB_ADDR=172.17.0.1 \ + -e DB_DATABASE=keycloak \ + -e DB_USER=keycloak \ + -e DB_PASSWORD=keycloak \ + --name juice-keycloak \ + juice-keycloak +``` + +Linux: PGHOST=172.17.0.1 +Mac&Win: PGHOST=host.docker.internal + +## Testing +There is a file `url.test.js` that will generate a login URL. After sucessfull login, provide the redirect url from the browser as an argument for the script to acquire tokens. + +The script includes installable package listed at the top. + +Change configuration inside of the script according to your realm settings. \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..ebf78ea --- /dev/null +++ b/build.sh @@ -0,0 +1,63 @@ +#!/bin/bash +function cleanup { + if [ ! -z "${RPM_BUILD_ROOT}" ] && [ -d "${RPM_BUILD_ROOT}" ] && [ "${RPM_BUILD_ROOT}" != "./" ] + then + rm -rf "${RPM_BUILD_ROOT}" + fi +} +export SOURCE_DIR=`dirname $0` +echo "${SOURCE_DIR}" |grep -q "^\." +if [ $? -eq 0 ] +then + echo "sourcdir is ." + export SOURCE_DIR="`pwd`/" +fi +echo "${SOURCE_DIR}" |grep -q "^/" +if [ $? -ne 0 ] +then + export SOURCE_DIR="`pwd`/`dirname $0`" +fi +echo "SOURCE_DIR: ${SOURCE_DIR}" +export RELEASE_VERSION=`cat version.ini` +if [ "${docker_registry}" != "" ] || [ "${1}" == "docker-image" ] || [ "${2}" == "docker-image" ] +then + docker build -t ${docker_registry}${docker_registry_path_suffix}/juice-keycloak:${RELEASE_VERSION} -t ${docker_registry}${docker_registry_path_suffix}/juice-keycloak:latest . && + docker login --username ${docker_registry_username} --password ${docker_registry_password} ${docker_registry} && + docker push ${docker_registry}${docker_registry_path_suffix}/juice-keycloak:${RELEASE_VERSION} && + docker push ${docker_registry}${docker_registry_path_suffix}/juice-keycloak:latest + exit $? +fi + + +if [ "${BUILD_RPM}" != "" ] || [ "${1}" == "rpm" ] || [ "${2}" == "rpm" ] +then + + export _RPMBUILD_DIR="${RPMBUILD_DIR:-/tmp/rpmbuild-juice-keycloak}" + export RPM_BUILD_ROOT="${RPM_BUILD_ROOT:-/tmp/build-juice-keycloak}" + if [ ! -d "${_RPMBUILD_DIR}" ] + then + mkdir -p "${_RPMBUILD_DIR}" + mkdir -p "${_RPMBUILD_DIR}"/RPMS/noarch + mkdir -p "${_RPMBUILD_DIR}"/SOURCES + mkdir -p "${_RPMBUILD_DIR}"/SPECS + mkdir -p "${_RPMBUILD_DIR}"/SRPMS + fi + if [ ! -d "${RPM_BUILD_ROOT}" ] + then + mkdir -p "${RPM_BUILD_ROOT}" + fi + rpmbuild --target noarch -bb "${SOURCE_DIR}/juice-keycloak.spec" --buildroot="${RPM_BUILD_ROOT}" --define "_topdir ${_RPMBUILD_DIR}" --define "_release_version ${RELEASE_VERSION}" +fi + +if [ "${RUN_HOOKS}" != "" ] +then + if [ "${BUILD_RPM}" != "" ] || [ "${1}" == "rpm" ] || [ "${2}" == "rpm" ] + then + run-parts "${SOURCE_DIR}/build-hooks/rpm" + fi + if [ "${docker_registry}" != "" ] || [ "${1}" == "docker-image" ] || [ "${2}" == "docker-image" ] + then + run-parts "${SOURCE_DIR}/build-hooks/docker-image" + fi +fi +cleanup \ No newline at end of file diff --git a/juice/account/resources/css/account.css b/juice/account/resources/css/account.css new file mode 100644 index 0000000..3878e43 --- /dev/null +++ b/juice/account/resources/css/account.css @@ -0,0 +1,277 @@ +html { + height: 100%; +} + +body { + background-color: #F9F9F9; + margin: 0; + padding: 0; + height: 100%; +} + +header .navbar { + margin-bottom: 0; + min-height: inherit; +} + +.header .container { + position: relative; +} + +.navbar-title { + background-image: url('../img/logo.png'); + height: 25px; + background-repeat: no-repeat; + width: 123px; + margin: 3px 10px 5px; + text-indent: -99999px; +} + +.navbar-pf .navbar-utility { + right: 20px; + top: -34px; + font-size: 12px; +} + +.navbar-pf .navbar-utility > li > a { + color: #fff !important; + padding-bottom: 12px; + padding-top: 11px; + border-left: medium none; +} + +.container { + height: 100%; +} + +.content-area { + background-color: #fff; + border-color: #CECECE; + border-style: solid; + border-width: 0 1px; + height: 100%; + padding: 0 30px; +} + +.margin-bottom { + margin-bottom: 10px; +} + +/* Sidebar */ + +.bs-sidebar { + background-color: #f9f9f9; + padding-top: 44px; + padding-right: 0; + padding-left: 0; + z-index: 20; +} +.bs-sidebar ul { + list-style: none; + padding-left: 12px; +} + +.bs-sidebar ul li { + margin-bottom: 0.5em; + margin-left: -1em; +} +.bs-sidebar ul li a { + font-size: 14px; + padding-left: 25px; + color: #4d5258; + line-height: 28px; + display: block; + border-width: 1px 0 1px 1px; + border-style: solid; + border-color: #f9f9f9; +} +.bs-sidebar ul li a:hover, +.bs-sidebar ul li a:focus { + text-decoration: none; + color: #777777; + border-right: 2px solid #aaa; +} +.bs-sidebar ul li.active a { + background-color: #c7e5f0; + border-color: #56bae0; + font-weight: bold; + background-image: url(../img/icon-sidebar-active.png); + background-repeat: no-repeat; + background-position: right center; +} + +.bs-sidebar ul li.active a:hover { + border-right: none; +} + + +.content-area h2 { + font-family: "Open Sans", sans-serif; + font-weight: 100; + font-size: 24px; + margin-bottom: 25px; + margin-top: 25px; +} + +.subtitle { + text-align: right; + margin-top: 30px; + color: #909090; +} + +.required { + color: #CB2915; +} + + +.alert { + margin-top: 30px; + margin-bottom: 0; +} + +.feedback-aligner .alert { + background-position: 1.27273em center; + background-repeat: no-repeat; + border-radius: 2px; + border-width: 1px; + color: #4D5258; + display: inline-block; + font-size: 1.1em; + line-height: 1.4em; + margin: 0; + padding: 0.909091em 3.63636em; + position: relative; + text-align: left; +} +.alert.alert-success { + background-color: #E4F1E1; + border-color: #4B9E39; +} +.alert.alert-error { + background-color: #F8E7E7; + border-color: #B91415; +} +.alert.alert-warning { + background-color: #FEF1E9; + border-color: #F17528; +} +.alert.alert-info { + background-color: #E4F3FA; + border-color: #5994B2; +} + +.form-horizontal { + border-top: 1px solid #E9E8E8; + padding-top: 23px; +} + +.form-horizontal .control-label { + color: #909090; + line-height: 1.4em; + padding-top: 5px; + position: relative; + text-align: right; + width: 100%; +} + +.form-group { + position: relative; +} + +.control-label + .required { + position: absolute; + right: -2px; + top: 0; +} + +#kc-form-buttons { + text-align: right; + margin-top: 10px; +} + +#kc-form-buttons .btn-primary { + float: right; + margin-left: 8px; +} + +/* Authenticator page */ + +ol { + padding-left: 40px; +} + +ol li { + font-size: 13px; + margin-bottom: 10px; + position: relative; +} + +ol li img { + margin-top: 15px; + margin-bottom: 5px; + border: 1px solid #eee; +} + +hr + .form-horizontal { + border: none; + padding-top: 0; +} + +.kc-dropdown{ + position: relative; +} +.kc-dropdown > a{ + display:block; + padding: 11px 10px 12px; + line-height: 12px; + font-size: 12px; + color: #fff !important; + text-decoration: none; +} +.kc-dropdown > a::after{ + content: "\2c5"; + margin-left: 4px; +} +.kc-dropdown:hover > a{ + background-color: rgba(0,0,0,0.2); +} +.kc-dropdown ul li a{ + padding: 1px 11px; + font-size: 12px; + color: #000 !important; + border: 1px solid #fff; + text-decoration: none; + display:block; + line-height: 20px; +} +.kc-dropdown ul li a:hover{ + color: #4d5258; + background-color: #d4edfa; + border-color: #b3d3e7; +} +.kc-dropdown ul{ + position: absolute; + z-index: 2000; + list-style:none; + display:none; + padding: 5px 0px; + margin: 0px; + background-color: #fff !important; + border: 1px solid #b6b6b6; + border-radius: 1px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; + min-width: 100px; +} +.kc-dropdown:hover ul{ + display:block; +} + + +#kc-totp-secret-key { + border: 1px solid #eee; + font-size: 16px; + padding: 10px; + margin: 50px 0; +} \ No newline at end of file diff --git a/juice/account/resources/img/icon-sidebar-active.png b/juice/account/resources/img/icon-sidebar-active.png new file mode 100644 index 0000000000000000000000000000000000000000..e7b9b082836b728286e1962f7d2efc81ddbe0b71 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^;y}#D!3HFcd~W{?q$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c73FxkIEGZ*N=jIun2<7~^e}_RcjiI`=UK zVCeYHtShu&-DyXLq!)E7W+-p1Ilz&A@c@&Xh}=}&gd+kA7qR+CC9YLuAP#kTzP zOOabP0l+XkKxNbyD literal 0 HcmV?d00001 diff --git a/juice/account/resources/img/keycloak-logo.png b/juice/account/resources/img/keycloak-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..955574855d493cc248e112b7d755a95d641613f8 GIT binary patch literal 5213 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 span { + -moz-box-sizing: border-box; + color: white; + float: left; + font-size: 11px; + font-family: "Open Sans", sans-serif; + font-weight: bold; + height: 24px; + line-height: 24px; + padding: 0; + width: 50%; +} +.onoffswitch .onoffswitch-switch { + background-image: linear-gradient(top, #fafafa 0%, #ededed 100%); + background-image: -o-linear-gradient(top, #fafafa 0%, #ededed 100%); + background-image: -moz-linear-gradient(top, #fafafa 0%, #ededed 100%); + background-image: -webkit-linear-gradient(top, #fafafa 0%, #ededed 100%); + background-image: -ms-linear-gradient(top, #fafafa 0%, #ededed 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fafafa), color-stop(1, 0, #ededed)); + border: 1px solid #aaa; + border-radius: 2px; + bottom: 0; + margin: 0; + position: absolute; + right: 39px; + top: 0; + transition: all 0.3s ease-in 0s; + -webkit-transition: all 0.3s ease-in 0s; + width: 23px; +} +.onoffswitch .onoffswitch-inner .onoffswitch-active { + background-image: linear-gradient(top, #00a9ec 0%, #009bd3 100%); + background-image: -o-linear-gradient(top, #00a9ec 0%, #009bd3 100%); + background-image: -moz-linear-gradient(top, #00a9ec 0%, #009bd3 100%); + background-image: -webkit-linear-gradient(top, #00a9ec 0%, #009bd3 100%); + background-image: -ms-linear-gradient(top, #00a9ec 0%, #009bd3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #00a9ec), color-stop(1, 0, #009bd3)); + color: #FFFFFF; + padding-left: 10px; +} +.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner .onoffswitch-active, +.onoffswitch-checkbox:disabled + .onoffswitch-label .onoffswitch-inner .onoffswitch-inactive { + background-image: none; + background-color: #e5e5e5; + color: #9d9fa1; +} +.onoffswitch .onoffswitch-inner .onoffswitch-inactive { + background: linear-gradient(#fefefe, #e8e8e8) repeat scroll 0 0 transparent; + color: #4d5258; + padding-right: 10px; + text-align: right; +} +.onoffswitch .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { + margin-left: 0; +} +.onoffswitch .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { + right: 0; +} + + +/*********** Select 2 ***********/ + +.select2-container { + width: 100%; +} + +.select2-container-multi .select2-choices .select2-search-field { + height: 26px; +} + +/*********** html select ********/ +.overflow-select { + overflow: auto; +} + + +/*********** New Menu ***********/ + + +.sidebar-pf-left{ + background: #292e34; +} + +.sidebar-pf .nav-pills > li a i, .sidebar-pf .nav-pills > li a span{ + color: #72767b; + display: inline-block; + margin-right: 10px; +} +.sidebar-pf .nav-pills > li > a{ + color: #dbdada; + padding: 0px 20px 0 30px!important; + line-height: 30px; + border-left-width: 12px; + border-left-style: solid; + border-left-color: #292e34; + margin-left: -6px; +} + +.sidebar-pf .nav-pills > li > a:hover{ + background: #393f44; + border-color:#292e34; + border-left-color: #393f44; + color: #fff; +} + +.sidebar-pf .nav-pills > li > a:after{ + display: none!important; +} + + +.sidebar-pf .nav-pills > li.active > a { + color: #fff; + background: #393f44!important; + border-bottom: 1px solid #000!important; + border-top: 1px solid #000!important; + border-left-color: #39a5dc!important; +} + +.sidebar-pf .nav-pills > li.active a i, .sidebar-pf .nav-pills > li.active a span{ + color: #39a5dc; +} + +/*********** Realm selector ***********/ + +.realm-selector{ + color: #fff; + margin: 0 -20px; + position: relative; +} + +.realm-dropmenu{ + display: none; + cursor: pointer; + position: absolute; + top: 60px; + left: 0; + right: 0; + z-index: 999; + background: #fff; +} + +.realm-selector:hover .realm-dropmenu{ + display: block; +} + +.realm-add{ + padding: 10px; +} + +.realm-selector h2{ + font-size: 16px; + line-height: 60px; + padding: 0 20px; + margin: 0; + border-bottom: 1px solid #d5d5d6; +} + +.realm-selector h2 i{ + display: inline-block; + float: right; + line-height: 60px; +} + + +.realm-selector ul{ + padding-left: 0; + margin: 0; + list-style: none; + max-height: 200px; + overflow-y:auto; +} + + +.realm-selector ul li a{ + line-height: 60px; + padding: 0 20px; + border-bottom: 1px solid #d5d5d6; + line-height: 39px; + display: block; + font-size: 14px; +} + + +/*********** Overwrites header defaults ***********/ + +.navbar-pf{ + border-top: none!important; +} + +.navbar-pf .navbar-brand { + padding: 0; + height: 56px; + line-height: 56px; + background-position: center center; + background-image: url('../img/keyclok-logo.png'); + background-size: 148px 30px; + background-repeat: no-repeat; + width: 148px; +} + +.navbar-pf .navbar-utility .dropdown-toggle { + padding: 23px !important; +} + +.clickable { + cursor: pointer; +} + +h1 i { + color: #999999; + font-size: 18px; + margin-left: 10px; +} + +/* Action cell */ +.kc-action-cell { + background-color: #eeeeee; + background-image: linear-gradient(to bottom, #fafafa 0%, #ededed 100%); + background-repeat: repeat-x; + + text-align: center; + vertical-align: middle; + + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + cursor:pointer; +} + +.kc-action-cell:hover { + background-color: #eeeeee; + background-image: none; +} + +.kc-sorter span { + margin-left: 10px; +} + + +/* Time selector */ + +.time-selector input { + display: inline-block; + width: 120px; + padding-right: 0; + margin-right: 0; +} + +.time-selector select { + display: inline-block; + width: 80px; + margin-left: 0; + padding-left: 0; +} + +.ace_editor { + height: 600px; + width: 100%; +} + +.kc-button-input-file input { + float: left; + width: 73%; +} + +.kc-button-input-file label { + float: left; + margin-left: 2%; + width: 25%; +} + +table.kc-authz-table-expanded { + margin-top: 0px !important; +} + +.no-gutter > [class*='col-'] { + padding-right:0!important; + padding-left:0!important; +} + +.password-conceal { + font-family: 'text-security-disc'; + font-size: 14px; +} + +/* Deactivation styles for user-group membership tree models */ + +div[tree-model] li .deactivate { + color: #4a5053; + opacity: 0.4; +} + +div[tree-model] li .deactivate_selected { + background-color: #dcdcdc; + font-weight: bold; + padding: 1px 5px; +} + +/* search highlighting */ + +div[tree-model] li .highlight { + background-color: #aaddff; +} + +/* Manage credentials */ +table.credentials-table { + margin-top: 0; + margin-bottom: 20px; +} + +table.credentials-table td { + vertical-align: middle !important; +} + +table.credentials-table input[type='text'] { + width: 100%; +} + +td.credential-arrows-cell { + width: 75px; +} + +td.credential-label-cell { + padding: 5px !important; +} + +td.credential-action-cell { + padding: 0px !important; +} + +td.credential-action-cell div.kc-action-cell { + width: 100%; + height: 36px; + line-height: 34px; +} + +td.credential-action-cell.expanded div.kc-action-cell { + border-bottom: 1px solid #d1d1d1; +} + +table.credential-data-table td { + word-break: break-all; +} + +table.credential-data-table tr:first-child td { + border-top: 0; +} + +table.credential-data-table td:first-child { + width: 150px; +} + +table.credential-data-table td.key { + text-align: right; + font-weight: bold; +} + diff --git a/juice/admin/resources/img/keyclok-logo.png b/juice/admin/resources/img/keyclok-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ca53f0a04ba97029b387ada299ae4b454b54ccd4 GIT binary patch literal 5268 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 akN*dX+`h$pZtd{^0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/juice/admin/resources/img/select-arrow.png b/juice/admin/resources/img/select-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a865a6fa7324e054cffa6e1878d69438780b20c7 GIT binary patch literal 1154 zcmbVMTWHfz7>p*_>aXo9GTd$y`I14I@;lVM5JhweKRY3LHQC7fY1b^;WQwQUZl zZ_DPjwq`A+Q}I<~jVEFP6CjoJ%#!7boBlvw?V=p`?Z&9uZobt9Ob)oP#nEs*8W&c;ICuXx!@E2M)bdlwvIE#@G8ws*Pl;dI?8%xx(sU$BXLTpf}aEyknRX9Ex zim)s%ggGwB*G7`bSSXr`29=P4X)D;YTqMb3NA3NLQ&>ArT2 zA9uH-Ywx;_@dpP62I76EH_hHudfZ-m_3_D@=eypgs=j~y@N$1<+s2d9fz493b7t>b zdi$;G;Cc4Oy<=aF_s6}ZOI9R~$+;sl=AP@Cwm_P2^Y!O +<@layout.registrationLayout displayMessage=false; section> + <#if section = "header"> + + +
+ <#elseif section = "form"> +
+

${message.summary?no_esc}

+
+ + + diff --git a/juice/login/login-page-expired.ftl b/juice/login/login-page-expired.ftl new file mode 100644 index 0000000..1721b76 --- /dev/null +++ b/juice/login/login-page-expired.ftl @@ -0,0 +1,13 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout; section> + <#if section = "header"> + + +
+ <#elseif section = "form"> +

+ ${msg("pageExpiredMsg1")} +

+ ${msg("pageExpiredButton")} + + diff --git a/juice/login/login-reset-password.ftl b/juice/login/login-reset-password.ftl new file mode 100644 index 0000000..6705aa3 --- /dev/null +++ b/juice/login/login-reset-password.ftl @@ -0,0 +1,61 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayInfo=true displayMessage=!messagesPerField.existsError('username'); section> + <#if section = "header"> + + + <#elseif section = "form"> +
+
+ + + +
+ <#if auth?has_content && auth.showUsername()> + + + + <#else> + + + + + <#if messagesPerField.existsError('username')> +
+ ${kcSanitize(messagesPerField.get('username'))?no_esc} +
+ +
+ +
+ +
+ +
+
+ <#if client?? && client.baseUrl?has_content> + ${kcSanitize(msg("backToLogin"))?no_esc} + +
+
+
+ ${msg("haveAccount")} ${msg("signInHere")} +
+
+
+
+ +
+ <#elseif section = "info" > + + + + \ No newline at end of file diff --git a/juice/login/login-verify-email.ftl b/juice/login/login-verify-email.ftl new file mode 100644 index 0000000..48536d3 --- /dev/null +++ b/juice/login/login-verify-email.ftl @@ -0,0 +1,14 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayInfo=true; section> + <#if section = "header"> + + +
+ <#elseif section = "form"> +

${msg("emailVerifyInstruction1")}

+ <#elseif section = "info"> +
${msg("emailVerifyInstruction2")}
+ ${msg("emailVerifyInstruction3")} + + + \ No newline at end of file diff --git a/juice/login/login.ftl b/juice/login/login.ftl new file mode 100644 index 0000000..a789581 --- /dev/null +++ b/juice/login/login.ftl @@ -0,0 +1,122 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??; section> + <#if section = "header"> +
+ + <#if realm.internationalizationEnabled && locale.supported?size gt 1> +
+
+
+ ${locale.current} +
    + <#list locale.supported as l> + <#if locale.current==l.label> +
  • ${l.label}
  • + <#else> +
  • ${l.label}
  • + + +
+
+
+
+ +
+ + <#elseif section = "form"> +
+
+ <#if realm.password> +
+
+ + <#if usernameEditDisabled??> + + <#else> + + <#if messagesPerField.existsError('username','password')> +
+ ${kcSanitize(messagesPerField.getFirstError('username','password'))?no_esc} +
+ + + + +
+ +
+ + + +
+ +
+ value="${auth.selectedCredential}"/> + +
+ + + <#if realm.password && social.providers??> + + + + + +
+
+
+ <#if realm.resetPasswordAllowed> + ${msg("doForgotPassword")} + +
+ <#if realm.password && realm.registrationAllowed && !registrationDisabled??> +
+
+ ${msg("noAccount")} ${msg("signUpHere")} +
+
+ +
+ + + +
+
+ + + + diff --git a/juice/login/messages/messages_de.properties b/juice/login/messages/messages_de.properties new file mode 100644 index 0000000..496ce3b --- /dev/null +++ b/juice/login/messages/messages_de.properties @@ -0,0 +1,35 @@ +welcome=Willkommen! +error=Fehler +signinTerms=Mit der Registrierung erkl\u00E4ren Sie sich mit den Nutzungsbedingungen und der Datenschutzerkl\u00E4rung von Juice einverstanden. +signupTerms=Mit der Registrierung erkl\u00E4ren Sie sich mit den Nutzungsbedingungen und der Datenschutzerkl\u00E4rung von Juice einverstanden. +signInHere=Hier anmelden +signUpHere=Mit Email registrieren +passwordRecovery=Passwort wiederherstellen + +loginAccountTitle=Anmelden +doRegister=Registrieren +doForgotPassword=Passwort vergessen? +noAccount=Haben Sie ein Konto? + +emailForgotTitle=Ihr Passwort +emailForgotTitle1=zur\u00FCcksetzen +emailInstruction=Bitte geben Sie Ihre E-Mail Adresse ein +emailInstruction1=Wir senden Ihnen dann eine E-Mail, um Ihr Passwort zur\u00FCckzusetzen. + +doSubmit=E-Mail senden +backToLogin=Zur\u00FCck +haveAccount=Haben Sie ein Konto? + +registerTitle=Welcome to the +registerTitle1=JUICE WORLD + +emailVerifyTitle=Verifizieren Sie Ihre E-Mail +emailVerifyInstruction1=Sie m\u00FCssen Ihre E-Mail-Adresse verifizieren, um Ihr Konto zu aktivieren. Eine E-Mail mit Anweisungen zur Verifizierung Ihrer E-Mail-Adresse wurde an Sie gesendet +emailVerifyInstruction3=Senden Sie die E-Mail erneut +identity-provider-login-label=Oder registrieren/anmelden mit + +pageExpiredTitle=Ihre Sitzung ist abgelaufen +pageExpiredMsg1=Keine Sorge, melden Sie sich einfach erneut an + +pageExpiredButton=Gehen Sie zu Anmelden + diff --git a/juice/login/messages/messages_en.properties b/juice/login/messages/messages_en.properties new file mode 100644 index 0000000..cade1f7 --- /dev/null +++ b/juice/login/messages/messages_en.properties @@ -0,0 +1,33 @@ +welcome=Welcome! +error=Error +signinTerms=By signing up, you agree to Juice''s ToS and Privacy +signupTerms=By signing up, you agree to Juice''s Terms of Use +signInHere=Sign in here +signUpHere=Sign up with the email +passwordRecovery=Password recovery + +loginAccountTitle=Sign in +doRegister=Sign up +doForgotPassword=Forgot password? +noAccount=Not a member? + +emailForgotTitle=Reset +emailForgotTitle1=your password +emailInstruction=Please enter your email address. +emailInstruction1=We will send you an email to reset your password. + +doSubmit=Send email +backToLogin=Go back +haveAccount=Have an account? + +registerTitle=Welcome to the +registerTitle1=JUICE WORLD + +emailVerifyTitle=Verify your email +emailVerifyInstruction1=You need to verify your email address to activate your account. An email with instructions to verify your email address has been sent to you +emailVerifyInstruction3=Re-send the email + +pageExpiredTitle=Your session has expired +pageExpiredMsg1=No worry, simply sign in again + +pageExpiredButton=Go to Sign In \ No newline at end of file diff --git a/juice/login/register.ftl b/juice/login/register.ftl new file mode 100644 index 0000000..03c8027 --- /dev/null +++ b/juice/login/register.ftl @@ -0,0 +1,139 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayMessage=!messagesPerField.existsError('firstName','lastName','email','username','password','password-confirm'); section> + <#if section = "header"> +
+ + <#if realm.internationalizationEnabled && locale.supported?size gt 1> +
+
+
+ ${locale.current} +
    + <#list locale.supported as l> + <#if locale.current==l.label> +
  • ${l.label}
  • + <#else> +
  • ${l.label}
  • + + +
+
+
+
+ +
+ + <#elseif section = "form"> +
+ +
+
+ + + + <#if messagesPerField.existsError('email')> +
+ ${kcSanitize(messagesPerField.get('email'))?no_esc} +
+ +
+ + <#if passwordRequired??> +
+ + + + <#if messagesPerField.existsError('password')> +
+ ${kcSanitize(messagesPerField.get('password'))?no_esc} +
+ +
+ +
+ + + + <#if messagesPerField.existsError('password-confirm')> +
+ ${kcSanitize(messagesPerField.get('password-confirm'))?no_esc} +
+ +
+ +
+ + <#if !realm.registrationEmailAsUsername> +
+
+ +
+
+ + + <#if messagesPerField.existsError('username')> +
+ ${kcSanitize(messagesPerField.get('username'))?no_esc} +
+ +
+
+ + + <#if recaptchaRequired??> +
+
+
+
+
+ + +
+ +
+ +
+ + + +
+ + +
+
+ + <#if realm.password && realm.registrationAllowed && !registrationDisabled??> +
+
+ ${msg("haveAccount")} ${msg("signInHere")} +
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/juice/login/resources/css/styles.css b/juice/login/resources/css/styles.css new file mode 100644 index 0000000..b1027e4 --- /dev/null +++ b/juice/login/resources/css/styles.css @@ -0,0 +1,758 @@ +@font-face { + font-family: "Swis721 Th Bt"; + src: local("Swiss721ThinBT.ttf"), url("../fonts/Swiss721ThinBT.ttf") format("truetype"); + font-weight: 300; +} +@font-face { + font-family: "Swis721 Md BT"; + src: local("Swiss721MediumBT.ttf"), url("../fonts/Swiss721MediumBT.ttf") format("truetype"); + font-weight: 500; +} +@font-face { + font-family: "Roboto"; + src: local("Roboto-Medium.ttf"), url("../fonts/Roboto-Medium.ttf") format("truetype"); + font-weight: 300; +} + +.login-pf { + height: 100%; + background: none; +} + +.login-pf body { + background: url(../img/juice-bc.svg); + background-repeat: no-repeat; + background-color: #202020; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} +.login-pf body::before { + position: absolute; + width: 56px; + height: 56px; + content: ""; + top: 56px; + left: 63px; + background: url(../img/juice.svg); + background-repeat: no-repeat; +} + +.login-pf-page { + padding-top: 0px; +} + +.h1, .h2, .h3, h1, h2, h3 { + margin-top: 0px; + margin-bottom: 0px; +} + +#kc-header-wrapper { + padding: 0px 10px; +} + +.card-login { + margin: 0 auto; + box-shadow: -8px -8px 32px rgba(101, 101, 101, 0.12), -2px -2px 16px rgba(101, 101, 101, 0.24), 4px 4px 16px rgba(0, 0, 0, 0.3), 8px 8px 32px rgba(0, 0, 0, 0.1); + padding: 56px; + min-width: 440px; + max-width: 440px; + border-radius: 16px; + background-color: #212121; +} + +.login-pf-page-header { + background: url(../img/juice.svg); + background-position-x: 63px; + background-position-y: 56px; + background-repeat: no-repeat; +} +.header-login-welcome { + font-family: "Swis721 Th Bt"; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #98989E; +} +.header-login-title { + padding-top: 4px; + font-family: "Swis721 Th Bt"; + font-style: normal; + font-weight: 300; + font-size: 38px; + line-height: 48px; + letter-spacing: -0.75px; + background: linear-gradient(180deg, #FF9900 0%, #FF7100 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.error-welcome { + background: #F52E2E; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.error-image { + width: 96px; + height: 96px; + background: url(../img/error.svg); + margin: 32px 0px 12px; +} +.verify-image { + width: 96px; + height: 96px; + background: url(../img/verify-email.svg); + margin: 32px 0px 12px; +} +.instruction { + margin: 0px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 19px; + line-height: 28px; + display: flex; + align-items: center; + letter-spacing: -0.1px; + color: #BDBFBF; +} +#kc-error-message .instruction { + margin: 0px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 19px; + line-height: 28px; + display: flex; + align-items: center; + letter-spacing: -0.1px; + color: #BDBFBF; +} +#kc-info { + margin: 0px; +} +.verify-email-question { + margin-top: 32px; + margin-bottom: 8px; + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 16px; + display: flex; + align-items: center; + color: #BDBFBF; +} +.login-pf-page .login-pf-signup a { + margin-left: 0px; +} +.login-pf-page .login-pf-settings { + flex-wrap: nowrap; +} +.verify-email-link { + margin-left: 0px; + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 16px; + display: flex; + align-items: center; + text-align: right; + color: #FF7100; +} +a.verify-email-link:hover { + color: #FF7100; +} + +#kc-page-title{ + text-align: left; +} + +#kc-form-buttons { + margin-top: 32px; + margin-bottom: 32px; +} + +.pf-c-button { + height: 56px; + background: #181818; + box-shadow: -4px -4px 8px rgba(101, 101, 101, 0.08), -1px -1px 3px rgba(101, 101, 101, 0.12), 1px 1px 4px rgba(0, 0, 0, 0.3), 4px 4px 8px rgba(0, 0, 0, 0.4); + border-radius: 4px; + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 16px; + line-height: 24px; + text-align: center; + color: #BDBFBF; + text-transform: capitalize; +} +.pf-c-button:hover, +.pf-c-button:focus { + background: #292929; +} +.pf-c-button:disabled { + background: #292929; + opacity: 0.3; +} + +.pf-c-form-control{ + position: relative; + min-height: 56px; + border: 1px solid #333333; + box-sizing: border-box; + border-radius: 4px; + background: #212121; + font-family: "Swis721 Th BT"; + padding-left: 16px; + font-style: normal; + font-weight: 300; + font-size: 16px; + line-height: 24px; + color: #BDBFBF; +} + +.form-group { + position: relative; + margin-bottom: 24px; +} + +#input-error, +#input-error-username, +#input-error-email, +#input-error-password, +#input-error-password-confirm { + margin-top: 8px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #BDBFBF; +} + + +.pf-c-form-control:focus, .pf-c-form-control:hover{ + outline: none; + border: 1px solid rgba(255, 113, 0, 1); + caret-color: rgba(255, 113, 0, 1); + font-family: Swis721 Th BT; +} +.pf-c-form-control[aria-invalid="true"] { + border: 1px solid rgba(245, 46, 46, 1); + background: #212121; +} +.login-form-input-label { + position: absolute; + display: none; + padding: 0px 4px; + top: -10px; + left: 12px; + color:rgba(255, 113, 0, 1); + z-index: 100; + background-color: #212121; +} +.pf-c-form-control:focus ~ .login-form-input-label, +.pf-c-form-control:hover ~ .login-form-input-label, +.pf-c-form-control[aria-invalid="true"] ~ .login-form-input-label { + display: block; +} + +.pf-c-form-control[aria-invalid="true"] ~ .login-form-input-label { + color: rgba(245, 46, 46, 1); +} +#kc-form-wrapper a { + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 16px; + color: rgba(189, 191, 191, 1); +} +#kc-registration { + width: 200px; +} +#kc-registration span { + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 16px; + color: #414142; + display: flex; + flex-wrap: wrap; + justify-content: flex-end; +} + +#kc-registration a { + padding-left: 8px; + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 16px; + color: #FFFFFF; +} +#kc-registration a::after { + content: " →"; +} + +#kc-info-wrapper { + padding: 0px; + background-color: #212121; +} +.register-form { + margin-bottom: 32px; +} + +.juice-footer { + position: absolute; + right: 56px; + bottom: 56px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 12px; + line-height: 16px; + color: #BDBFBF; +} + +.juice-footer span { + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 20px; + color: #BDBFBF; +} + +/* password */ +#kc-form-options { + display: flex; + align-items: center; + width: 145px; +} + +#kc-form-options.signup-password { + width: 140px; +} + +#kc-form-options a { + font-family: "Swis721 Md BT"; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 16px; + color: #FFFFFF; +} + +.email-instruction { + padding-top: 12px; + padding-bottom: 32px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 19px; + line-height: 28px; + display: flex; + align-items: center; + letter-spacing: -0.1px; + color: #98989E; +} + +/* sign-up */ + +.signup-terms { + margin: 0 auto; + width: 272px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 12px; + line-height: 16px; + text-align: center; + color: #98989E; +} +.login-signup-terms { + margin: 32px auto; +} + +.form-horizontal .form-group { + margin-right: 0px; + margin-left: 0px; +} + +/* alert */ + +.pf-c-alert { + position: absolute; + bottom: 2.34%; + + width: 328px; + min-height: 68px; + background: rgba(0, 0, 0, 0.87); + box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.14), 0px 1px 18px rgba(0, 0, 0, 0.12), 0px 3px 5px rgba(0, 0, 0, 0.2); + border-radius: 4px; + z-index: 100; +} +.pf-c-alert.pf-m-inline { + display: flex; + justify-content: center; + align-items: center; + border: none; + margin-bottom: 0px; + padding: 0px; +} +.pf-c-alert__title { + width: 321px; + min-height: 68px; + padding-right: 8px; + padding-left: 15px; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #FFFFFF; +} +.pf-c-alert.pf-m-inline::before { + content: none; +} +.alert-button { + width: 65,79px; + height: 36px; + margin-right: 9px; + padding: 10px 9px 10px 30px; + font-family: "Roboto"; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 16px; + text-align: right; + letter-spacing: 1.25px; + text-transform: uppercase; + border: none; + outline: none; + background: linear-gradient(180deg, #FF9900 0%, #FF7100 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.welcome-container { + display: flex; + justify-content: space-between; + position: relative; +} + +#kc-current-locale-link { + position: relative; + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #BDBFBF; +} +#kc-locale-dropdown a { + color: #BDBFBF; +} + +#kc-current-locale-link::before { + content: ''; + position: absolute; + background: url(../img/locale.svg); + width: 16px; + height: 16px; + top: 2px; + left: -17px; +} + +#kc-current-locale-link::after { + display: none; +} + +#kc-locale ul { + list-style: none; + top: 20px; + padding: 2px 0; + width: 136px; + background: rgba(23, 23, 23, 0.92); + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.32); + border-radius: 8px; + border: none; +} +.kc-dropdown-item { + position: relative; + height: 40px; + padding-left: 30px; + display: flex; + justify-content: flex-start; + align-items: center; +} +.kc-dropdown-item::before { + position: absolute; + width: 16px; + height: 12px; + content: ''; + left: 16px; +} +.kc-dropdown-item.English::before { + background: url(../img/english.svg); +} +.kc-dropdown-item.Deutsch::before { + background: url(../img/deutsch.svg); +} + +#kc-locale ul li a { + font-family: "Swis721 Th BT"; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #BDBFBF !important; +} +#kc-locale .kc-dropdown-item.selected a { + font-family: "Swis721 Md BT"; +} + +#kc-locale ul li a:hover { + background: rgba(23, 23, 23, 0.92); + color: #FF7100 !important; +} +.kc-social-grid { + display: flex; + justify-content: center; + flex-wrap: nowrap; + grid-column-gap: 0px; +} +.social-button { + margin: 0 11px; + width: 48px !important; + height: 48px; + padding: 16px; + background: #212121; + border: none; + border-radius: 8px; +} +.social-button:last-child { + margin-right: 0; +} +.social-button:first-child { + margin-left: 0; +} +.separate-container { + margin: 32px 0px 32px; + display: flex; + align-items: center; +} +.separate-text { + margin: 0px 8px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 16px; + line-height: 24px; + display: flex; + align-items: center; + text-align: center; + color: #98989E; +} +.separator { + flex: auto; + max-width: 109px; + min-width: 10px; + height: 1px; + background-color: #333333; +} + +.login-sign-image { + background: url(../img/apple.svg); + width: 16px; + height: 16px; + margin-right: 16px; +} + +.login-sign-image.apple { + background: url(../img/apple.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.google { + background: url(../img/google.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.facebook { + background: url(../img/facebook.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.linkedin { + background: url(../img/linkedin.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.twitter { + background: url(../img/twitter.svg); + background-size: contain; + background-repeat: no-repeat; +} +.kc-social-provider-logo { + font-size: 17px; + width: 13px; + height: 13px; + float: left; +} +.expired-image { + width: 96px; + height: 96px; + background: url(../img/clock.svg); + margin: 32px 0px 2px; +} +.expired-button { + margin-top: 32px; + text-align: center; + padding-top: 16px; + text-transform: none; +} +.expired-title { + width: 215px; +} +.login-pf a:hover { + text-decoration: none; + color: #BDBFBF; +} + +@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + .header-login-title { + background: #202020; + color: #FF7100; + } + .error-welcome { + background: #202020; + color: #F52E2E; + } + .alert-button { + background: #202020; + color: #FF7100; + } +} + +@media (max-height: 900px) and (max-width: 880px) { + .login-pf body::before { + display: none; + } + .juice-footer { + display: none; + } +} + +@media (max-height: 680px) { + .login-pf body { + align-items: unset; + } +} + +@media (max-width: 767px) { + #kc-locale { + position: relative !important; + width: inherit; + top: 0px; + right: 0px; + text-align: none; + } + + #kc-info-wrapper { + border-top: none; + } +} + +@media (min-width: 768px) { + .login-pf-page .login-pf-page-header { + margin-bottom: 0px; + } +} + + +@media (max-width: 440px) { + .login-pf body::before { + display: none; + } + + .login-pf body { + align-items: unset; + } + .login-pf-page .login-pf-header { + margin-bottom: 32px; + } + #kc-header { + display: none; + } + .card-login { + min-width: 328px; + max-width: 448px; + padding: 16px 10px; + box-shadow: none; + } + .juice-footer { + display: none; + } +} + +@media (max-height: 440px) { + .login-pf body::before { + display: none; + } + .juice-footer { + display: none; + } +} + +@media (max-width: 360px) { + .card-login { + width: 360px; + padding: 16px 10px; + box-shadow: none; + } +} + +@media (max-width: 359px) { + .card-login { + min-width: 248px; + max-width: 304px; + padding: 16px 10px; + box-shadow: none; + } + #kc-registration { + width: 155px; + } + .social-button { + padding: 16px 12px; + } +} + +@media (max-width: 300px) { + .card-login { + min-width: 238px; + max-width: 278px; + padding: 16px 10px; + box-shadow: none; + } + .social-button { + padding: 16px 10px; + } +} + +@media (max-width: 279px) { + .login-pf-page { + overflow-y: auto; + overflow-x: auto; + } +} \ No newline at end of file diff --git a/juice/login/resources/fonts/Roboto-Medium.ttf b/juice/login/resources/fonts/Roboto-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f714a514d94e495095e2f1e525a341eade187c17 GIT binary patch literal 171656 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 Xg}~YYzj(9Rln@=C8yzhm6UP1nUuR=v literal 0 HcmV?d00001 diff --git a/juice/login/resources/fonts/Swiss721ThinBT.ttf b/juice/login/resources/fonts/Swiss721ThinBT.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ebc5cf58910d5084fcc4e26d5e5ffee6c40eb460 GIT binary patch literal 35264 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 + + diff --git a/juice/login/resources/img/clock.svg b/juice/login/resources/img/clock.svg new file mode 100644 index 0000000..4abc291 --- /dev/null +++ b/juice/login/resources/img/clock.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/img/deutsch.svg b/juice/login/resources/img/deutsch.svg new file mode 100644 index 0000000..9cb0d95 --- /dev/null +++ b/juice/login/resources/img/deutsch.svg @@ -0,0 +1,9 @@ + + + + Flag of Germany + + + + diff --git a/juice/login/resources/img/english.svg b/juice/login/resources/img/english.svg new file mode 100644 index 0000000..75a198d --- /dev/null +++ b/juice/login/resources/img/english.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/juice/login/resources/img/error.svg b/juice/login/resources/img/error.svg new file mode 100644 index 0000000..1970e12 --- /dev/null +++ b/juice/login/resources/img/error.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/img/facebook.svg b/juice/login/resources/img/facebook.svg new file mode 100644 index 0000000..f4ba330 --- /dev/null +++ b/juice/login/resources/img/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/img/google.svg b/juice/login/resources/img/google.svg new file mode 100644 index 0000000..8649a02 --- /dev/null +++ b/juice/login/resources/img/google.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/juice/login/resources/img/juice-bc copy.svg b/juice/login/resources/img/juice-bc copy.svg new file mode 100644 index 0000000..3c65812 --- /dev/null +++ b/juice/login/resources/img/juice-bc copy.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/juice/login/resources/img/juice-bc.svg b/juice/login/resources/img/juice-bc.svg new file mode 100644 index 0000000..3c65812 --- /dev/null +++ b/juice/login/resources/img/juice-bc.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/juice/login/resources/img/juice.svg b/juice/login/resources/img/juice.svg new file mode 100644 index 0000000..ee1ec0d --- /dev/null +++ b/juice/login/resources/img/juice.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/juice/login/resources/img/linkedin.svg b/juice/login/resources/img/linkedin.svg new file mode 100644 index 0000000..c87b46e --- /dev/null +++ b/juice/login/resources/img/linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/img/locale.svg b/juice/login/resources/img/locale.svg new file mode 100644 index 0000000..79484b7 --- /dev/null +++ b/juice/login/resources/img/locale.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/img/twitter.svg b/juice/login/resources/img/twitter.svg new file mode 100644 index 0000000..3727d76 --- /dev/null +++ b/juice/login/resources/img/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/img/verify-email.svg b/juice/login/resources/img/verify-email.svg new file mode 100644 index 0000000..0ff2735 --- /dev/null +++ b/juice/login/resources/img/verify-email.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/login/resources/js/script.js b/juice/login/resources/js/script.js new file mode 100644 index 0000000..1c235cf --- /dev/null +++ b/juice/login/resources/js/script.js @@ -0,0 +1,5 @@ + +function hideSnackbar() { + var x = document.getElementById("snackbar"); + document.getElementById('snackbar').style.display = 'none'; +} \ No newline at end of file diff --git a/juice/login/template.ftl b/juice/login/template.ftl new file mode 100644 index 0000000..904136a --- /dev/null +++ b/juice/login/template.ftl @@ -0,0 +1,134 @@ +<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false showAnotherWayIfPresent=true> + + + + + + + + + <#if properties.meta?has_content> + <#list properties.meta?split(' ') as meta> + + + + ${msg("loginTitle",(realm.displayName!''))} + + <#if properties.stylesCommon?has_content> + <#list properties.stylesCommon?split(' ') as style> + + + + <#if properties.styles?has_content> + <#list properties.styles?split(' ') as style> + + + + <#if properties.scripts?has_content> + <#list properties.scripts?split(' ') as script> + + + + <#if scripts??> + <#list scripts as script> + + + + + + +

+ <#if displayMessage && message?has_content && (message.type != 'warning' || !isAppInitiatedAction??)> +
+ +
${kcSanitize(message.summary)?no_esc}
+ +
+ + + + diff --git a/juice/login/theme.properties b/juice/login/theme.properties new file mode 100644 index 0000000..68bb1a9 --- /dev/null +++ b/juice/login/theme.properties @@ -0,0 +1,8 @@ +parent=keycloak +import=common/keycloak + +styles=css/login.css css/styles.css + +scripts=js/script.js + +kcFormCardClass=card-login \ No newline at end of file diff --git a/juice/welcome/index.ftl b/juice/welcome/index.ftl new file mode 100644 index 0000000..2999b2e --- /dev/null +++ b/juice/welcome/index.ftl @@ -0,0 +1,68 @@ + +  + + + Welcome to ${productNameFull} + + + + + + + + + <#if properties.stylesCommon?has_content> + <#list properties.stylesCommon?split(' ') as style> + + + + <#if properties.styles?has_content> + <#list properties.styles?split(' ') as style> + + + + + + + +
+
+ +
+
+ + + + \ No newline at end of file diff --git a/juice/welcome/messages/messages_en.properties b/juice/welcome/messages/messages_en.properties new file mode 100644 index 0000000..c5e2d19 --- /dev/null +++ b/juice/welcome/messages/messages_en.properties @@ -0,0 +1,20 @@ +loginAccountTitle=Sign in + +doRegister=Sign up +doForgotPassword=Forgot password? +noAccount=Not a member? + +emailForgotTitle=Reset +emailForgotTitle1=your password +emailInstruction=Please enter your email address. +emailInstruction1=We will send you an email to reset your password. +doSubmit=Send email +backToLogin=Go back +haveAccount=Have an account? + +registerTitle=Join +registerTitle1=The Community + +emailVerifyTitle=Verify your email +emailVerifyInstruction1=You need to verify your email address to activate your account. An email with instructions to verify your email address has been sent to you +emailVerifyInstruction3=Re-send the email \ No newline at end of file diff --git a/juice/welcome/messages/messages_ru.properties b/juice/welcome/messages/messages_ru.properties new file mode 100644 index 0000000..c5e2d19 --- /dev/null +++ b/juice/welcome/messages/messages_ru.properties @@ -0,0 +1,20 @@ +loginAccountTitle=Sign in + +doRegister=Sign up +doForgotPassword=Forgot password? +noAccount=Not a member? + +emailForgotTitle=Reset +emailForgotTitle1=your password +emailInstruction=Please enter your email address. +emailInstruction1=We will send you an email to reset your password. +doSubmit=Send email +backToLogin=Go back +haveAccount=Have an account? + +registerTitle=Join +registerTitle1=The Community + +emailVerifyTitle=Verify your email +emailVerifyInstruction1=You need to verify your email address to activate your account. An email with instructions to verify your email address has been sent to you +emailVerifyInstruction3=Re-send the email \ No newline at end of file diff --git a/juice/welcome/resources/apple.svg b/juice/welcome/resources/apple.svg new file mode 100644 index 0000000..fe11cc5 --- /dev/null +++ b/juice/welcome/resources/apple.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/welcome/resources/css/styles.css b/juice/welcome/resources/css/styles.css new file mode 100644 index 0000000..d32ffef --- /dev/null +++ b/juice/welcome/resources/css/styles.css @@ -0,0 +1,330 @@ +.login-pf { + height: 100%; + background: none; +} + +body { + background: url(../juice-bc.svg); + background-repeat: no-repeat; + background-color: #202020; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +body::before { + position: absolute; + width: 56px; + height: 56px; + content: ""; + top: 56px; + left: 63px; + background: url(../juice.svg); + background-repeat: no-repeat; +} + +.login-pf-page { + padding-top: 0px; +} + +.h1, .h2, .h3, h1, h2, h3 { + margin-top: 0px; + margin-bottom: 0px; +} + +#kc-header-wrapper { + padding: 0px 10px; +} + +.card-login { + margin: 0 auto; + box-shadow: -8px -8px 32px rgba(101, 101, 101, 0.12), -2px -2px 16px rgba(101, 101, 101, 0.24), 4px 4px 16px rgba(0, 0, 0, 0.3), 8px 8px 32px rgba(0, 0, 0, 0.1); + padding: 56px; + min-width: 440px; + max-width: 440px; + border-radius: 16px; + background-color: #212121; +} + +.group-form { + padding: 32px 0px; +} + +.login-sign-button { + display: flex; + justify-content: center; + align-items: center; + height: 56px; + margin-bottom: 24px; + background: #212121; + box-shadow: -2px -2px 4px rgba(101, 101, 101, 0.12), -1px -1px 3px rgba(101, 101, 101, 0.24), 1px 1px 2px rgba(0, 0, 0, 0.3), 2px 2px 4px rgba(0, 0, 0, 0.2); + border-radius: 4px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 16px; + line-height: 24px; + text-align: center; + color: #BDBFBF; + cursor: pointer; +} +.login-sign-button:hover, +.login-sign-button:focus { + background: #292929; +} + +.login-sign-image { + background: url(../apple.svg); + width: 24px; + height: 24px; + margin-right: 16px; +} + +.login-sign-image.apple { + background: url(../apple.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.google { + background: url(../google.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.facebook { + background: url(../facebook.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.linkedin { + background: url(../linkedin.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-sign-image.twitter { + background: url(../twitter.svg); + background-size: contain; + background-repeat: no-repeat; +} + +.login-social-sign-container { + display: flex; + justify-content: space-between; +} + +.login-social-sign-button { + display: flex; + justify-content: center; + align-items: center; + width: 156px; + height: 56px; + background: #212121; + border: 1px solid #292929; + box-sizing: border-box; + border-radius: 4px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 16px; + line-height: 24px; + text-align: center; + color: #BDBFBF; + cursor: pointer; +} +.login-social-sign-button:hover, +.login-social-sign-button:focus { + background: #292929; +} +.large-social-sign-button:hover { + color: #BDBFBF; + text-decoration: none; +} + +.login-sign-link { + padding-left: 3px; + font-weight: 500; +} + +.separate-text { + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 16px; + line-height: 24px; + display: flex; + align-items: center; + text-align: center; + color: #98989E; +} + +.separate-text::before { + width: 147px; + height: 1px; + margin-right: 8px; + content: ""; + background-color: #333333; +} +.separate-text::after { + width: 147px; + height: 1px; + margin-left: 8px; + content: ""; + background-color: #333333; +} + +.large-social-sign-button { + width: 328px; +} + +.signup-terms-container { + display: flex; + justify-content: center; +} + +.login-pf-page-header { + background: url(../img/juice.svg); + background-position-x: 63px; + background-position-y: 56px; + background-repeat: no-repeat; +} +.header-login-welcome { + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #98989E; +} +.header-login-title { + padding-top: 4px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 38px; + line-height: 48px; + letter-spacing: -0.75px; + background: linear-gradient(180deg, #FF9900 0%, #FF7100 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +#kc-page-title{ + text-align: left; +} + +#kc-form-buttons { + margin-top: 32px; + margin-bottom: 32px; +} + +.pf-c-button { + height: 56px; + background: #181818; + box-shadow: -4px -4px 8px rgba(101, 101, 101, 0.08), -1px -1px 3px rgba(101, 101, 101, 0.12), 1px 1px 4px rgba(0, 0, 0, 0.3), 4px 4px 8px rgba(0, 0, 0, 0.4); + border-radius: 4px; + font-family: Swis721 Md BT; + font-style: normal; + font-weight: 500; + font-size: 16px; + line-height: 24px; + text-align: center; + color: #BDBFBF; +} + +.pf-c-form-control{ + position: relative; + min-height: 56px; + border: 1px solid #333333; + box-sizing: border-box; + border-radius: 4px; + background: #212121; + font-family: Swis721 Th BT; + padding-left: 16px; + font-style: normal; + font-weight: 300; + font-size: 16px; + line-height: 24px; + color: #757575; +} + +.form-group { + position: relative; + margin-bottom: 24px; +} + +#input-error { + margin-top: 8px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 14px; + line-height: 20px; + color: #BDBFBF; +} + + +.pf-c-form-control:focus, .pf-c-form-control:hover{ + outline: none; + border: 1px solid rgba(255, 113, 0, 1); + caret-color: rgba(255, 113, 0, 1); + /* font-family: Swis721 Th BT; */ +} +.pf-c-form-control[aria-invalid="true"] { + border: 1px solid rgba(245, 46, 46, 1); + background: #212121; +} +.login-form-input-label { + position: absolute; + display: none; + padding: 0px 4px; + top: -10px; + left: 12px; + color:rgba(255, 113, 0, 1); + z-index: 100; + background-color: #212121; +} +.pf-c-form-control:focus ~ .login-form-input-label, +.pf-c-form-control:hover ~ .login-form-input-label, +.pf-c-form-control[aria-invalid="true"] ~ .login-form-input-label { + display: block; +} + +.pf-c-form-control[aria-invalid="true"] ~ .login-form-input-label { + color: rgba(245, 46, 46, 1); +} + +.juice-footer { + position: absolute; + right: 56px; + bottom: 56px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 12px; + line-height: 16px; + color: #BDBFBF; +} + +.juice-footer span { + font-family: Swis721 Md BT; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 20px; + color: #BDBFBF; +} + +.signup-terms { + width: 290px; + font-family: Swis721 Th BT; + font-style: normal; + font-weight: 300; + font-size: 12px; + line-height: 16px; + text-align: center; + color: #BDBFBF; +} diff --git a/juice/welcome/resources/facebook.svg b/juice/welcome/resources/facebook.svg new file mode 100644 index 0000000..0c95dd9 --- /dev/null +++ b/juice/welcome/resources/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/welcome/resources/google.svg b/juice/welcome/resources/google.svg new file mode 100644 index 0000000..c3ac925 --- /dev/null +++ b/juice/welcome/resources/google.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/juice/welcome/resources/js/realm.js b/juice/welcome/resources/js/realm.js new file mode 100644 index 0000000..fad9d27 --- /dev/null +++ b/juice/welcome/resources/js/realm.js @@ -0,0 +1,3219 @@ +function getAccess(Auth, Current, role) { + if (!Current.realm)return false; + var realmAccess = Auth.user && Auth.user['realm_access']; + if (realmAccess) { + realmAccess = realmAccess[Current.realm.realm]; + if (realmAccess) { + return realmAccess.indexOf(role) >= 0; + } + } + return false; +} + +function getAccessObject(Auth, Current) { + return { + get createRealm() { + return Auth.user && Auth.user.createRealm; + }, + + get queryUsers() { + return getAccess(Auth, Current, 'query-users') || this.viewUsers; + }, + + get queryGroups() { + return getAccess(Auth, Current, 'query-groups') || this.viewUsers; + }, + + get queryClients() { + return getAccess(Auth, Current, 'query-clients') || this.viewClients; + }, + + get viewRealm() { + return getAccess(Auth, Current, 'view-realm') || getAccess(Auth, Current, 'manage-realm') || this.manageRealm; + }, + + get viewClients() { + return getAccess(Auth, Current, 'view-clients') || getAccess(Auth, Current, 'manage-clients') || this.manageClients; + }, + + get viewUsers() { + return getAccess(Auth, Current, 'view-users') || getAccess(Auth, Current, 'manage-users') || this.manageClients; + }, + + get viewEvents() { + return getAccess(Auth, Current, 'view-events') || getAccess(Auth, Current, 'manage-events') || this.manageClients; + }, + + get viewIdentityProviders() { + return getAccess(Auth, Current, 'view-identity-providers') || getAccess(Auth, Current, 'manage-identity-providers') || this.manageIdentityProviders; + }, + + get viewAuthorization() { + return getAccess(Auth, Current, 'view-authorization') || this.manageAuthorization; + }, + + get manageRealm() { + return getAccess(Auth, Current, 'manage-realm'); + }, + + get manageClients() { + return getAccess(Auth, Current, 'manage-clients'); + }, + + get manageUsers() { + return getAccess(Auth, Current, 'manage-users'); + }, + + get manageEvents() { + return getAccess(Auth, Current, 'manage-events'); + }, + + get manageIdentityProviders() { + return getAccess(Auth, Current, 'manage-identity-providers'); + }, + + get manageAuthorization() { + return getAccess(Auth, Current, 'manage-authorization'); + }, + + get impersonation() { + return getAccess(Auth, Current, 'impersonation'); + } + }; +} + + +module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo, RealmSpecificLocalizationTexts) { + $scope.authUrl = authUrl; + $scope.resourceUrl = resourceUrl; + $scope.auth = Auth; + $scope.serverInfo = ServerInfo.get(); + + $scope.access = getAccessObject(Auth, Current); + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.fragment = $location.path(); + $scope.path = $location.path().substring(1).split("/"); + }); + + $scope.$watch(function() { + return Current.realm; + }, function() { + if(Current.realm !== null && currentRealm !== Current.realm.id) { + currentRealm = Current.realm.id; + translateProvider.translations(locale, resourceBundle); + RealmSpecificLocalizationTexts.get({id: currentRealm, locale: locale}, function (localizationTexts) { + translateProvider.translations(locale, localizationTexts.toJSON()); + }) + } + }) +}); + +module.controller('HomeCtrl', function(Realm, Auth, Current, $location) { + + Realm.query(null, function(realms) { + var realm; + if (realms.length == 1) { + realm = realms[0]; + } else if (realms.length == 2) { + if (realms[0].realm == Auth.user.realm) { + realm = realms[1]; + } else if (realms[1].realm == Auth.user.realm) { + realm = realms[0]; + } + } + if (realm) { + Current.realms = realms; + Current.realm = realm; + var access = getAccessObject(Auth, Current); + if (access.viewRealm || access.manageRealm) { + $location.url('/realms/' + realm.realm ); + } else if (access.queryClients) { + $location.url('/realms/' + realm.realm + "/clients"); + } else if (access.viewIdentityProviders) { + $location.url('/realms/' + realm.realm + "/identity-provider-settings"); + } else if (access.queryUsers) { + $location.url('/realms/' + realm.realm + "/users"); + } else if (access.queryGroups) { + $location.url('/realms/' + realm.realm + "/groups"); + } else if (access.viewEvents) { + $location.url('/realms/' + realm.realm + "/events"); + } + } else { + $location.url('/realms'); + } + }); +}); + +module.controller('RealmTabCtrl', function(Dialog, $scope, Current, Realm, Notifications, $location) { + $scope.removeRealm = function() { + Dialog.confirmDelete(Current.realm.realm, 'realm', function() { + Realm.remove({ id : Current.realm.realm }, function() { + Current.realms = Realm.query(); + Notifications.success("The realm has been deleted."); + $location.url("/"); + }); + }); + }; +}); + +module.controller('ServerInfoCtrl', function($scope, ServerInfo) { + ServerInfo.reload(); + + $scope.serverInfo = ServerInfo.get(); + + $scope.$watch($scope.serverInfo, function() { + $scope.providers = []; + for(var spi in $scope.serverInfo.providers) { + var p = angular.copy($scope.serverInfo.providers[spi]); + p.name = spi; + $scope.providers.push(p) + } + }); + + $scope.serverInfoReload = function() { + ServerInfo.reload(); + } +}); + +module.controller('RealmListCtrl', function($scope, Realm, Current) { + $scope.realms = Realm.query(); + Current.realms = $scope.realms; +}); + +module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $location) { +// Current.realms = Realm.get(); + $scope.current = Current; + + $scope.changeRealm = function(selectedRealm) { + $location.url("/realms/" + selectedRealm); + } +}); + +module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, $location, $route, Dialog, Notifications, Auth, $modal) { + console.log('RealmCreateCtrl'); + + Current.realm = null; + + $scope.realm = { + enabled: true + }; + + $scope.changed = false; + $scope.files = []; + + var oldCopy = angular.copy($scope.realm); + + $scope.importFile = function($fileContent){ + $scope.realm = angular.copy(JSON.parse($fileContent)); + $scope.importing = true; + }; + + $scope.viewImportDetails = function() { + $modal.open({ + templateUrl: resourceUrl + '/partials/modal/view-object.html', + controller: 'ObjectModalCtrl', + resolve: { + object: function () { + return $scope.realm; + } + } + }) + }; + + $scope.$watch('realm', function() { + if (!angular.equals($scope.realm, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.$watch('realm.realm', function() { + $scope.realm.id = $scope.realm.realm; + }, true); + + $scope.save = function() { + var realmCopy = angular.copy($scope.realm); + Realm.create(realmCopy, function() { + Notifications.success("The realm has been created."); + + Auth.refreshPermissions(function() { + $scope.$apply(function() { + $location.url("/realms/" + realmCopy.realm); + }); + }); + }); + }; + + $scope.cancel = function() { + $location.url("/"); + }; + + $scope.reset = function() { + $route.reload(); + } +}); + +module.controller('ObjectModalCtrl', function($scope, object) { + $scope.object = object; +}); + +module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $location, $window, Dialog, Notifications, Auth) { + $scope.createRealm = !realm.realm; + $scope.serverInfo = serverInfo; + $scope.realmName = realm.realm; + $scope.disableRename = realm.realm == masterRealm; + $scope.authServerUrl = authServerUrl; + + if (Current.realm == null || Current.realm.realm != realm.realm) { + for (var i = 0; i < Current.realms.length; i++) { + if (realm.realm == Current.realms[i].realm) { + Current.realm = Current.realms[i]; + break; + } + } + } + for (var i = 0; i < Current.realms.length; i++) { + if (Current.realms[i].realm == realm.realm) { + Current.realm = Current.realms[i]; + } + } + $scope.realm = angular.copy(realm); + + var oldCopy = angular.copy($scope.realm); + + $scope.changed = $scope.create; + + $scope.$watch('realm', function() { + if (!angular.equals($scope.realm, oldCopy)) { + $scope.changed = true; + } + }, true); + $scope.$watch('realmName', function() { + if (!angular.equals($scope.realmName, oldCopy.realm)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + var realmCopy = angular.copy($scope.realm); + realmCopy.realm = $scope.realmName; + $scope.changed = false; + var nameChanged = !angular.equals($scope.realmName, oldCopy.realm); + var oldName = oldCopy.realm; + Realm.update({ id : oldCopy.realm}, realmCopy, function () { + var data = Realm.query(function () { + Current.realms = data; + for (var i = 0; i < Current.realms.length; i++) { + if (Current.realms[i].realm == realmCopy.realm) { + Current.realm = Current.realms[i]; + oldCopy = angular.copy($scope.realm); + } + } + }); + + if (nameChanged) { + console.debug(Auth); + console.debug(Auth.authz.tokenParsed.iss); + + if (Auth.authz.tokenParsed.iss.endsWith(masterRealm)) { + Auth.refreshPermissions(function () { + Auth.refreshPermissions(function () { + Notifications.success("Your changes have been saved to the realm."); + $scope.$apply(function () { + $location.url("/realms/" + realmCopy.realm); + }); + }); + }); + } else { + delete Auth.authz.token; + delete Auth.authz.refreshToken; + + var newLocation = $window.location.href.replace('/' + oldName + '/', '/' + realmCopy.realm + '/') + .replace('/realms/' + oldName, '/realms/' + realmCopy.realm); + window.location.replace(newLocation); + } + } else { + $location.url("/realms/" + realmCopy.realm); + Notifications.success("Your changes have been saved to the realm."); + } + }); + }; + + $scope.reset = function() { + $scope.realm = angular.copy(oldCopy); + $scope.changed = false; + }; + + $scope.cancel = function() { + window.history.back(); + }; +}); + +function genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, url) { + $scope.realm = angular.copy(realm); + $scope.serverInfo = serverInfo; + $scope.registrationAllowed = $scope.realm.registrationAllowed; + + var oldCopy = angular.copy($scope.realm); + + $scope.changed = false; + + $scope.$watch('realm', function() { + if (!angular.equals($scope.realm, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + var realmCopy = angular.copy($scope.realm); + console.log('updating realm...'); + $scope.changed = false; + console.log('oldCopy.realm - ' + oldCopy.realm); + Realm.update({ id : oldCopy.realm}, realmCopy, function () { + $route.reload(); + Notifications.success("Your changes have been saved to the realm."); + $scope.registrationAllowed = $scope.realm.registrationAllowed; + }); + }; + + $scope.reset = function() { + $scope.realm = angular.copy(oldCopy); + $scope.changed = false; + }; + + $scope.cancel = function() { + $route.reload(); + }; + +} + +module.controller('DefenseHeadersCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications) { + genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, "/realms/" + realm.realm + "/defense/headers"); +}); + +module.controller('RealmLoginSettingsCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications) { + // KEYCLOAK-5474: Make sure duplicateEmailsAllowed is disabled if loginWithEmailAllowed + $scope.$watch('realm.loginWithEmailAllowed', function() { + if ($scope.realm.loginWithEmailAllowed) { + $scope.realm.duplicateEmailsAllowed = false; + } + }); + + genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, "/realms/" + realm.realm + "/login-settings"); +}); + +module.controller('RealmOtpPolicyCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications) { + $scope.optionsDigits = [ 6, 8 ]; + + genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, "/realms/" + realm.realm + "/authentication/otp-policy"); +}); + +module.controller('RealmWebAuthnPolicyCtrl', function ($scope, Current, Realm, realm, serverInfo, $http, $route, $location, Dialog, Notifications) { + + $scope.deleteAcceptableAaguid = function(index) { + $scope.realm.webAuthnPolicyAcceptableAaguids.splice(index, 1); + }; + + $scope.addAcceptableAaguid = function() { + $scope.realm.webAuthnPolicyAcceptableAaguids.push($scope.newAcceptableAaguid); + $scope.newAcceptableAaguid = ""; + }; + + // Just for case the user fill particular URL with disabled WebAuthn feature. + $scope.redirectIfWebAuthnDisabled = function () { + if (!serverInfo.featureEnabled('WEB_AUTHN')) { + $location.url("/realms/" + $scope.realm.realm + "/authentication"); + } + }; + + genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, "/realms/" + realm.realm + "/authentication/webauthn-policy"); +}); + +module.controller('RealmWebAuthnPasswordlessPolicyCtrl', function ($scope, Current, Realm, realm, serverInfo, $http, $route, $location, Dialog, Notifications) { + + $scope.deleteAcceptableAaguid = function(index) { + $scope.realm.webAuthnPolicyPasswordlessAcceptableAaguids.splice(index, 1); + }; + + $scope.addAcceptableAaguid = function() { + $scope.realm.webAuthnPolicyPasswordlessAcceptableAaguids.push($scope.newAcceptableAaguid); + $scope.newAcceptableAaguid = ""; + }; + + // Just for case the user fill particular URL with disabled WebAuthn feature. + $scope.redirectIfWebAuthnDisabled = function () { + if (!serverInfo.featureEnabled('WEB_AUTHN')) { + $location.url("/realms/" + $scope.realm.realm + "/authentication"); + } + }; + + genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, "/realms/" + realm.realm + "/authentication/webauthn-policy-passwordless"); +}); + +module.controller('RealmThemeCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications) { + genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, "/realms/" + realm.realm + "/theme-settings"); + + $scope.supportedLocalesOptions = { + 'multiple' : true, + 'simple_tags' : true, + 'tags' : [] + }; + + updateSupported(); + + function localeForTheme(type, name) { + name = name || 'base'; + for (var i = 0; i < serverInfo.themes[type].length; i++) { + if (serverInfo.themes[type][i].name == name) { + return serverInfo.themes[type][i].locales || []; + } + } + return []; + } + + function updateSupported() { + if ($scope.realm.internationalizationEnabled) { + var accountLocales = localeForTheme('account', $scope.realm.accountTheme); + var loginLocales = localeForTheme('login', $scope.realm.loginTheme); + var emailLocales = localeForTheme('email', $scope.realm.emailTheme); + + var supportedLocales = []; + for (var i = 0; i < accountLocales.length; i++) { + var l = accountLocales[i]; + if (loginLocales.indexOf(l) >= 0 && emailLocales.indexOf(l) >= 0) { + supportedLocales.push(l); + } + } + + $scope.supportedLocalesOptions.tags = supportedLocales; + + if (!$scope.realm.supportedLocales) { + $scope.realm.supportedLocales = supportedLocales; + } else { + for (var i = 0; i < $scope.realm.supportedLocales.length; i++) { + if (supportedLocales.indexOf($scope.realm.supportedLocales[i]) == -1) { + $scope.realm.supportedLocales = supportedLocales; + } + } + } + + if (!$scope.realm.defaultLocale || supportedLocales.indexOf($scope.realm.defaultLocale) == -1) { + $scope.realm.defaultLocale = 'en'; + } + } + } + + $scope.$watch('realm.loginTheme', updateSupported); + $scope.$watch('realm.accountTheme', updateSupported); + $scope.$watch('realm.emailTheme', updateSupported); + $scope.$watch('realm.internationalizationEnabled', updateSupported); +}); + +module.controller('RealmLocalizationCtrl', function($scope, Current, $location, Realm, realm, serverInfo, Notifications, RealmSpecificLocales, realmSpecificLocales, RealmSpecificLocalizationTexts, RealmSpecificLocalizationText, Dialog, $translate){ + $scope.realm = realm; + $scope.realmSpecificLocales = realmSpecificLocales; + $scope.newLocale = null; + $scope.selectedRealmSpecificLocales = null; + $scope.localizationTexts = null; + + $scope.createLocale = function() { + if(!$scope.newLocale) { + Notifications.error($translate.instant('missing-locale')); + return; + } + $scope.realmSpecificLocales.push($scope.newLocale) + $scope.selectedRealmSpecificLocales = $scope.newLocale; + $scope.newLocale = null; + $location.url('/create/localization/' + realm.realm + '/' + $scope.selectedRealmSpecificLocales); + } + + $scope.$watch(function() { + return $scope.selectedRealmSpecificLocales; + }, function() { + if($scope.selectedRealmSpecificLocales != null) { + $scope.updateRealmSpecificLocalizationTexts(); + } + }) + + $scope.updateRealmSpecificLocales = function() { + RealmSpecificLocales.get({id: realm.realm}, function (updated) { + $scope.realmSpecificLocales = updated; + }) + } + + $scope.updateRealmSpecificLocalizationTexts = function() { + RealmSpecificLocalizationTexts.get({id: realm.realm, locale: $scope.selectedRealmSpecificLocales }, function (updated) { + $scope.localizationTexts = updated; + }) + } + + $scope.removeLocalizationText = function(key) { + Dialog.confirmDelete(key, 'localization text', function() { + RealmSpecificLocalizationText.remove({ + realm: realm.realm, + locale: $scope.selectedRealmSpecificLocales, + key: key + }, function () { + $scope.updateRealmSpecificLocalizationTexts(); + Notifications.success($translate.instant('localization-text.remove.success')); + }); + }); + } +}); + +module.controller('RealmLocalizationUploadCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications, $upload, $translate){ + $scope.realm = realm; + $scope.locale = null; + $scope.files = []; + + $scope.onFileSelect = function($files) { + $scope.files = $files; + }; + + $scope.reset = function() { + $scope.locale = null; + $scope.files = null; + }; + + $scope.save = function() { + + if(!$scope.files || $scope.files.length === 0) { + Notifications.error($translate.instant('missing-file')); + return; + } + //$files: an array of files selected, each file has name, size, and type. + for (var i = 0; i < $scope.files.length; i++) { + var $file = $scope.files[i]; + $scope.upload = $upload.upload({ + url: authUrl + '/admin/realms/' + realm.realm + '/localization/' + $scope.locale, + file: $file + }).then(function(response) { + $scope.reset(); + Notifications.success($translate.instant('localization-file.upload.success')); + }).catch(function() { + Notifications.error($translate.instant('localization-file.upload.error')); + }); + } + }; + +}); + +module.controller('RealmLocalizationDetailCtrl', function($scope, Current, $location, Realm, realm, Notifications, locale, key, RealmSpecificLocalizationText, localizationText, $translate){ + $scope.realm = realm; + $scope.locale = locale; + $scope.key = key; + $scope.value = ((localizationText)? localizationText.content : null); + + $scope.create = !key; + + $scope.save = function() { + if ($scope.create) { + RealmSpecificLocalizationText.save({ + realm: realm.realm, + locale: $scope.locale, + key: $scope.key + }, $scope.value, function (data, headers) { + $location.url("/realms/" + realm.realm + "/localization"); + Notifications.success($translate.instant('localization-text.create.success')); + }); + } else { + RealmSpecificLocalizationText.save({ + realm: realm.realm, + locale: $scope.locale, + key: $scope.key + }, $scope.value, function (data, headers) { + $location.url("/realms/" + realm.realm + "/localization"); + Notifications.success($translate.instant('localization-text.update.success')); + }); + } + }; + + $scope.cancel = function () { + $location.url("/realms/" + realm.realm + "/localization"); + }; + +}); + +module.controller('RealmCacheCtrl', function($scope, realm, RealmClearUserCache, RealmClearRealmCache, RealmClearKeysCache, Notifications) { + $scope.realm = angular.copy(realm); + + $scope.clearUserCache = function() { + RealmClearUserCache.save({ realm: realm.realm}, function () { + Notifications.success("User cache cleared"); + }); + } + + $scope.clearRealmCache = function() { + RealmClearRealmCache.save({ realm: realm.realm}, function () { + Notifications.success("Realm cache cleared"); + }); + } + + $scope.clearKeysCache = function() { + RealmClearKeysCache.save({ realm: realm.realm}, function () { + Notifications.success("Public keys cache cleared"); + }); + } + + +}); + +module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $http, $location, $route, Dialog, Notifications, serverInfo) { + var parse = function(policyString) { + var policies = []; + if (!policyString || policyString.length == 0){ + return policies; + } + + var policyArray = policyString.split(" and "); + + for (var i = 0; i < policyArray.length; i ++){ + var policyToken = policyArray[i]; + var id; + var value; + if (policyToken.indexOf('(') == -1) { + id = policyToken.trim(); + value = null; + } else { + id = policyToken.substring(0, policyToken.indexOf('(')); + value = policyToken.substring(policyToken.indexOf('(') + 1, policyToken.lastIndexOf(')')).trim(); + } + + for (var j = 0; j < serverInfo.passwordPolicies.length; j++) { + if (serverInfo.passwordPolicies[j].id == id) { + // clone + var p = JSON.parse(JSON.stringify(serverInfo.passwordPolicies[j])); + + p.value = value && value || p.defaultValue; + policies.push(p); + } + } + } + return policies; + }; + + var toString = function(policies) { + if (!policies || policies.length == 0) { + return ""; + } + var policyString = ""; + for (var i = 0; i < policies.length; i++) { + policyString += policies[i].id + '(' + policies[i].value + ')'; + if (i != policies.length - 1) { + policyString += ' and '; + } + } + return policyString; + } + + $scope.realm = realm; + $scope.serverInfo = serverInfo; + + $scope.changed = false; + console.log(JSON.stringify(parse(realm.passwordPolicy))); + $scope.policy = parse(realm.passwordPolicy); + var oldCopy = angular.copy($scope.policy); + + $scope.$watch('policy', function() { + $scope.changed = ! angular.equals($scope.policy, oldCopy); + }, true); + + $scope.addPolicy = function(policy){ + policy.value = policy.defaultValue; + if (!$scope.policy) { + $scope.policy = []; + } + $scope.policy.push(policy); + } + + $scope.removePolicy = function(index){ + $scope.policy.splice(index, 1); + } + + $scope.save = function() { + $scope.realm.passwordPolicy = toString($scope.policy); + console.log($scope.realm.passwordPolicy); + + Realm.update($scope.realm, function () { + $route.reload(); + Notifications.success("Your changes have been saved to the realm."); + }); + }; + + $scope.reset = function() { + $route.reload(); + }; +}); + +module.controller('RealmDefaultRolesCtrl', function ($scope, $route, Realm, realm, roles, Notifications, ClientRole, Client) { + + console.log('RealmDefaultRolesCtrl'); + + $scope.realm = realm; + + $scope.availableRealmRoles = []; + $scope.selectedRealmRoles = []; + $scope.selectedRealmDefRoles = []; + + $scope.availableClientRoles = []; + $scope.selectedClientRoles = []; + $scope.selectedClientDefRoles = []; + + if (!$scope.realm.hasOwnProperty('defaultRoles') || $scope.realm.defaultRoles === null) { + $scope.realm.defaultRoles = []; + } + + // Populate available roles. Available roles are neither already assigned + for (var i = 0; i < roles.length; i++) { + var item = roles[i].name; + + if ($scope.realm.defaultRoles.indexOf(item) < 0) { + $scope.availableRealmRoles.push(item); + } + } + + $scope.addRealmDefaultRole = function () { + + // Remove selected roles from the Available roles and add them to realm default roles (move from left to right). + for (var i = 0; i < $scope.selectedRealmRoles.length; i++) { + var selectedRole = $scope.selectedRealmRoles[i]; + + $scope.realm.defaultRoles.push(selectedRole); + + var index = $scope.availableRealmRoles.indexOf(selectedRole); + if (index > -1) { + $scope.availableRealmRoles.splice(index, 1); + } + } + + $scope.selectedRealmRoles = []; + + // Update/save the realm with new default roles. + Realm.update($scope.realm, function () { + Notifications.success("Realm default roles updated."); + }); + }; + + $scope.deleteRealmDefaultRole = function () { + + // Remove selected roles from the realm default roles and add them to available roles (move from right to left). + for (var i = 0; i < $scope.selectedRealmDefRoles.length; i++) { + $scope.availableRealmRoles.push($scope.selectedRealmDefRoles[i]); + + var index = $scope.realm.defaultRoles.indexOf($scope.selectedRealmDefRoles[i]); + if (index > -1) { + $scope.realm.defaultRoles.splice(index, 1); + } + } + + $scope.selectedRealmDefRoles = []; + + // Update/save the realm with new default roles. + //var realmCopy = angular.copy($scope.realm); + Realm.update($scope.realm, function () { + Notifications.success("Realm default roles updated."); + }); + }; + + $scope.changeClient = function (client) { + $scope.selectedClient = client; + $scope.selectedClientRoles = []; + $scope.selectedClientDefRoles = []; + if (!client || !client.id) { + $scope.selectedClient = null; + return; + } + + // Populate available roles for selected client + if ($scope.selectedClient) { + ClientRole.query({realm: $scope.realm.realm, client: $scope.selectedClient.id}, function (appDefaultRoles) { + if (!$scope.selectedClient.hasOwnProperty('defaultRoles') || $scope.selectedClient.defaultRoles === null) { + $scope.selectedClient.defaultRoles = []; + } + + $scope.availableClientRoles = []; + console.log('default roles', appDefaultRoles); + for (var i = 0; i < appDefaultRoles.length; i++) { + + var roleName = appDefaultRoles[i].name; + if ($scope.selectedClient.defaultRoles.indexOf(roleName) < 0) { + $scope.availableClientRoles.push(roleName); + } + } + }); + } else { + $scope.availableClientRoles = null; + } + }; + + $scope.addClientDefaultRole = function () { + + // Remove selected roles from the app available roles and add them to app default roles (move from left to right). + for (var i = 0; i < $scope.selectedClientRoles.length; i++) { + var role = $scope.selectedClientRoles[i]; + + var idx = $scope.selectedClient.defaultRoles.indexOf(role); + if (idx < 0) { + $scope.selectedClient.defaultRoles.push(role); + } + + idx = $scope.availableClientRoles.indexOf(role); + + if (idx != -1) { + $scope.availableClientRoles.splice(idx, 1); + } + } + + $scope.selectedClientRoles = []; + + // Update/save the selected client with new default roles. + delete $scope.selectedClient.text; + Client.update({ + realm: $scope.realm.realm, + client: $scope.selectedClient.id + }, $scope.selectedClient, function () { + Notifications.success("Your changes have been saved to the client."); + Client.get({realm: realm.realm, client: $scope.selectedClient.id}, function(response) { + response.text = response.clientId; + $scope.changeClient(response); + }); + }); + }; + + $scope.rmClientDefaultRole = function () { + + // Remove selected roles from the app default roles and add them to app available roles (move from right to left). + for (var i = 0; i < $scope.selectedClientDefRoles.length; i++) { + var role = $scope.selectedClientDefRoles[i]; + var idx = $scope.selectedClient.defaultRoles.indexOf(role); + if (idx != -1) { + $scope.selectedClient.defaultRoles.splice(idx, 1); + } + idx = $scope.availableClientRoles.indexOf(role); + if (idx < 0) { + $scope.availableClientRoles.push(role); + } + } + + $scope.selectedClientDefRoles = []; + + // Update/save the selected client with new default roles. + delete $scope.selectedClient.text; + Client.update({ + realm: $scope.realm.realm, + client: $scope.selectedClient.id + }, $scope.selectedClient, function () { + Notifications.success("Your changes have been saved to the client."); + Client.get({realm: realm.realm, client: $scope.selectedClient.id}, function(response) { + response.text = response.clientId; + $scope.changeClient(response); + }); + }); + }; + + clientSelectControl($scope, $route.current.params.realm, Client); +}); + + + +module.controller('IdentityProviderTabCtrl', function(Dialog, $scope, Current, Notifications, $location) { + $scope.removeIdentityProvider = function() { + Dialog.confirmDelete($scope.identityProvider.alias, 'provider', function() { + $scope.identityProvider.$remove({ + realm : Current.realm.realm, + alias : $scope.identityProvider.alias + }, function() { + $location.url("/realms/" + Current.realm.realm + "/identity-provider-settings"); + Notifications.success("The identity provider has been deleted."); + }); + }); + }; +}); + +module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, $route, realm, instance, providerFactory, IdentityProvider, serverInfo, authFlows, $location, Notifications, Dialog) { + $scope.realm = angular.copy(realm); + + $scope.initSamlProvider = function() { + $scope.nameIdFormats = [ + /* + { + format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + name: "Transient" + }, + */ + { + format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + name: "Persistent" + + }, + { + format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", + name: "Email" + + }, + { + format: "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos", + name: "Kerberos" + + }, + { + format: "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", + name: "X.509 Subject Name" + + }, + { + format: "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName", + name: "Windows Domain Qualified Name" + + }, + { + format: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + name: "Unspecified" + + } + ]; + $scope.signatureAlgorithms = [ + "RSA_SHA1", + "RSA_SHA256", + "RSA_SHA256_MGF1", + "RSA_SHA512", + "RSA_SHA512_MGF1", + "DSA_SHA1" + ]; + $scope.xmlKeyNameTranformers = [ + "NONE", + "KEY_ID", + "CERT_SUBJECT" + ]; + $scope.principalTypes = [ + { + type: "SUBJECT", + name: "Subject NameID" + + }, + { + type: "ATTRIBUTE", + name: "Attribute [Name]" + + }, + { + type: "FRIENDLY_ATTRIBUTE", + name: "Attribute [Friendly Name]" + + } + ]; + if (instance && instance.alias) { + + } else { + $scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format; + $scope.identityProvider.config.principalType = $scope.principalTypes[0].type; + $scope.identityProvider.config.signatureAlgorithm = $scope.signatureAlgorithms[1]; + $scope.identityProvider.config.xmlSigKeyInfoKeyNameTransformer = $scope.xmlKeyNameTranformers[1]; + } + $scope.identityProvider.config.entityId = $scope.identityProvider.config.entityId || (authUrl + '/realms/' + realm.realm); + } + + $scope.hidePassword = true; + $scope.fromUrl = { + data: '' + }; + + if (instance && instance.alias) { + $scope.identityProvider = angular.copy(instance); + $scope.newIdentityProvider = false; + for (var i in serverInfo.identityProviders) { + var provider = serverInfo.identityProviders[i]; + + if (provider.id == instance.providerId) { + $scope.provider = provider; + } + } + } else { + $scope.identityProvider = {}; + $scope.identityProvider.config = {}; + $scope.identityProvider.alias = providerFactory.id; + $scope.identityProvider.providerId = providerFactory.id; + + $scope.identityProvider.enabled = true; + $scope.identityProvider.authenticateByDefault = false; + $scope.identityProvider.firstBrokerLoginFlowAlias = 'first broker login'; + $scope.identityProvider.config.useJwksUrl = 'true'; + $scope.identityProvider.config.syncMode = 'IMPORT'; + $scope.newIdentityProvider = true; + } + + $scope.changed = $scope.newIdentityProvider; + + $scope.$watch('identityProvider', function() { + if (!angular.equals($scope.identityProvider, instance)) { + $scope.changed = true; + } + }, true); + + + $scope.serverInfo = serverInfo; + + $scope.allProviders = angular.copy(serverInfo.identityProviders); + + $scope.configuredProviders = angular.copy(realm.identityProviders); + + removeUsedSocial(); + + $scope.authFlows = []; + for (var i=0 ; i 0) { + $scope.importUrl = true; + } else{ + $scope.importUrl = false; + } + }); + + $scope.$watch('configuredProviders', function(configuredProviders) { + if (configuredProviders) { + $scope.configuredProviders = angular.copy(configuredProviders); + + for (var j = 0; j < configuredProviders.length; j++) { + var configProvidedId = configuredProviders[j].providerId; + + for (var i in $scope.allProviders) { + var provider = $scope.allProviders[i]; + if (provider.id == configProvidedId) { + configuredProviders[j].provider = provider; + } + } + } + $scope.configuredProviders = angular.copy(configuredProviders); + } + }, true); + + $scope.callbackUrl = authServerUrl + "/realms/" + realm.realm + "/broker/"; + + $scope.addProvider = function(provider) { + $location.url("/create/identity-provider/" + realm.realm + "/" + provider.id); + }; + + $scope.save = function() { + if ($scope.newIdentityProvider) { + if (!$scope.identityProvider.alias) { + Notifications.error("You must specify an alias"); + return; + } + IdentityProvider.save({ + realm: $scope.realm.realm, alias: '' + }, $scope.identityProvider, function () { + $location.url("/realms/" + realm.realm + "/identity-provider-settings/provider/" + $scope.identityProvider.providerId + "/" + $scope.identityProvider.alias); + Notifications.success("The " + $scope.identityProvider.alias + " provider has been created."); + }); + } else { + IdentityProvider.update({ + realm: $scope.realm.realm, + alias: $scope.identityProvider.alias + }, $scope.identityProvider, function () { + $route.reload(); + Notifications.success("The " + $scope.identityProvider.alias + " provider has been updated."); + }); + } + }; + + $scope.cancel = function() { + if ($scope.newIdentityProvider) { + $location.url("/realms/" + realm.realm + "/identity-provider-settings"); + } else { + $route.reload(); + } + }; + + + $scope.reset = function() { + $scope.identityProvider = {}; + $scope.configuredProviders = angular.copy($scope.realm.identityProviders); + }; + + $scope.showPassword = function(flag) { + $scope.hidePassword = flag; + }; + + $scope.removeIdentityProvider = function(identityProvider) { + Dialog.confirmDelete(identityProvider.alias, 'provider', function() { + IdentityProvider.remove({ + realm : realm.realm, + alias : identityProvider.alias + }, function() { + $route.reload(); + Notifications.success("The identity provider has been deleted."); + }); + }); + }; + + // KEYCLOAK-5932: remove social providers that have already been defined + function removeUsedSocial() { + var i = $scope.allProviders.length; + while (i--) { + if ($scope.allProviders[i].groupName !== 'Social') continue; + if ($scope.configuredProviders != null) { + for (var j = 0; j < $scope.configuredProviders.length; j++) { + if ($scope.configuredProviders[j].providerId === $scope.allProviders[i].id) { + $scope.allProviders.splice(i, 1); + break; + } + } + } + } + }; + + if (instance && instance.alias) { + try { $scope.authnContextClassRefs = JSON.parse($scope.identityProvider.config.authnContextClassRefs || '[]'); } catch (e) { $scope.authnContextClassRefs = []; } + try { $scope.authnContextDeclRefs = JSON.parse($scope.identityProvider.config.authnContextDeclRefs || '[]'); } catch (e) { $scope.authnContextDeclRefs = []; } + } else { + $scope.authnContextClassRefs = []; + $scope.authnContextDeclRefs = []; + } + + $scope.deleteAuthnContextClassRef = function(index) { + $scope.authnContextClassRefs.splice(index, 1); + $scope.identityProvider.config.authnContextClassRefs = JSON.stringify($scope.authnContextClassRefs); + }; + + $scope.addAuthnContextClassRef = function() { + $scope.authnContextClassRefs.push($scope.newAuthnContextClassRef); + $scope.identityProvider.config.authnContextClassRefs = JSON.stringify($scope.authnContextClassRefs); + $scope.newAuthnContextClassRef = ""; + }; + + $scope.deleteAuthnContextDeclRef = function(index) { + $scope.authnContextDeclRefs.splice(index, 1); + $scope.identityProvider.config.authnContextDeclRefs = JSON.stringify($scope.authnContextDeclRefs); + }; + + $scope.addAuthnContextDeclRef = function() { + $scope.authnContextDeclRefs.push($scope.newAuthnContextDeclRef); + $scope.identityProvider.config.authnContextDeclRefs = JSON.stringify($scope.authnContextDeclRefs); + $scope.newAuthnContextDeclRef = ""; + }; +}); + +module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, $route, Dialog, Notifications, TimeUnit, TimeUnit2, serverInfo) { + $scope.realm = realm; + $scope.serverInfo = serverInfo; + $scope.actionTokenProviders = $scope.serverInfo.providers.actionTokenHandler.providers; + + $scope.realm.accessTokenLifespan = TimeUnit2.asUnit(realm.accessTokenLifespan); + $scope.realm.accessTokenLifespanForImplicitFlow = TimeUnit2.asUnit(realm.accessTokenLifespanForImplicitFlow); + $scope.realm.ssoSessionIdleTimeout = TimeUnit2.asUnit(realm.ssoSessionIdleTimeout); + $scope.realm.ssoSessionMaxLifespan = TimeUnit2.asUnit(realm.ssoSessionMaxLifespan); + $scope.realm.ssoSessionIdleTimeoutRememberMe = TimeUnit2.asUnit(realm.ssoSessionIdleTimeoutRememberMe); + $scope.realm.ssoSessionMaxLifespanRememberMe = TimeUnit2.asUnit(realm.ssoSessionMaxLifespanRememberMe); + $scope.realm.offlineSessionIdleTimeout = TimeUnit2.asUnit(realm.offlineSessionIdleTimeout); + // KEYCLOAK-7688 Offline Session Max for Offline Token + $scope.realm.offlineSessionMaxLifespan = TimeUnit2.asUnit(realm.offlineSessionMaxLifespan); + $scope.realm.clientSessionIdleTimeout = TimeUnit2.asUnit(realm.clientSessionIdleTimeout); + $scope.realm.clientSessionMaxLifespan = TimeUnit2.asUnit(realm.clientSessionMaxLifespan); + $scope.realm.clientOfflineSessionIdleTimeout = TimeUnit2.asUnit(realm.clientOfflineSessionIdleTimeout); + $scope.realm.clientOfflineSessionMaxLifespan = TimeUnit2.asUnit(realm.clientOfflineSessionMaxLifespan); + $scope.realm.accessCodeLifespan = TimeUnit2.asUnit(realm.accessCodeLifespan); + $scope.realm.accessCodeLifespanLogin = TimeUnit2.asUnit(realm.accessCodeLifespanLogin); + $scope.realm.accessCodeLifespanUserAction = TimeUnit2.asUnit(realm.accessCodeLifespanUserAction); + $scope.realm.actionTokenGeneratedByAdminLifespan = TimeUnit2.asUnit(realm.actionTokenGeneratedByAdminLifespan); + $scope.realm.actionTokenGeneratedByUserLifespan = TimeUnit2.asUnit(realm.actionTokenGeneratedByUserLifespan); + $scope.realm.attributes = realm.attributes + + var oldCopy = angular.copy($scope.realm); + $scope.changed = false; + + $scope.$watch('realm', function() { + if (!angular.equals($scope.realm, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.$watch('actionLifespanId', function () { + // changedActionLifespanId signals other watchers that we were merely + // changing the dropdown and we should not enable 'save' button + if ($scope.actionTokenAttribute && $scope.actionTokenAttribute.hasOwnProperty('time')) { + $scope.changedActionLifespanId = true; + } + + $scope.actionTokenAttribute = TimeUnit2.asUnit($scope.realm.attributes['actionTokenGeneratedByUserLifespan.' + $scope.actionLifespanId]); + }, true); + + $scope.$watch('actionTokenAttribute', function () { + if ($scope.actionLifespanId === null) return; + + if ($scope.changedActionLifespanId) { + $scope.changedActionLifespanId = false; + return; + } else { + $scope.changed = true; + } + + if ($scope.actionTokenAttribute !== null) { + $scope.realm.attributes['actionTokenGeneratedByUserLifespan.' + $scope.actionLifespanId] = $scope.actionTokenAttribute.toSeconds(); + } + }, true); + + $scope.changeRevokeRefreshToken = function() { + + }; + + $scope.save = function() { + $scope.realm.accessTokenLifespan = $scope.realm.accessTokenLifespan.toSeconds(); + $scope.realm.accessTokenLifespanForImplicitFlow = $scope.realm.accessTokenLifespanForImplicitFlow.toSeconds(); + $scope.realm.ssoSessionIdleTimeout = $scope.realm.ssoSessionIdleTimeout.toSeconds(); + $scope.realm.ssoSessionMaxLifespan = $scope.realm.ssoSessionMaxLifespan.toSeconds(); + $scope.realm.ssoSessionIdleTimeoutRememberMe = $scope.realm.ssoSessionIdleTimeoutRememberMe.toSeconds(); + $scope.realm.ssoSessionMaxLifespanRememberMe = $scope.realm.ssoSessionMaxLifespanRememberMe.toSeconds(); + $scope.realm.offlineSessionIdleTimeout = $scope.realm.offlineSessionIdleTimeout.toSeconds(); + // KEYCLOAK-7688 Offline Session Max for Offline Token + $scope.realm.offlineSessionMaxLifespan = $scope.realm.offlineSessionMaxLifespan.toSeconds(); + $scope.realm.clientSessionIdleTimeout = $scope.realm.clientSessionIdleTimeout.toSeconds(); + $scope.realm.clientSessionMaxLifespan = $scope.realm.clientSessionMaxLifespan.toSeconds(); + $scope.realm.clientOfflineSessionIdleTimeout = $scope.realm.clientOfflineSessionIdleTimeout.toSeconds(); + $scope.realm.clientOfflineSessionMaxLifespan = $scope.realm.clientOfflineSessionMaxLifespan.toSeconds(); + $scope.realm.accessCodeLifespan = $scope.realm.accessCodeLifespan.toSeconds(); + $scope.realm.accessCodeLifespanUserAction = $scope.realm.accessCodeLifespanUserAction.toSeconds(); + $scope.realm.accessCodeLifespanLogin = $scope.realm.accessCodeLifespanLogin.toSeconds(); + $scope.realm.actionTokenGeneratedByAdminLifespan = $scope.realm.actionTokenGeneratedByAdminLifespan.toSeconds(); + $scope.realm.actionTokenGeneratedByUserLifespan = $scope.realm.actionTokenGeneratedByUserLifespan.toSeconds(); + + Realm.update($scope.realm, function () { + $route.reload(); + Notifications.success("The changes have been saved to the realm."); + }); + }; + + $scope.resetToDefaultToken = function (actionTokenId) { + $scope.actionTokenAttribute = {}; + delete $scope.realm.attributes['actionTokenGeneratedByUserLifespan.' + $scope.actionLifespanId]; + //Only for UI effects, resets to the original state + $scope.actionTokenAttribute.unit = 'Minutes'; + } + + $scope.reset = function() { + $route.reload(); + }; +}); + +module.controller('ViewKeyCtrl', function($scope, key) { + $scope.key = key; +}); + +module.controller('RealmKeysCtrl', function($scope, Realm, realm, $http, $route, $location, Dialog, Notifications, serverInfo, keys, Components, $modal) { + $scope.realm = angular.copy(realm); + $scope.keys = keys.keys; + $scope.active = {}; + + Components.query({realm: realm.realm, + parent: realm.id, + type: 'org.keycloak.keys.KeyProvider' + }, function(data) { + for (var i = 0; i < keys.keys.length; i++) { + for (var j = 0; j < data.length; j++) { + if (keys.keys[i].providerId == data[j].id) { + keys.keys[i].provider = data[j]; + } + } + } + + for (var t in keys.active) { + for (var i = 0; i < keys.keys.length; i++) { + if (keys.active[t] == keys.keys[i].kid) { + $scope.active[t] = keys.keys[i]; + } + } + } + }); + + $scope.viewKey = function(key) { + $modal.open({ + templateUrl: resourceUrl + '/partials/modal/view-key.html', + controller: 'ViewKeyCtrl', + resolve: { + key: function () { + return key; + } + } + }) + } +}); + +module.controller('RealmKeysProvidersCtrl', function($scope, Realm, realm, $http, $route, $location, Dialog, Notifications, serverInfo, Components, $modal) { + $scope.realm = angular.copy(realm); + $scope.enableUpload = false; + + $scope.providers = serverInfo.componentTypes['org.keycloak.keys.KeyProvider']; + + Components.query({realm: realm.realm, + parent: realm.id, + type: 'org.keycloak.keys.KeyProvider' + }, function(data) { + $scope.instances = data; + + for (var i = 0; i < $scope.instances.length; i++) { + for (var j = 0; j < $scope.providers.length; j++) { + if ($scope.providers[j].id === $scope.instances[i].providerId) { + $scope.instances[i].provider = $scope.providers[j]; + } + } + } + }); + + $scope.addProvider = function(provider) { + $location.url("/create/keys/" + realm.realm + "/providers/" + provider.id); + }; + + $scope.removeInstance = function(instance) { + Dialog.confirmDelete(instance.name, 'key provider', function() { + Components.remove({ + realm : realm.realm, + componentId : instance.id + }, function() { + $route.reload(); + Notifications.success("The provider has been deleted."); + }); + }); + }; +}); + +module.controller('GenericKeystoreCtrl', function($scope, $location, Notifications, $route, Dialog, realm, serverInfo, instance, providerId, Components) { + $scope.create = !instance.providerId; + $scope.realm = realm; + + var providers = serverInfo.componentTypes['org.keycloak.keys.KeyProvider']; + var providerFactory = null; + for (var i = 0; i < providers.length; i++) { + var p = providers[i]; + if (p.id == providerId) { + $scope.providerFactory = p; + providerFactory = p; + break; + } + } + + if ($scope.create) { + $scope.instance = { + name: providerFactory.id, + providerId: providerFactory.id, + providerType: 'org.keycloak.keys.KeyProvider', + parentId: realm.id, + config: { + 'priority': ["0"] + } + } + } else { + $scope.instance = angular.copy(instance); + } + + if (providerFactory.properties) { + for (var i = 0; i < providerFactory.properties.length; i++) { + var configProperty = providerFactory.properties[i]; + if (!$scope.instance.config[configProperty.name]) { + if (configProperty.defaultValue) { + $scope.instance.config[configProperty.name] = [configProperty.defaultValue]; + if (!$scope.create) { + instance.config[configProperty.name] = [configProperty.defaultValue]; + } + } else { + $scope.instance.config[configProperty.name] = ['']; + if (!$scope.create) { + instance.config[configProperty.name] = [configProperty.defaultValue]; + } + } + } + } + } + + $scope.$watch('instance', function() { + if (!angular.equals($scope.instance, instance)) { + $scope.changed = true; + } + + }, true); + + $scope.save = function() { + $scope.changed = false; + if ($scope.create) { + Components.save({realm: realm.realm}, $scope.instance, function (data, headers) { + var l = headers().location; + var id = l.substring(l.lastIndexOf("/") + 1); + + $location.url("/realms/" + realm.realm + "/keys/providers/" + $scope.instance.providerId + "/" + id); + Notifications.success("The provider has been created."); + }); + } else { + Components.update({realm: realm.realm, + componentId: instance.id + }, + $scope.instance, function () { + $route.reload(); + Notifications.success("The provider has been updated."); + }); + } + }; + + $scope.reset = function() { + $route.reload(); + }; + + $scope.cancel = function() { + if ($scope.create) { + $location.url("/realms/" + realm.realm + "/keys"); + } else { + $route.reload(); + } + }; +}); + +module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmClientSessionStats, RealmLogoutAll, Notifications) { + $scope.realm = realm; + $scope.stats = stats; + + $scope.logoutAll = function() { + RealmLogoutAll.save({realm : realm.realm}, function (globalReqResult) { + var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0; + var failedCount = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0; + + if (failedCount > 0) { + var msgStart = successCount>0 ? 'Successfully logout all users under: ' + globalReqResult.successRequests + ' . ' : ''; + Notifications.error(msgStart + 'Failed to logout users under: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again'); + } else { + window.location.reload(); + } + }); + }; +}); + + +module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevocation, realm, $http, $location, Dialog, Notifications) { + $scope.realm = angular.copy(realm); + + var setNotBefore = function() { + if ($scope.realm.notBefore == 0) { + $scope.notBefore = "None"; + } else { + $scope.notBefore = new Date($scope.realm.notBefore * 1000); + } + }; + + setNotBefore(); + + var reset = function() { + Realm.get({ id : realm.realm }, function(updated) { + $scope.realm = updated; + setNotBefore(); + }) + + }; + + $scope.clear = function() { + Realm.update({ realm: realm.realm, notBefore : 0 }, function () { + $scope.notBefore = "None"; + Notifications.success('Not Before cleared for realm.'); + reset(); + }); + } + $scope.setNotBeforeNow = function() { + Realm.update({ realm: realm.realm, notBefore : new Date().getTime()/1000}, function () { + Notifications.success('Not Before set for realm.'); + reset(); + }); + } + $scope.pushRevocation = function() { + RealmPushRevocation.save({ realm: realm.realm}, function (globalReqResult) { + var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0; + var failedCount = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0; + + if (successCount==0 && failedCount==0) { + Notifications.warn('No push sent. No admin URI configured or no registered cluster nodes available'); + } else if (failedCount > 0) { + var msgStart = successCount>0 ? 'Successfully push notBefore to: ' + globalReqResult.successRequests + ' . ' : ''; + Notifications.error(msgStart + 'Failed to push notBefore to: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again'); + } else { + Notifications.success('Successfully push notBefore to all configured clients'); + } + }); + } + +}); + + +module.controller('RoleTabCtrl', function(Dialog, $scope, Current, Notifications, $location) { + $scope.removeRole = function() { + Dialog.confirmDelete($scope.role.name, 'role', function() { + RoleById.remove({ + realm: realm.realm, + role: $scope.role.id + }, function () { + $route.reload(); + Notifications.success("The role has been deleted."); + }); + }); + }; +}); + + +module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, RoleList, RoleById, filterFilter) { + $scope.realm = realm; + $scope.roles = []; + + $scope.query = { + realm: realm.realm, + search : null, + max : 20, + first : 0 + } + + $scope.$watch('query.search', function (newVal, oldVal) { + if($scope.query.search && $scope.query.search.length >= 3) { + $scope.firstPage(); + } + }, true); + + $scope.firstPage = function() { + $scope.query.first = 0; + $scope.searchQuery(); + } + + $scope.previousPage = function() { + $scope.query.first -= parseInt($scope.query.max); + if ($scope.query.first < 0) { + $scope.query.first = 0; + } + $scope.searchQuery(); + } + + $scope.nextPage = function() { + $scope.query.first += parseInt($scope.query.max); + $scope.searchQuery(); + } + + $scope.searchQuery = function() { + $scope.searchLoaded = false; + + $scope.roles = RoleList.query($scope.query, function() { + $scope.searchLoaded = true; + $scope.lastSearch = $scope.query.search; + }); + }; + + $scope.searchQuery(); + + $scope.removeRole = function (role) { + Dialog.confirmDelete(role.name, 'role', function () { + RoleById.remove({ + realm: realm.realm, + role: role.id + }, function () { + $route.reload(); + Notifications.success("The role has been deleted."); + }); + }); + }; +}); + + +module.controller('RoleDetailCtrl', function($scope, realm, role, roles, Client, $route, + Role, ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, + $http, $location, Dialog, Notifications, RealmRoleRemover, ComponentUtils) { + $scope.realm = realm; + $scope.role = angular.copy(role); + $scope.create = !role.name; + + $scope.changed = $scope.create; + + $scope.save = function() { + convertAttributeValuesToLists(); + console.log('save'); + if ($scope.create) { + Role.save({ + realm: realm.realm + }, $scope.role, function (data, headers) { + $scope.changed = false; + convertAttributeValuesToString($scope.role); + role = angular.copy($scope.role); + + Role.get({ realm: realm.realm, role: role.name }, function(role) { + var id = role.id; + $location.url("/realms/" + realm.realm + "/roles/" + id); + Notifications.success("The role has been created."); + }); + }); + } else { + $scope.update(); + } + }; + + $scope.remove = function() { + RealmRoleRemover.remove($scope.role, realm, Dialog, $location, Notifications); + }; + + $scope.cancel = function () { + $location.url("/realms/" + realm.realm + "/roles"); + }; + + $scope.addAttribute = function() { + $scope.role.attributes[$scope.newAttribute.key] = $scope.newAttribute.value; + delete $scope.newAttribute; + } + + $scope.removeAttribute = function(key) { + delete $scope.role.attributes[key]; + } + + function convertAttributeValuesToLists() { + var attrs = $scope.role.attributes; + for (var attribute in attrs) { + if (typeof attrs[attribute] === "string") { + var attrVals = attrs[attribute].split("##"); + attrs[attribute] = attrVals; + } + } + } + + function convertAttributeValuesToString(role) { + var attrs = role.attributes; + for (var attribute in attrs) { + if (typeof attrs[attribute] === "object") { + var attrVals = attrs[attribute].join("##"); + attrs[attribute] = attrVals; + console.log("attribute" + attrVals) + } + } + } + + roleControl($scope, $route, realm, role, roles, Client, + ClientRole, RoleById, RoleRealmComposites, RoleClientComposites, + $http, $location, Notifications, Dialog, ComponentUtils); +}); + +module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, realm, $http, $location, Dialog, Notifications, RealmSMTPConnectionTester) { + console.log('RealmSMTPSettingsCtrl'); + + var booleanSmtpAtts = ["auth","ssl","starttls"]; + + $scope.realm = realm; + + if ($scope.realm.smtpServer) { + $scope.realm.smtpServer = typeObject($scope.realm.smtpServer); + }; + + var oldCopy = angular.copy($scope.realm); + $scope.changed = false; + + $scope.$watch('realm', function() { + if (!angular.equals($scope.realm, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + var realmCopy = angular.copy($scope.realm); + realmCopy['smtpServer'] = detypeObject(realmCopy.smtpServer); + $scope.changed = false; + Realm.update(realmCopy, function () { + $location.url("/realms/" + realm.realm + "/smtp-settings"); + Notifications.success("Your changes have been saved to the realm."); + }); + }; + + $scope.reset = function() { + $scope.realm = angular.copy(oldCopy); + $scope.changed = false; + }; + + $scope.testConnection = function() { + RealmSMTPConnectionTester.save({realm: realm.realm}, realm.smtpServer, function() { + Notifications.success("SMTP connection successful. E-mail was sent!"); + }, function(errorResponse) { + if (error.data.errorMessage) { + Notifications.error(error.data.errorMessage); + } else { + Notifications.error('Unexpected error during SMTP validation'); + } + }); + }; + + /* Convert string attributes containing a boolean to actual boolean type + convert an integer string (port) to integer. */ + function typeObject(obj){ + for (var att in obj){ + if (booleanSmtpAtts.indexOf(att) < 0) + continue; + if (obj[att] === "true"){ + obj[att] = true; + } else if (obj[att] === "false"){ + obj[att] = false; + } + } + + obj['port'] = parseInt(obj['port']); + + return obj; + } + + /* Convert all non-string values to strings to invert changes caused by the typeObject function. */ + function detypeObject(obj){ + for (var att in obj){ + if (booleanSmtpAtts.indexOf(att) < 0) + continue; + if (obj[att] === true){ + obj[att] = "true"; + } else if (obj[att] === false){ + obj[att] = "false" + } + } + + obj['port'] = obj['port'] && obj['port'].toString(); + + return obj; + } +}); + +module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmEventsConfig, RealmEvents, RealmAdminEvents, realm, serverInfo, $location, Notifications, TimeUnit, Dialog) { + $scope.realm = realm; + + $scope.eventsConfig = eventsConfig; + + $scope.eventsConfig.expirationUnit = TimeUnit.autoUnit(eventsConfig.eventsExpiration); + $scope.eventsConfig.eventsExpiration = TimeUnit.toUnit(eventsConfig.eventsExpiration, $scope.eventsConfig.expirationUnit); + + $scope.eventListeners = Object.keys(serverInfo.providers.eventsListener.providers); + + $scope.eventsConfigSelectOptions = { + 'multiple': true, + 'simple_tags': true, + 'tags': $scope.eventListeners + }; + + $scope.eventSelectOptions = { + 'multiple': true, + 'simple_tags': true, + 'tags': serverInfo.enums['eventType'] + }; + + var oldCopy = angular.copy($scope.eventsConfig); + $scope.changed = false; + + $scope.$watch('eventsConfig', function() { + if (!angular.equals($scope.eventsConfig, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + $scope.changed = false; + + var copy = angular.copy($scope.eventsConfig) + delete copy['expirationUnit']; + + copy.eventsExpiration = TimeUnit.toSeconds($scope.eventsConfig.eventsExpiration, $scope.eventsConfig.expirationUnit); + + RealmEventsConfig.update({ + id : realm.realm + }, copy, function () { + $location.url("/realms/" + realm.realm + "/events-settings"); + Notifications.success("Your changes have been saved to the realm."); + }); + }; + + $scope.reset = function() { + $scope.eventsConfig = angular.copy(oldCopy); + $scope.changed = false; + }; + + $scope.clearEvents = function() { + Dialog.confirmDelete($scope.realm.realm, 'events', function() { + RealmEvents.remove({ id : $scope.realm.realm }, function() { + Notifications.success("The events has been cleared."); + }); + }); + }; + + $scope.clearAdminEvents = function() { + Dialog.confirmDelete($scope.realm.realm, 'admin-events', function() { + RealmAdminEvents.remove({ id : $scope.realm.realm }, function() { + Notifications.success("The admin events has been cleared."); + }); + }); + }; +}); + +module.controller('RealmEventsCtrl', function($scope, RealmEvents, realm, serverInfo) { + $scope.realm = realm; + $scope.page = 0; + + $scope.eventSelectOptions = { + 'multiple': true, + 'simple_tags': true, + 'tags': serverInfo.enums['eventType'] + }; + + $scope.query = { + id : realm.realm, + max : 5, + first : 0 + } + + $scope.disablePaste = function(e) { + e.preventDefault(); + return false; + } + + $scope.update = function() { + $scope.query.first = 0; + for (var i in $scope.query) { + if ($scope.query[i] === '') { + delete $scope.query[i]; + } + } + $scope.events = RealmEvents.query($scope.query); + } + + $scope.reset = function() { + $scope.query.first = 0; + $scope.query.max = 5; + $scope.query.type = ''; + $scope.query.client = ''; + $scope.query.user = ''; + $scope.query.dateFrom = ''; + $scope.query.dateTo = ''; + + $scope.update(); + } + + $scope.queryUpdate = function() { + for (var i in $scope.query) { + if ($scope.query[i] === '') { + delete $scope.query[i]; + } + } + $scope.events = RealmEvents.query($scope.query); + } + + $scope.firstPage = function() { + $scope.query.first = 0; + $scope.queryUpdate(); + } + + $scope.previousPage = function() { + $scope.query.first -= parseInt($scope.query.max); + if ($scope.query.first < 0) { + $scope.query.first = 0; + } + $scope.queryUpdate(); + } + + $scope.nextPage = function() { + $scope.query.first += parseInt($scope.query.max); + $scope.queryUpdate(); + } + + $scope.update(); +}); + +module.controller('RealmAdminEventsCtrl', function($scope, RealmAdminEvents, realm, serverInfo, $modal, $filter) { + $scope.realm = realm; + $scope.page = 0; + + $scope.query = { + id : realm.realm, + max : 5, + first : 0 + }; + + $scope.adminEnabledEventOperationsOptions = { + 'multiple': true, + 'simple_tags': true, + 'tags': serverInfo.enums['operationType'] + }; + + $scope.adminEnabledEventResourceTypesOptions = { + 'multiple': true, + 'simple_tags': true, + 'tags': serverInfo.enums['resourceType'] + }; + + $scope.disablePaste = function(e) { + e.preventDefault(); + return false; + } + + $scope.update = function() { + $scope.query.first = 0; + for (var i in $scope.query) { + if ($scope.query[i] === '') { + delete $scope.query[i]; + } + } + $scope.events = RealmAdminEvents.query($scope.query); + }; + + $scope.reset = function() { + $scope.query.first = 0; + $scope.query.max = 5; + $scope.query.operationTypes = ''; + $scope.query.resourceTypes = ''; + $scope.query.resourcePath = ''; + $scope.query.authRealm = ''; + $scope.query.authClient = ''; + $scope.query.authUser = ''; + $scope.query.authIpAddress = ''; + $scope.query.dateFrom = ''; + $scope.query.dateTo = ''; + + $scope.update(); + }; + + $scope.queryUpdate = function() { + for (var i in $scope.query) { + if ($scope.query[i] === '') { + delete $scope.query[i]; + } + } + $scope.events = RealmAdminEvents.query($scope.query); + } + + $scope.firstPage = function() { + $scope.query.first = 0; + $scope.queryUpdate(); + } + + $scope.previousPage = function() { + $scope.query.first -= parseInt($scope.query.max); + if ($scope.query.first < 0) { + $scope.query.first = 0; + } + $scope.queryUpdate(); + } + + $scope.nextPage = function() { + $scope.query.first += parseInt($scope.query.max); + $scope.queryUpdate(); + } + + $scope.update(); + + $scope.viewRepresentation = function(event) { + $modal.open({ + templateUrl: resourceUrl + '/partials/modal/realm-events-admin-representation.html', + controller: 'RealmAdminEventsModalCtrl', + resolve: { + event: function () { + return event; + } + } + }) + } + + $scope.viewAuth = function(event) { + $modal.open({ + templateUrl: resourceUrl + '/partials/modal/realm-events-admin-auth.html', + controller: 'RealmAdminEventsModalCtrl', + resolve: { + event: function () { + return event; + } + } + }) + } +}); + +module.controller('RealmAdminEventsModalCtrl', function($scope, $filter, event) { + $scope.event = event; +}); + +module.controller('RealmBruteForceCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications, TimeUnit, $route) { + console.log('RealmBruteForceCtrl'); + + $scope.realm = realm; + + $scope.realm.waitIncrementUnit = TimeUnit.autoUnit(realm.waitIncrementSeconds); + $scope.realm.waitIncrement = TimeUnit.toUnit(realm.waitIncrementSeconds, $scope.realm.waitIncrementUnit); + + $scope.realm.minimumQuickLoginWaitUnit = TimeUnit.autoUnit(realm.minimumQuickLoginWaitSeconds); + $scope.realm.minimumQuickLoginWait = TimeUnit.toUnit(realm.minimumQuickLoginWaitSeconds, $scope.realm.minimumQuickLoginWaitUnit); + + $scope.realm.maxFailureWaitUnit = TimeUnit.autoUnit(realm.maxFailureWaitSeconds); + $scope.realm.maxFailureWait = TimeUnit.toUnit(realm.maxFailureWaitSeconds, $scope.realm.maxFailureWaitUnit); + + $scope.realm.maxDeltaTimeUnit = TimeUnit.autoUnit(realm.maxDeltaTimeSeconds); + $scope.realm.maxDeltaTime = TimeUnit.toUnit(realm.maxDeltaTimeSeconds, $scope.realm.maxDeltaTimeUnit); + + var oldCopy = angular.copy($scope.realm); + $scope.changed = false; + + $scope.$watch('realm', function() { + if (!angular.equals($scope.realm, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + var realmCopy = angular.copy($scope.realm); + delete realmCopy["waitIncrementUnit"]; + delete realmCopy["waitIncrement"]; + delete realmCopy["minimumQuickLoginWaitUnit"]; + delete realmCopy["minimumQuickLoginWait"]; + delete realmCopy["maxFailureWaitUnit"]; + delete realmCopy["maxFailureWait"]; + delete realmCopy["maxDeltaTimeUnit"]; + delete realmCopy["maxDeltaTime"]; + + realmCopy.waitIncrementSeconds = TimeUnit.toSeconds($scope.realm.waitIncrement, $scope.realm.waitIncrementUnit) + realmCopy.minimumQuickLoginWaitSeconds = TimeUnit.toSeconds($scope.realm.minimumQuickLoginWait, $scope.realm.minimumQuickLoginWaitUnit) + realmCopy.maxFailureWaitSeconds = TimeUnit.toSeconds($scope.realm.maxFailureWait, $scope.realm.maxFailureWaitUnit) + realmCopy.maxDeltaTimeSeconds = TimeUnit.toSeconds($scope.realm.maxDeltaTime, $scope.realm.maxDeltaTimeUnit) + + $scope.changed = false; + Realm.update(realmCopy, function () { + oldCopy = angular.copy($scope.realm); + $location.url("/realms/" + realm.realm + "/defense/brute-force"); + Notifications.success("Your changes have been saved to the realm."); + }); + }; + + $scope.reset = function() { + $route.reload(); + }; +}); + + +module.controller('IdentityProviderMapperListCtrl', function($scope, realm, identityProvider, mapperTypes, mappers) { + $scope.realm = realm; + $scope.identityProvider = identityProvider; + $scope.mapperTypes = mapperTypes; + $scope.mappers = mappers; +}); + +module.controller('IdentityProviderMapperCtrl', function($scope, realm, identityProvider, mapperTypes, mapper, IdentityProviderMapper, Notifications, Dialog, $location) { + $scope.realm = realm; + $scope.identityProvider = identityProvider; + $scope.create = false; + $scope.mapper = angular.copy(mapper); + $scope.changed = false; + $scope.mapperType = mapperTypes[mapper.identityProviderMapper]; + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); + + $scope.$watch('mapper', function() { + if (!angular.equals($scope.mapper, mapper)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + IdentityProviderMapper.update({ + realm : realm.realm, + alias: identityProvider.alias, + mapperId : mapper.id + }, $scope.mapper, function() { + $scope.changed = false; + mapper = angular.copy($scope.mapper); + $location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers/" + mapper.id); + Notifications.success("Your changes have been saved."); + }); + }; + + $scope.reset = function() { + $scope.mapper = angular.copy(mapper); + $scope.changed = false; + }; + + $scope.cancel = function() { + //$location.url("/realms"); + window.history.back(); + }; + + $scope.remove = function() { + Dialog.confirmDelete($scope.mapper.name, 'mapper', function() { + IdentityProviderMapper.remove({ realm: realm.realm, alias: mapper.identityProviderAlias, mapperId : $scope.mapper.id }, function() { + Notifications.success("The mapper has been deleted."); + $location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers"); + }); + }); + }; + +}); + +module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, identityProvider, mapperTypes, IdentityProviderMapper, Notifications, Dialog, $location) { + $scope.realm = realm; + $scope.identityProvider = identityProvider; + $scope.create = true; + $scope.mapper = { identityProviderAlias: identityProvider.alias, config: {}}; + $scope.mapperTypes = mapperTypes; + + // make first type the default + $scope.mapperType = mapperTypes[Object.keys(mapperTypes)[0]]; + $scope.mapper.config.syncMode = 'INHERIT'; + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); + + $scope.save = function() { + $scope.mapper.identityProviderMapper = $scope.mapperType.id; + IdentityProviderMapper.save({ + realm : realm.realm, alias: identityProvider.alias + }, $scope.mapper, function(data, headers) { + var l = headers().location; + var id = l.substring(l.lastIndexOf("/") + 1); + $location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers/" + id); + Notifications.success("Mapper has been created."); + }); + }; + + $scope.cancel = function() { + //$location.url("/realms"); + window.history.back(); + }; + + +}); + +module.controller('RealmFlowBindingCtrl', function($scope, flows, Current, Realm, realm, serverInfo, $http, $route, Dialog, Notifications) { + $scope.flows = []; + $scope.clientFlows = []; + for (var i=0 ; i 0) { + $scope.provider = formProviders[0]; + } + + $scope.save = function() { + $scope.flow.provider = $scope.provider.id; + CreateExecutionFlow.save({realm: realm.realm, alias: parentFlow.alias}, $scope.flow, function() { + $location.url("/realms/" + realm.realm + "/authentication/flows"); + Notifications.success("Flow Created."); + }) + } + $scope.cancel = function() { + $location.url("/realms/" + realm.realm + "/authentication/flows"); + }; +}); + +module.controller('CreateExecutionCtrl', function($scope, realm, parentFlow, formActionProviders, authenticatorProviders, clientAuthenticatorProviders, + CreateExecution, + Notifications, $location) { + $scope.realm = realm; + $scope.parentFlow = parentFlow; + + if (parentFlow.providerId == 'form-flow') { + $scope.providers = formActionProviders; + } else if (parentFlow.providerId == 'client-flow') { + $scope.providers = clientAuthenticatorProviders; + } else { + $scope.providers = authenticatorProviders; + } + + $scope.provider = {}; + if ($scope.providers.length > 0) { + $scope.provider = $scope.providers[0]; + } + + $scope.save = function() { + var execution = { + provider: $scope.provider.id + } + CreateExecution.save({realm: realm.realm, alias: parentFlow.alias}, execution, function() { + $location.url("/realms/" + realm.realm + "/authentication/flows"); + Notifications.success("Execution Created."); + }) + } + $scope.cancel = function() { + $location.url("/realms/" + realm.realm + "/authentication/flows"); + }; +}); + + + +module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow, LastFlowSelected, Dialog, + AuthenticationFlows, AuthenticationFlowsCopy, AuthenticationFlowsUpdate, AuthenticationFlowExecutions, + AuthenticationExecution, AuthenticationExecutionRaisePriority, AuthenticationExecutionLowerPriority, + $modal, Notifications, CopyDialog, UpdateDialog, $location) { + $scope.realm = realm; + $scope.flows = flows; + + if (selectedFlow !== null) { + LastFlowSelected.alias = selectedFlow; + } + + if (selectedFlow === null && LastFlowSelected.alias !== null) { + selectedFlow = LastFlowSelected.alias; + } + + if (flows.length > 0) { + $scope.flow = flows[0]; + if (selectedFlow) { + for (var i = 0; i < flows.length; i++) { + if (flows[i].alias == selectedFlow) { + $scope.flow = flows[i]; + break; + } + } + } + } + + $scope.selectFlow = function(flow) { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + flow.alias); + }; + + var setupForm = function() { + AuthenticationFlowExecutions.query({realm: realm.realm, alias: $scope.flow.alias}, function(data) { + $scope.executions = data; + $scope.choicesmax = 0; + $scope.levelmax = 0; + for (var i = 0; i < $scope.executions.length; i++ ) { + var execution = $scope.executions[i]; + if (execution.requirementChoices.length > $scope.choicesmax) { + $scope.choicesmax = execution.requirementChoices.length; + } + if (execution.level > $scope.levelmax) { + $scope.levelmax = execution.level; + } + } + $scope.levelmaxempties = []; + for (j = 0; j < $scope.levelmax; j++) { + $scope.levelmaxempties.push(j); + + } + for (var i = 0; i < $scope.executions.length; i++ ) { + var execution = $scope.executions[i]; + execution.empties = []; + for (j = 0; j < $scope.choicesmax - execution.requirementChoices.length; j++) { + execution.empties.push(j); + } + execution.preLevels = []; + for (j = 0; j < execution.level; j++) { + execution.preLevels.push(j); + } + execution.postLevels = []; + for (j = execution.level; j < $scope.levelmax; j++) { + execution.postLevels.push(j); + } + } + }) + }; + + $scope.copyFlow = function() { + CopyDialog.open('Copy Authentication Flow', $scope.flow.alias, function(name) { + AuthenticationFlowsCopy.save({realm: realm.realm, alias: $scope.flow.alias}, { + newName: name + }, function() { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + name); + Notifications.success("Flow copied."); + }) + }) + }; + + $scope.deleteFlow = function() { + Dialog.confirmDelete($scope.flow.alias, 'flow', function() { + $scope.removeFlow(); + }); + }; + + $scope.removeFlow = function() { + console.log('Remove flow:' + $scope.flow.alias); + if (realm.browserFlow == $scope.flow.alias) { + Notifications.error("Cannot remove flow, it is currently being used as the browser flow."); + + } else if (realm.registrationFlow == $scope.flow.alias) { + Notifications.error("Cannot remove flow, it is currently being used as the registration flow."); + + } else if (realm.directGrantFlow == $scope.flow.alias) { + Notifications.error("Cannot remove flow, it is currently being used as the direct grant flow."); + + } else if (realm.resetCredentialsFlow == $scope.flow.alias) { + Notifications.error("Cannot remove flow, it is currently being used as the reset credentials flow."); + + } else if (realm.clientAuthenticationFlow == $scope.flow.alias) { + Notifications.error("Cannot remove flow, it is currently being used as the client authentication flow."); + + } else if (realm.dockerAuthenticationFlow == $scope.flow.alias) { + Notifications.error("Cannot remove flow, it is currently being used as the docker authentication flow."); + + } else { + AuthenticationFlows.remove({realm: realm.realm, flow: $scope.flow.id}, function () { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + flows[0].alias); + Notifications.success("Flow removed"); + }) + } + + }; + + $scope.editFlow = function(flow) { + var copy = angular.copy(flow); + UpdateDialog.open('Update Authentication Flow', copy.alias, copy.description, function(name, desc) { + copy.alias = name; + copy.description = desc; + AuthenticationFlowsUpdate.update({realm: realm.realm, flow: flow.id}, copy, function() { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + name); + Notifications.success("Flow updated"); + }); + }) + }; + + $scope.addFlow = function() { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/flow/execution/' + $scope.flow.id); + + } + + $scope.addSubFlow = function(execution) { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + execution.flowId + '/create/flow/execution/' + $scope.flow.alias); + + } + + $scope.addSubFlowExecution = function(execution) { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + execution.flowId + '/create/execution/' + $scope.flow.alias); + + } + + $scope.addExecution = function() { + $location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/execution/' + $scope.flow.id); + + } + + $scope.createFlow = function() { + $location.url("/realms/" + realm.realm + '/authentication/create/flow'); + } + + $scope.updateExecution = function(execution) { + var copy = angular.copy(execution); + delete copy.empties; + delete copy.levels; + delete copy.preLevels; + delete copy.postLevels; + AuthenticationFlowExecutions.update({realm: realm.realm, alias: $scope.flow.alias}, copy, function() { + Notifications.success("Auth requirement updated"); + setupForm(); + }); + + }; + + $scope.editExecutionFlow = function(execution) { + var copy = angular.copy(execution); + delete copy.empties; + delete copy.levels; + delete copy.preLevels; + delete copy.postLevels; + UpdateDialog.open('Update Execution Flow', copy.displayName, copy.description, function(name, desc) { + copy.displayName = name; + copy.description = desc; + AuthenticationFlowExecutions.update({realm: realm.realm, alias: $scope.flow.alias}, copy, function() { + Notifications.success("Execution Flow updated"); + setupForm(); + }); + }) + }; + + $scope.removeExecution = function(execution) { + console.log('removeExecution: ' + execution.id); + var exeOrFlow = execution.authenticationFlow ? 'flow' : 'execution'; + Dialog.confirmDelete(execution.displayName, exeOrFlow, function() { + AuthenticationExecution.remove({realm: realm.realm, execution: execution.id}, function() { + Notifications.success("The " + exeOrFlow + " was removed."); + setupForm(); + }); + }); + + } + + $scope.raisePriority = function(execution) { + AuthenticationExecutionRaisePriority.save({realm: realm.realm, execution: execution.id}, function() { + Notifications.success("Priority raised"); + setupForm(); + }) + } + + $scope.lowerPriority = function(execution) { + AuthenticationExecutionLowerPriority.save({realm: realm.realm, execution: execution.id}, function() { + Notifications.success("Priority lowered"); + setupForm(); + }) + } + + $scope.setupForm = setupForm; + + if (selectedFlow == null) { + $scope.selectFlow(flows[0]); + } else { + setupForm(); + } +}); + +module.controller('RequiredActionsCtrl', function($scope, realm, unregisteredRequiredActions, + $modal, $route, + RegisterRequiredAction, RequiredActions, RequiredActionRaisePriority, RequiredActionLowerPriority, Notifications) { + console.log('RequiredActionsCtrl'); + $scope.realm = realm; + $scope.unregisteredRequiredActions = unregisteredRequiredActions; + $scope.requiredActions = []; + var setupRequiredActionsForm = function() { + console.log('setupRequiredActionsForm'); + RequiredActions.query({realm: realm.realm}, function(data) { + $scope.requiredActions = []; + for (var i = 0; i < data.length; i++) { + $scope.requiredActions.push(data[i]); + } + }); + }; + + $scope.updateRequiredAction = function(action) { + RequiredActions.update({realm: realm.realm, alias: action.alias}, action, function() { + Notifications.success("Required action updated"); + setupRequiredActionsForm(); + }); + } + + $scope.raisePriority = function(action) { + RequiredActionRaisePriority.save({realm: realm.realm, alias: action.alias}, function() { + Notifications.success("Required action's priority raised"); + setupRequiredActionsForm(); + }) + } + + $scope.lowerPriority = function(action) { + RequiredActionLowerPriority.save({realm: realm.realm, alias: action.alias}, function() { + Notifications.success("Required action's priority lowered"); + setupRequiredActionsForm(); + }) + } + + $scope.register = function() { + var controller = function($scope, $modalInstance) { + $scope.unregisteredRequiredActions = unregisteredRequiredActions; + $scope.selected = { + selected: $scope.unregisteredRequiredActions[0] + } + $scope.ok = function () { + $modalInstance.close(); + RegisterRequiredAction.save({realm: realm.realm}, $scope.selected.selected, function() { + $route.reload(); + }); + }; + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + } + $modal.open({ + templateUrl: resourceUrl + '/partials/modal/unregistered-required-action-selector.html', + controller: controller, + resolve: { + } + }); + } + + setupRequiredActionsForm(); + + +}); + +module.controller('AuthenticationConfigCtrl', function($scope, realm, flow, configType, config, AuthenticationConfig, Notifications, + Dialog, $location, ComponentUtils) { + $scope.realm = realm; + $scope.flow = flow; + $scope.configType = configType; + $scope.create = false; + $scope.config = angular.copy(config); + $scope.changed = false; + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); + + $scope.$watch('config', function() { + if (!angular.equals($scope.config, config)) { + $scope.changed = true; + } + }, true); + + $scope.save = function() { + var configCopy = angular.copy($scope.config); + ComponentUtils.convertAllListValuesToMultivaluedString(configType.properties, configCopy.config); + + AuthenticationConfig.update({ + realm : realm.realm, + config : config.id + }, configCopy, function() { + $scope.changed = false; + config = angular.copy($scope.config); + $location.url("/realms/" + realm.realm + '/authentication/flows/' + flow.id + '/config/' + configType.providerId + "/" + config.id); + Notifications.success("Your changes have been saved."); + }); + }; + + $scope.reset = function() { + $scope.config = angular.copy(config); + $scope.changed = false; + }; + + $scope.cancel = function() { + //$location.url("/realms"); + window.history.back(); + }; + + $scope.remove = function() { + Dialog.confirmDelete($scope.config.alias, 'config', function() { + AuthenticationConfig.remove({ realm: realm.realm, config : $scope.config.id }, function() { + Notifications.success("The config has been deleted."); + $location.url("/realms/" + realm.realm + '/authentication/flows/' + flow.id); + }); + }); + }; + +}); + +module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, flow, configType, execution, AuthenticationExecutionConfig, + Notifications, Dialog, $location, ComponentUtils) { + $scope.realm = realm; + $scope.flow = flow; + $scope.create = true; + $scope.configType = configType; + + var defaultConfig = {}; + if (configType && Array.isArray(configType.properties)) { + for(var i = 0; i < configType.properties.length; i++) { + var property = configType.properties[i]; + if (property && property.name) { + defaultConfig[property.name] = property.defaultValue; + } + } + } + + $scope.config = { config: defaultConfig}; + + $scope.$watch(function() { + return $location.path(); + }, function() { + $scope.path = $location.path().substring(1).split("/"); + }); + + $scope.save = function() { + var configCopy = angular.copy($scope.config); + ComponentUtils.convertAllListValuesToMultivaluedString(configType.properties, configCopy.config); + + AuthenticationExecutionConfig.save({ + realm : realm.realm, + execution: execution + }, configCopy, function(data, headers) { + var l = headers().location; + var id = l.substring(l.lastIndexOf("/") + 1); + var url = "/realms/" + realm.realm + '/authentication/flows/' + flow.id + '/config/' + configType.providerId + "/" + id; + console.log('redirect url: ' + url); + $location.url(url); + Notifications.success("Config has been created."); + }); + }; + + $scope.cancel = function() { + //$location.url("/realms"); + window.history.back(); + }; +}); + +module.controller('ClientInitialAccessCtrl', function($scope, realm, clientInitialAccess, ClientInitialAccess, Dialog, Notifications, $route, $location) { + $scope.realm = realm; + $scope.clientInitialAccess = clientInitialAccess; + + $scope.remove = function(id) { + Dialog.confirmDelete(id, 'initial access token', function() { + ClientInitialAccess.remove({ realm: realm.realm, id: id }, function() { + Notifications.success("The initial access token was deleted."); + $route.reload(); + }); + }); + } +}); + +module.controller('ClientInitialAccessCreateCtrl', function($scope, realm, ClientInitialAccess, TimeUnit, Dialog, $location, $translate) { + $scope.expirationUnit = 'Days'; + $scope.expiration = TimeUnit.toUnit(0, $scope.expirationUnit); + $scope.count = 1; + $scope.realm = realm; + + $scope.save = function() { + var expiration = TimeUnit.toSeconds($scope.expiration, $scope.expirationUnit); + ClientInitialAccess.save({ + realm: realm.realm + }, { expiration: expiration, count: $scope.count}, function (data) { + console.debug(data); + $scope.id = data.id; + $scope.token = data.token; + }); + }; + + $scope.cancel = function() { + $location.url('/realms/' + realm.realm + '/client-registration/client-initial-access'); + }; + + $scope.done = function() { + var btns = { + ok: { + label: $translate.instant('continue'), + cssClass: 'btn btn-primary' + }, + cancel: { + label: $translate.instant('cancel'), + cssClass: 'btn btn-default' + } + } + + var title = $translate.instant('initial-access-token.confirm.title'); + var message = $translate.instant('initial-access-token.confirm.text'); + Dialog.open(title, message, btns, function() { + $location.url('/realms/' + realm.realm + '/client-registration/client-initial-access'); + }); + }; +}); + +module.controller('ClientRegPoliciesCtrl', function($scope, realm, clientRegistrationPolicyProviders, policies, Dialog, Notifications, Components, $route, $location) { + $scope.realm = realm; + $scope.providers = clientRegistrationPolicyProviders; + $scope.anonPolicies = []; + $scope.authPolicies = []; + for (var i=0 ; i { + $scope.headerTitle = translatedValue; + }).catch(() => { + $scope.headerTitle = $scope.instance.providerId; + }); + + if ($scope.create) { + $scope.instance.name = ""; + $scope.instance.parentId = realm.id; + $scope.instance.config = {}; + + if ($scope.providerType.properties) { + + for (let i = 0; i < $scope.providerType.properties.length; i++) { + let configProperty = $scope.providerType.properties[i]; + $scope.instance.config[configProperty.name] = toDefaultValue(configProperty); + } + } + } + + if ($scope.providerType.properties) { + ComponentUtils.addLastEmptyValueToMultivaluedLists($scope.providerType.properties, $scope.instance.config); + ComponentUtils.addMvOptionsToMultivaluedLists($scope.providerType.properties); + } + + let oldCopy = angular.copy($scope.instance); + $scope.changed = false; + + $scope.$watch('instance', function() { + if (!angular.equals($scope.instance, oldCopy)) { + $scope.changed = true; + } + }, true); + + $scope.reset = function() { + $scope.create ? window.history.back() : $route.reload(); + }; + + $scope.hasValidValues = () => $scope.changed && $scope.instance.name; + + $scope.save = function() { + $scope.changed = false; + if ($scope.create) { + Components.save({realm: realm.realm}, $scope.instance, function (data, headers) { + var l = headers().location; + var id = l.substring(l.lastIndexOf("/") + 1); + $location.url("/realms/" + realm.realm + "/client-registration/client-reg-policies/" + $scope.instance.providerId + "/" + id); + Notifications.success("The policy has been created."); + }); + } else { + Components.update({realm: realm.realm, + componentId: instance.id + }, + $scope.instance, function () { + $route.reload(); + Notifications.success("The policy has been updated."); + }); + } + }; + +}); + +module.controller('RealmImportCtrl', function($scope, realm, $route, + Notifications, $modal, $resource) { + $scope.rawContent = {}; + $scope.fileContent = { + enabled: true + }; + $scope.changed = false; + $scope.files = []; + $scope.realm = realm; + $scope.overwrite = false; + $scope.skip = false; + $scope.importUsers = false; + $scope.importGroups = false; + $scope.importClients = false; + $scope.importIdentityProviders = false; + $scope.importRealmRoles = false; + $scope.importClientRoles = false; + $scope.ifResourceExists='FAIL'; + $scope.isMultiRealm = false; + $scope.results = {}; + $scope.currentPage = 0; + var pageSize = 15; + + var oldCopy = angular.copy($scope.fileContent); + + $scope.importFile = function($fileContent){ + var parsed; + try { + parsed = JSON.parse($fileContent); + } catch (e) { + Notifications.error('Unable to parse JSON file.'); + return; + } + + $scope.rawContent = angular.copy(parsed); + if (($scope.rawContent instanceof Array) && ($scope.rawContent.length > 0)) { + if ($scope.rawContent.length > 1) $scope.isMultiRealm = true; + $scope.fileContent = $scope.rawContent[0]; + } else { + $scope.fileContent = $scope.rawContent; + } + + $scope.importing = true; + setOnOffSwitchDefaults(); + $scope.results = {}; + if (!$scope.hasResources()) { + $scope.nothingToImport(); + } + }; + + $scope.hasResults = function() { + return (Object.keys($scope.results).length > 0) && + ($scope.results.results !== undefined) && + ($scope.results.results.length > 0); + } + + $scope.resultsPage = function() { + if (!$scope.hasResults()) return {}; + return $scope.results.results.slice(startIndex(), endIndex()); + } + + function startIndex() { + return pageSize * $scope.currentPage; + } + + function endIndex() { + var length = $scope.results.results.length; + var endIndex = startIndex() + pageSize; + if (endIndex > length) endIndex = length; + return endIndex; + } + + function setOnOffSwitchDefaults() { + $scope.importUsers = $scope.hasArray('users'); + $scope.importGroups = $scope.hasArray('groups'); + $scope.importClients = $scope.hasArray('clients'); + $scope.importIdentityProviders = $scope.hasArray('identityProviders'); + $scope.importRealmRoles = $scope.hasRealmRoles(); + $scope.importClientRoles = $scope.hasClientRoles(); + } + + $scope.setFirstPage = function() { + $scope.currentPage = 0; + } + + $scope.setNextPage = function() { + $scope.currentPage++; + } + + $scope.setPreviousPage = function() { + $scope.currentPage--; + } + + $scope.hasNext = function() { + if (!$scope.hasResults()) return false; + var length = $scope.results.results.length; + //console.log('length=' + length); + var endIndex = startIndex() + pageSize; + //console.log('endIndex=' + endIndex); + return length > endIndex; + } + + $scope.hasPrevious = function() { + if (!$scope.hasResults()) return false; + return $scope.currentPage > 0; + } + + $scope.viewImportDetails = function() { + $modal.open({ + templateUrl: resourceUrl + '/partials/modal/view-object.html', + controller: 'ObjectModalCtrl', + resolve: { + object: function () { + return $scope.fileContent; + } + } + }) + }; + + $scope.hasArray = function(section) { + return ($scope.fileContent !== 'undefined') && + ($scope.fileContent.hasOwnProperty(section)) && + ($scope.fileContent[section] instanceof Array) && + ($scope.fileContent[section].length > 0); + } + + $scope.hasRealmRoles = function() { + return $scope.hasRoles() && + ($scope.fileContent.roles.hasOwnProperty('realm')) && + ($scope.fileContent.roles.realm instanceof Array) && + ($scope.fileContent.roles.realm.length > 0); + } + + $scope.hasRoles = function() { + return ($scope.fileContent !== 'undefined') && + ($scope.fileContent.hasOwnProperty('roles')) && + ($scope.fileContent.roles !== 'undefined'); + } + + $scope.hasClientRoles = function() { + return $scope.hasRoles() && + ($scope.fileContent.roles.hasOwnProperty('client')) && + (Object.keys($scope.fileContent.roles.client).length > 0); + } + + $scope.itemCount = function(section) { + if (!$scope.importing) return 0; + if ($scope.hasRealmRoles() && (section === 'roles.realm')) return $scope.fileContent.roles.realm.length; + if ($scope.hasClientRoles() && (section === 'roles.client')) return clientRolesCount($scope.fileContent.roles.client); + + if (!$scope.fileContent.hasOwnProperty(section)) return 0; + + return $scope.fileContent[section].length; + } + + clientRolesCount = function(clientRoles) { + var total = 0; + for (var clientName in clientRoles) { + total += clientRoles[clientName].length; + } + return total; + } + + $scope.hasResources = function() { + return ($scope.importUsers && $scope.hasArray('users')) || + ($scope.importGroups && $scope.hasArray('groups')) || + ($scope.importClients && $scope.hasArray('clients')) || + ($scope.importIdentityProviders && $scope.hasArray('identityProviders')) || + ($scope.importRealmRoles && $scope.hasRealmRoles()) || + ($scope.importClientRoles && $scope.hasClientRoles()); + } + + $scope.nothingToImport = function() { + Notifications.error('No resources specified to import.'); + } + + $scope.$watch('fileContent', function() { + if (!angular.equals($scope.fileContent, oldCopy)) { + $scope.changed = true; + } + setOnOffSwitchDefaults(); + }, true); + + $scope.successMessage = function() { + var message = $scope.results.added + ' records added. '; + if ($scope.ifResourceExists === 'SKIP') { + message += $scope.results.skipped + ' records skipped.' + } + if ($scope.ifResourceExists === 'OVERWRITE') { + message += $scope.results.overwritten + ' records overwritten.'; + } + return message; + } + + $scope.save = function() { + var json = angular.copy($scope.fileContent); + json.ifResourceExists = $scope.ifResourceExists; + if (!$scope.importUsers) delete json.users; + if (!$scope.importGroups) delete json.groups; + if (!$scope.importIdentityProviders) delete json.identityProviders; + if (!$scope.importClients) delete json.clients; + + if (json.hasOwnProperty('roles')) { + if (!$scope.importRealmRoles) delete json.roles.realm; + if (!$scope.importClientRoles) delete json.roles.client; + } + + var importFile = $resource(authUrl + '/admin/realms/' + realm.realm + '/partialImport'); + $scope.results = importFile.save(json, function() { + Notifications.success($scope.successMessage()); + }, function(error) { + if (error.data.errorMessage) { + Notifications.error(error.data.errorMessage); + } else { + Notifications.error('Unexpected error during import'); + } + }); + }; + + $scope.reset = function() { + $route.reload(); + } + +}); + +module.controller('RealmExportCtrl', function($scope, realm, $http, + $httpParamSerializer, Notifications, Dialog) { + $scope.realm = realm; + $scope.exportGroupsAndRoles = false; + $scope.exportClients = false; + + $scope.export = function() { + if ($scope.exportGroupsAndRoles || $scope.exportClients) { + Dialog.confirm('Export', 'This operation may make server unresponsive for a while.\n\nAre you sure you want to proceed?', download); + } else { + download(); + } + } + + function download() { + var exportUrl = authUrl + '/admin/realms/' + realm.realm + '/partial-export'; + var params = {}; + if ($scope.exportGroupsAndRoles) { + params['exportGroupsAndRoles'] = true; + } + if ($scope.exportClients) { + params['exportClients'] = true; + } + if (Object.keys(params).length > 0) { + exportUrl += '?' + $httpParamSerializer(params); + } + $http.post(exportUrl) + .then(function(response) { + var download = angular.fromJson(response.data); + download = angular.toJson(download, true); + saveAs(new Blob([download], { type: 'application/json' }), 'realm-export.json'); + }).catch(function() { + Notifications.error("Sorry, something went wrong."); + }); + } +}); diff --git a/juice/welcome/resources/juice-bc.svg b/juice/welcome/resources/juice-bc.svg new file mode 100644 index 0000000..3c65812 --- /dev/null +++ b/juice/welcome/resources/juice-bc.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/juice/welcome/resources/juice.svg b/juice/welcome/resources/juice.svg new file mode 100644 index 0000000..ee1ec0d --- /dev/null +++ b/juice/welcome/resources/juice.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/juice/welcome/resources/linkedin.svg b/juice/welcome/resources/linkedin.svg new file mode 100644 index 0000000..e74895b --- /dev/null +++ b/juice/welcome/resources/linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/welcome/resources/twitter.svg b/juice/welcome/resources/twitter.svg new file mode 100644 index 0000000..a6b0441 --- /dev/null +++ b/juice/welcome/resources/twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/juice/welcome/theme.properties b/juice/welcome/theme.properties new file mode 100644 index 0000000..805c36a --- /dev/null +++ b/juice/welcome/theme.properties @@ -0,0 +1,11 @@ +styles=css/welcome.css css/styles.css +import=common/keycloak + +stylesCommon=node_modules/patternfly/dist/css/patternfly.css node_modules/patternfly/dist/css/patternfly-additions.css +scripts=realm.js +signInUrl=/auth/realms/demo/account +displayCommunityLinks=true + +welcomeTitle=Welcome + +locales=ca,cs,da,de,en,es,fr,hu,it,ja,lt,nl,no,pl,pt-BR,ru,sk,sv,tr,zh-CN diff --git a/restart.sh b/restart.sh new file mode 100755 index 0000000..46e17f5 --- /dev/null +++ b/restart.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +docker build -t juice-keycloak . + +docker stop juice-keycloak || true + +docker run --rm -it -d \ + -p 8080:8080 \ + -e KEYCLOAK_USER=admin \ + -e KEYCLOAK_PASSWORD=admin \ + -e DB_VENDOR=postgres \ + -e DB_ADDR=host.docker.internal \ + -e DB_DATABASE=keycloak \ + -e DB_USER=keycloak \ + -e DB_PASSWORD=keycloak \ + --name juice-keycloak \ + juice-keycloak \ No newline at end of file diff --git a/test/url.test.js b/test/url.test.js new file mode 100644 index 0000000..17d1a01 --- /dev/null +++ b/test/url.test.js @@ -0,0 +1,110 @@ +// installable packages +const base64url = require("base64url"); +const { v4: uuidv4 } = require("uuid"); +const fetch = require("node-fetch").default; + +// native packages +const crypto = require("crypto"); +const { URL, URLSearchParams } = require("url"); + +// config +const BASE_URL = "http://localhost:8080"; +const REALM = "demorealm"; +const REDIRECT_URI = "com.juice.booster3://auth"; +const CLIENT_ID = "native-webview"; + +const sha256encrypt = (code) => { + const base64Digest = crypto + .createHash("sha256") + .update(code) + .digest("base64"); + + return base64url.fromBase64(base64Digest); +}; + +const nonceFactory = () => { + const timestamp = Date.now(); + return sha256encrypt(timestamp.toString()); +}; + +function generateAuthUrl() { + const nonce = uuidv4(); + const state = uuidv4(); + + const url = new URL( + `${BASE_URL}/auth/realms/${REALM}/protocol/openid-connect/auth`, + ); + url.searchParams.append("client_id", CLIENT_ID); + url.searchParams.append("redirect_uri", REDIRECT_URI); + url.searchParams.append("state", state); + url.searchParams.append("response_mode", "fragment"); + url.searchParams.append("response_type", "code"); + url.searchParams.append("scope", "openid"); + url.searchParams.append("nonce", nonce); + + console.log(url.href); +} + +async function getTokenByUrl(urlString) { + let url = new URL(urlString); + + let code = new URLSearchParams(url.hash).get("code"); + console.log(`Auth with code is: ${code}`); + + url = new URL( + `${BASE_URL}/auth/realms/${REALM}/protocol/openid-connect/token`, + ); + const payload = { + client_id: CLIENT_ID, + code, + grant_type: "authorization_code", + redirect_uri: REDIRECT_URI, + }; + + let response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "*/*", + }, + redirect: "manual", + body: new URLSearchParams(payload).toString(), + }); + + const token = await response.json(); + console.log(token); +} + +async function refresh(refresh_token) { + const url = new URL( + `${BASE_URL}/auth/realms/${REALM}/protocol/openid-connect/token`, + ); + const payload = { + client_id: CLIENT_ID, + refresh_token, + grant_type: "refresh_token", + redirect_uri: REDIRECT_URI, + }; + + let response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "*/*", + }, + redirect: "manual", + body: new URLSearchParams(payload).toString(), + }); + + const token = await response.json(); + console.log(token); +} + +if (require.main === module) { + const redirectUri = process.argv[2]; + if (redirectUri == null) { + generateAuthUrl(); + } else { + getTokenByUrl(redirectUri); + } +} diff --git a/version.ini b/version.ini new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/version.ini @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file
+
+
+
+
+
+ + <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> + <#if displayRequiredFields> +
+
+ * ${msg("requiredFields")} +
+
+

<#nested "header">

+
+
+ <#else> +

<#nested "header">

+ + <#else> + <#if displayRequiredFields> +
+ <#else> + <#nested "show-username"> +
+ + + + +
+ + +
+
+
+ + <#-- App-initiated actions should not see warning messages about the need to complete the action --> + <#-- during login. --> + + + <#nested "form"> + + <#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent> +
+ +
+ + + <#if displayInfo> +
+
+ <#nested "info"> +
+
+ +
+
+ +
+