diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.css b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.css index 596fdfca56a..acc7fb4fcf5 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.css +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.css @@ -5,6 +5,92 @@ body { color: #4d5258; font-family: "Open Sans", sans-serif; } +.feedback-aligner { + top: 0.9em; +} +/* Header */ +.header.rcue .navbar.primary .navbar-inner { + min-height: 42px; +} +.header.rcue .navbar.primary .nav > li .dropdown-label { + font-size: 0.84615384615385em; + color: #dbdada; + margin-left: 1.36363636363636em; + display: inline-block; +} +.header.rcue .navbar.primary .nav > li .dropdown { + display: inline-block; + margin-left: 0.53846153846154em; + margin-top: 0.46153846153846em; + min-width: 15.3846153846154em; + width: auto; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle { + font-size: 0.84615384615385em; + color: #fff; + display: inline-block; + line-height: 2.36363636363636em; + border: 1px solid #676c6e; + border-radius: 2px; + padding: 0 0.54545454545455em; + background: #555a5e url(img/sprite-arrow-down.svg) no-repeat right -26px; + display: block; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle:hover, +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle:focus { + text-decoration: none; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle:hover { + border-color: #7e8385; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu { + left: 0; + min-width: 0; + width: 100%; + overflow: hidden; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li, +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected { + width: auto; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li a, +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected a { + width: auto; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li a:hover, +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected a:hover { + background-color: #D5ECF9; + background-image: none; + border-bottom: 1px solid #A7D7F1; + border-top: 1px solid #A7D7F1; + color: #4D5258; +} +.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected a { + background-color: #2B99C0; + background-image: linear-gradient(top, #2ea1ca 0%, #2792b6 100%); + background-image: -o-linear-gradient(top, #2ea1ca 0%, #2792b6 100%); + background-image: -moz-linear-gradient(top, #2ea1ca 0%, #2792b6 100%); + background-image: -webkit-linear-gradient(top, #2ea1ca 0%, #2792b6 100%); + background-image: -ms-linear-gradient(top, #2ea1ca 0%, #2792b6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #2ea1ca), color-stop(1, 0, #2792b6)); + background-repeat: repeat-x; + color: #FFFFFF; +} +.header.rcue .navbar.primary .nav > li .dropdown.open .dropdown-toggle { + text-decoration: none; + box-shadow: inset 0px 2px 5px rgba(0, 0, 0, 0.2); + border-color: #7e8385; +} +.header.rcue .navbar.primary .button { + font-size: 0.84615384615385em; + margin-right: 1.36363636363636em; + margin-top: 0.63636363636364em; +} +.header.rcue .navbar.primary .button:focus { + text-decoration: none; +} .bs-sidebar { background-color: #f9f9f9; padding-top: 4.3em; @@ -197,6 +283,7 @@ table a:hover { width: 30.8em; padding-left: 1.5em; padding-right: 1.5em; + padding-top: .5em; background-color: #fff; border: 1px solid #b6b6b6; border-top: none; @@ -221,6 +308,118 @@ table a:hover { td.token-cell button { margin-top: -1px; } +/* Page: User Account */ +.user form fieldset div:first-child { + margin-top: 1em; +} +.user form fieldset p + div { + margin-top: 2.5em; +} +.user .bs-sidebar { + padding-top: 3.3em; +} +.user p.info { + font-size: 1.1em; +} +table.list { + border-left: none; + border-right: none; + margin-top: 1.5em; + border-color: #ededed; +} +table.list caption { + display: none; +} +table.list tbody tr:first-child td { + border-top: 1px solid #ededed; +} +table.list tbody tr:nth-child(2n) { + background-color: #fff; +} +table.list tbody tr td { + border-right: none; + border-left: none; +} +table.list tbody tr td.provider { + font-weight: bold; + font-size: 1.2em; +} +table.list tbody tr td.provider a { + padding-top: 0.41666666666667em; + padding-bottom: 0.33333333333333em; + display: inline-block; +} +table.list tbody tr td.soft { + color: #a1a1a1; +} +table.list tbody tr td.action { + text-align: right; + font-size: 1em; +} +table.list tbody tr.expanded { + background-color: #f6f6f6; +} +table.list tbody tr.expanded .provider { + font-size: 1.39130434782609em; + padding-top: 0.5em; + display: block; +} +table.list tbody tr.expanded p { + margin-top: 1.30434782608696em; + margin-bottom: 1.73913043478261em; +} +table.list tbody tr.expanded ul li { + margin-left: 1.30434782608696em; +} +table.list tbody tr.expanded ul li .item, +table.list tbody tr.expanded ul li .status { + margin-right: 2.60869565217391em; +} +table.list tbody tr.expanded ul li .red { + color: #BA1212; +} +table.list tbody tr.expanded .form-actions { + font-size: 0.8695652173913em; + margin-top: 0.5em; + margin-bottom: 1.5em; +} +/* Page: TOTP setup */ +.totp ol li { + margin-bottom: 2.5em; + margin-left: 2.4em; + width: 100%; +} +.totp ol li p { + font-size: 1.3em; + margin-bottom: 1.53846153846154em; +} +.totp ol li p strong { + text-indent: -1em; + float: left; + font-size: 1.84615384615385em; + font-weight: normal; + margin-top: -0.375em; + color: #999; +} +.totp ol li img { + width: 136px; +} +.totp ol li .code { + font-size: 1.3em; + margin-left: 1.53846153846154em; + vertical-align: bottom; +} +.totp ol li:last-child { + margin-bottom: 0; +} +.totp ol li .form-actions { + margin-right: 2.4em; +} +.totp ol li .form-actions input[type="button"], +.totp ol li .form-actions button, +.totp ol li .form-actions a.button { + font-size: 1.1em; +} /* Break Points */ @media (max-width: 1200px) { #container-right-bg { @@ -229,11 +428,13 @@ td.token-cell button { } } @media (max-width: 992px) { - .bs-sidebar { + .bs-sidebar, + .user .bs-sidebar { padding-top: 2em; width: 100%; } - .bs-sidebar ul li a { + .bs-sidebar ul li a, + .user .bs-sidebar ul li a { border-width: 1px; } #content-area .top-nav { @@ -243,6 +444,9 @@ td.token-cell button { margin-left: 0; width: 750px; } + .user #content-area #content { + border-top: 1px solid #cecece; + } } @media (max-width: 768px) { .container { diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.less b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.less index 409a44cddad..5fa386a81d4 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.less +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/admin-console.less @@ -15,6 +15,121 @@ body { font-family: @open-sans; } +.feedback-aligner { + top: 0.9em; +} + + +/* Header */ + +.header.rcue .navbar.primary { + + .navbar-inner { + min-height: 42px; + } + + .nav > li { + + .dropdown-label { + font-size: 0.84615384615385em; + color: #dbdada; + margin-left: 1.36363636363636em; + display: inline-block; + } + + .dropdown { + display: inline-block; + margin-left: 0.53846153846154em; + margin-top: 0.46153846153846em; + min-width: 15.3846153846154em; + width: auto; + + .dropdown-toggle { + font-size: 0.84615384615385em; + color: #fff; + display: inline-block; + line-height: 2.36363636363636em; + border: 1px solid #676c6e; + border-radius: 2px; + padding: 0 0.54545454545455em; + background: #555a5e url(img/sprite-arrow-down.svg) no-repeat right -26px; + display: block; + + &:hover, + &:focus { + text-decoration: none; + } + + &:hover { + border-color: #7e8385; + } + } + + .dropdown-menu { + + left: 0; + min-width: 0; + width: 100%; + overflow: hidden; + + li, + li.selected { + width: auto; + + a { + width: auto; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + + &:hover { + background-color: #D5ECF9; + background-image: none; + border-bottom: 1px solid #A7D7F1; + border-top: 1px solid #A7D7F1; + color: #4D5258; + } + } + } + + li.selected a { + background-color: #2B99C0; + background-image: linear-gradient(top, #2EA1CA 0%, #2792B6 100%); + background-image: -o-linear-gradient(top, #2EA1CA 0%, #2792B6 100%); + background-image: -moz-linear-gradient(top, #2EA1CA 0%, #2792B6 100%); + background-image: -webkit-linear-gradient(top, #2EA1CA 0%, #2792B6 100%); + background-image: -ms-linear-gradient(top, #2EA1CA 0%, #2792B6 100%); + background-image: -webkit-gradient( + linear, + left top, + left bottom, + color-stop(0.0, #2EA1CA), + color-stop(1,0, #2792B6) + ); + background-repeat: repeat-x; + color: #FFFFFF; + } + } + + &.open .dropdown-toggle { + text-decoration: none; + box-shadow: inset 0px 2px 5px rgba(0,0,0,0.2); + border-color: #7e8385; + } + } + } + + .button { + font-size: 0.84615384615385em; + margin-right: 1.36363636363636em; + margin-top: 0.63636363636364em; + + &:focus { + text-decoration: none; + } + } +} + + .bs-sidebar { background-color: @bg-grey; padding-top: 4.3em; @@ -256,6 +371,7 @@ table { width: 30.8em; padding-left: 1.5em; padding-right: 1.5em; + padding-top: .5em; background-color: #fff; border: 1px solid #b6b6b6; border-top: none; @@ -287,6 +403,158 @@ td.token-cell button { } +/* Page: User Account */ + +.user { + + form fieldset div:first-child { + margin-top: 1em; + } + + form fieldset p + div { + margin-top: 2.5em; + } + + .bs-sidebar { + padding-top: 3.3em; + } + + p.info { + font-size: 1.1em; + } +} + +table.list { + + border-left: none; + border-right: none; + margin-top: 1.5em; + border-color: #ededed; + + caption { + display: none; + } + + tbody tr { + + &:first-child td { + border-top: 1px solid #ededed; + } + + &:nth-child(2n) { + background-color: #fff; + } + + td { + border-right: none; + border-left: none; + + &.provider { + font-weight: bold; + font-size: 1.2em; + + a { + padding-top: 0.41666666666667em; + padding-bottom: 0.33333333333333em; + display: inline-block; + } + } + + &.soft { + color: #a1a1a1; + } + + &.action { + text-align: right; + font-size: 1em; + } + } + + &.expanded { + + background-color: #f6f6f6; + + .provider { + font-size: 1.39130434782609em; + padding-top: 0.5em; + display: block; + } + + p { + margin-top: 1.30434782608696em; + margin-bottom: 1.73913043478261em; + } + + ul li { + margin-left: 1.30434782608696em; + + .item, + .status { + margin-right: 2.60869565217391em; + } + + .red { + color: #BA1212; + } + } + + .form-actions { + font-size: 0.8695652173913em; + margin-top: 0.5em; + margin-bottom: 1.5em; + } + } + } +} + +/* Page: TOTP setup */ + +.totp ol li { + margin-bottom: 2.5em; + margin-left: 2.4em; + width: 100%; + + p { + font-size: 1.3em; + margin-bottom: 1.53846153846154em; + + strong { + text-indent: -1em; + float: left; + font-size: 1.84615384615385em; + font-weight: normal; + margin-top: -0.375em; + color: #999; + } + } + + img { + width: 136px; + } + + .code { + font-size: 1.3em; + margin-left: 1.53846153846154em; + vertical-align: bottom; + } + + &:last-child { + margin-bottom: 0; + } + + .form-actions { + margin-right: 2.4em; + + input[type="button"], + button, + a.button { + font-size: 1.1em; + } + } +} + + + /* Break Points */ @media (max-width: 1200px) { @@ -299,7 +567,8 @@ td.token-cell button { @media (max-width: 992px) { - .bs-sidebar { + .bs-sidebar, + .user .bs-sidebar { padding-top: 2em; width: 100%; @@ -316,6 +585,10 @@ td.token-cell button { margin-left: 0; width: 750px; } + + .user #content-area #content { + border-top: 1px solid #cecece; + } } diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.css b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.css index 25762091f35..a23e981a1bf 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.css +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.css @@ -29,12 +29,19 @@ a:hover { text-decoration: underline; } /* Styles from Gabriel */ +a:hover, +a:focus { + color: #0099d3; +} strong { font-weight: bold; } .hidden { display: none; } +.feedback.show { + display: inline-block !important; +} .pull-right { float: right; } diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.less b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.less index 9af7c45f714..ee48322433b 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.less +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/base.less @@ -33,6 +33,11 @@ a:hover { /* Styles from Gabriel */ +a:hover, +a:focus { + color: #0099d3; +} + strong { font-weight: bold; } @@ -41,6 +46,10 @@ strong { display: none; } +.feedback.show { + display: inline-block!important; +} + .pull-right { float: right; } diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.css b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.css index 5f105e2d27b..6b3afd32e62 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.css +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.css @@ -33,7 +33,6 @@ input[type="text"].error, input[type="password"].error, input[type="email"].error { border-color: #ba1212; - background-color: #f8e7e7; transition: all 0.33s ease-in-out; -moz-transition: all 0.33s ease-in-out; -webkit-transition: all 0.33s ease-in-out; @@ -143,7 +142,8 @@ a.button { padding-bottom: 0; line-height: 2.18181818181818em; } -button.primary { +button.primary, +.button.primary { border-color: #21799e; background-image: linear-gradient(top, #00a9ec 0%, #009bd3 100%); background-image: -o-linear-gradient(top, #00a9ec 0%, #009bd3 100%); @@ -154,14 +154,37 @@ button.primary { color: #fff; } button.primary:hover, -button.primary:focus { +button.primary:focus, +.button.primary:hover, +.button.primary:focus { background-color: #009BD3; } -button.primary:enabled:active { +button.primary:enabled:active, +.button.primary:enabled:active { background-color: #0099d4; box-shadow: inset 0 0 5px 3px #0074ae; } /* Code from Gabriel */ +button.primary, +.button.primary { + background-color: #00a2df; +} +button.primary:hover, +.button.primary:hover, +button.primary:active, +.button.primary:active, +button.primary:focus, +.button.primary:focus { + background-image: none; + color: #fff; +} +.rcue-login-register.register .two-fields input[type="text"] { + width: 121px; + min-width: 0; +} +.rcue-login-register.register .two-fields input + input { + margin-left: 10px; +} .search-comp { position: relative; display: inline-block; @@ -187,6 +210,23 @@ button.primary:enabled:active { width: 20em; font-weight: normal; } +.feedback-aligner { + position: absolute; + top: 1.5em; + text-align: center; + width: 100%; + height: 0; + z-index: 100; +} +.feedback-aligner .feedback { + position: relative; + display: inline-block; + text-align: left; + border-width: 1px; +} +.feedback-aligner .feedback p { + border-width: 1px; +} .feedback { position: absolute; opacity: 0; @@ -194,30 +234,49 @@ button.primary:enabled:active { -moz-transition: opacity 0.33s ease-in-out; -webkit-transition: opacity 0.33s ease-in-out; } +.feedback p { + padding: 0.90909090909091em 3.63636363636364em; + border-style: solid; + border-width: 1px 1px 0px 1px; + background-repeat: no-repeat; + background-position: 1.27272727272727em center; + font-size: 1.1em; + line-height: 1.4em; + border-radius: 2px; + color: #4d5258; + margin-bottom: 0; +} .feedback.show { opacity: 1; } -.feedback.error { - background-image: url(img/feedback-error-arrow-down.svg); +.feedback.bottom-left { background-position: left bottom; background-repeat: no-repeat; padding-bottom: 1em; } +.feedback.bottom-left p { + background-position: 1.27272727272727em 1.63636363636364em; +} +.feedback.error { + background-image: url(img/feedback-error-arrow-down.svg); +} .feedback.error p { border-color: #b91415; background-image: url(img/feedback-error-sign.svg); background-color: #f8e7e7; - color: #4d5258; } -.feedback p { - padding: 1em 3.63636363636364em; - border-style: solid; - border-width: 1px 1px 0px 1px; - background-repeat: no-repeat; - background-position: 1.27272727272727em 1.63636363636364em; - font-size: 1.1em; - line-height: 1.27272727272727em; - border-radius: 2px; +.feedback.success { + background-image: url(img/feedback-success-arrow-down.svg); +} +.feedback.success p { + border-color: #4b9e39; + background-image: url(img/feedback-success-sign.svg); + background-color: #e4f1e1; +} +.feedback.warning p { + border-color: #f17528; + background-image: url(img/feedback-warning-sign.svg); + background-color: #fef1e9; } button, a.button { @@ -240,6 +299,20 @@ button[class^="icon-"] { button[class^="icon-"]:hover { background-image: url(img/sprites.png); } +button.link { + border: none; + background-color: transparent; + background: none; + box-shadow: none; + font-weight: normal; + font-size: 1em; + color: #0099D3; + letter-spacing: 0; + padding: 0; +} +button.link:hover { + text-decoration: underline; +} legend { font-size: 1em; border-width: 1px 0 0 0; @@ -266,6 +339,12 @@ legend .icon-info { legend .icon-info:hover { background-image: url(img/sprites.png); } +fieldset.border-top { + border-width: 1px 0 0 0; + border-style: solid; + border-color: #e9e8e8; + padding-top: 2em; +} .form-group { display: block; margin-bottom: 1em; @@ -295,14 +374,15 @@ legend .icon-info:hover { margin-top: 4px; } .form-group .required { - position: absolute; - left: 10em; font-size: 1.1em; color: #CB2915; } legend + .form-group { padding-top: 1em; } +legend + table { + margin-top: 1em; +} .code { font-family: Courier, ​monospace; } @@ -437,6 +517,11 @@ input[type="email"].tiny { .select-rcue option:hover { background-color: #d5ecf9; } +.select2-container { + float: left; + margin-top: 0.3em; + margin-bottom: 0.3em; +} .select2-container .select2-choice > .select2-chosen { line-height: 2.1em; padding-left: 0.90909090909091em; @@ -448,13 +533,18 @@ input[type="email"].tiny { .select2-container .select2-choice .select2-arrow { display: none; } +.select2-dropdown-open { + background-color: #fff; +} .select2-dropdown-open .select2-choice, .select2-dropdown-open .select2-choices { - background-image: url(img/chosen-arrow-down.png), -moz-linear-gradient(center top, #eeeeee 0%, #ffffff 50%); border-bottom: none; border-radius: 2px 2px 0 0; + background-image: url(img/chosen-arrow-down.png); + background-color: transparent; background-repeat: no-repeat; background-position: right top; + box-shadow: none; } .select2-dropdown-open .select2-choice, .select2-dropdown-open.select2-drop-above .select2-choice, @@ -467,24 +557,53 @@ input[type="email"].tiny { } .select2-drop-active { border-radius: 0 0 2px 2px; - margin-top: -3px; - padding-top: 3px; + margin-top: -1px; + padding-top: 4px; } .select2-container.select2-drop-above .select2-choice { border-radius: 0 0 2px 2px; - background-image: url(img/chosen-arrow-up.png), -moz-linear-gradient(center top, #eeeeee 0%, #ffffff 50%); + background-image: url(img/chosen-arrow-up.png); background-repeat: no-repeat; - background-position: right -1px; + background-position: right 0; + box-shadow: none; } .select2-drop.select2-drop-above { border-radius: 2px 2px 0 0; padding-top: 0; - margin-top: 0; + margin-top: 2px; } .select2-drop.select2-drop-above.select2-drop-active, .select2-drop-active { border-color: #62AFDB; } +.select2-results { + padding-left: 0; + margin-right: 0; +} +.select2-results li { + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.select2-results .select2-result-label, +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-selection-limit { + font-size: 1.1em; + padding-left: 1.09090909090909em; +} +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-selection-limit { + color: #838383; + padding-top: 3px; + padding-bottom: 4px; +} +.select2-results .select2-highlighted { + background-color: #d5ecf9; + border-top: 1px solid #a7d7f1; + border-bottom: 1px solid #a7d7f1; + color: #4d5258; +} .input-group input + .select-rcue { border-radius: 0 2px 2px 0; border-left: 0; @@ -543,6 +662,7 @@ input[type="email"].tiny { margin: 0.272727272727273em 0; box-shadow: none; outline: 0 none; + float: left; } .tokenfield.form-control:hover { border-color: #62afdb; @@ -552,7 +672,7 @@ input[type="email"].tiny { box-shadow: #62afdb 0 0 5px; } .token { - display: inline-block; + float: left; background-color: #d4ecf8; border: 1px solid #a3d7f0; border-radius: 1px; @@ -563,12 +683,12 @@ input[type="email"].tiny { outline: 0 none; } .token span { - display: inline-block; + float: left; font-size: 1.1em; line-height: 1.45454545454545em; } .token .close { - text-indent: -99999em; + text-indent: -9999999em; width: 1.6em; height: 1.6em; line-height: 1.6em; @@ -586,5 +706,72 @@ input[type="email"].tiny { } .form-actions .primary { float: right; - margin-left: 8px; + margin-left: 0.90909090909091em; +} +.form-actions a { + font-size: 1.1em; + margin-right: 0.90909090909091em; +} +.changing-selectors .select-title { + display: inline-block; +} +.changing-selectors .select-title label { + font-size: 1.1em; + margin-left: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.5em; + display: block; + font-weight: normal; +} +.changing-selectors select { + min-height: 150px; + font-size: 1.1em; + padding: 0.545454545454545em; + min-width: 18.1818181818182em; + border: 1px #b6b6b6 solid; + border-radius: 2px; + box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.1); + color: #333; + max-width: 200px; + width: auto; + min-width: 150px; + display: inline-block; +} +.changing-selectors select:hover { + border-color: #62afdb; +} +.changing-selectors select option { + padding: 0.36363636363636em 0.45454545454545em; + display: block; +} +.changing-selectors select option[disabled="disabled"] { + color: #aaa; +} +.changing-selectors .middle-buttons { + display: inline-block; + width: 4.5em; + margin-left: 0.8em; + margin-right: 0.8em; + vertical-align: middle; +} +.changing-selectors .middle-buttons button:first-child { + margin-bottom: 0.5em; +} +.changing-selectors .middle-buttons button.disabled span { + opacity: 0.4; + filter: alpha(opacity=40); +} +.changing-selectors .middle-buttons button span { + margin-right: 0; +} +.breadcrumb { + background: none; + margin: 5px 0 5px 0; + padding: 0; +} +.breadcrumb li a { + font-size: 1.1em; +} +.breadcrumb > li + li:before { + content: "» "; } diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.less b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.less index dfc353d9e58..962e7b1d42d 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.less +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/forms.less @@ -35,7 +35,6 @@ input[type="email"], &.error { border-color: #ba1212; - background-color: #f8e7e7; transition: all .33s ease-in-out; -moz-transition: all .33s ease-in-out; -webkit-transition: all .33s ease-in-out; @@ -150,7 +149,8 @@ a.button { line-height: 2.18181818181818em; } -button.primary { +button.primary, +.button.primary { border-color: #21799e; background-image: linear-gradient(top, #00A9EC 0%, #009BD3 100%); background-image: -o-linear-gradient(top, #00A9EC 0%, #009BD3 100%); @@ -168,11 +168,14 @@ button.primary { } button.primary:hover, -button.primary:focus { +button.primary:focus, +.button.primary:hover, +.button.primary:focus { background-color: #009BD3; } -button.primary:enabled:active { +button.primary:enabled:active, +.button.primary:enabled:active { background-color: #0099d4; box-shadow: inset 0 0 5px 3px #0074ae; } @@ -180,6 +183,30 @@ button.primary:enabled:active { /* Code from Gabriel */ +button.primary, +.button.primary { + background-color: #00a2df; + + &:hover, + &:active, + &:focus { + background-image: none; + color: #fff; + } +} + +.rcue-login-register.register .two-fields { + + input[type="text"] { + width: 121px; + min-width: 0; + } + + input + input { + margin-left: 10px; + } +} + .search-comp { position: relative; display: inline-block; @@ -210,40 +237,88 @@ button.primary:enabled:active { } } +.feedback-aligner { + position: absolute; + top: 1.5em; + text-align: center; + width: 100%; + height: 0; + z-index: 100; + + .feedback { + position: relative; + display: inline-block; + text-align: left; + border-width: 1px; + + p { + border-width: 1px; + } + } +} + .feedback { position: absolute; opacity: 0; transition: opacity .33s ease-in-out; -moz-transition: opacity .33s ease-in-out; -webkit-transition: opacity .33s ease-in-out; + + p { + padding: 0.90909090909091em 3.63636363636364em; + border-style: solid; + border-width: 1px 1px 0px 1px; + background-repeat: no-repeat; + background-position: 1.27272727272727em center; + font-size: 1.1em; + line-height: 1.4em; + border-radius: 2px; + color: #4d5258; + margin-bottom: 0; + } &.show { opacity: 1; } - - &.error { - background-image: url(img/feedback-error-arrow-down.svg); + + &.bottom-left { background-position: left bottom; background-repeat: no-repeat; padding-bottom: 1em; + p { + background-position: 1.27272727272727em 1.63636363636364em; + } + } + + &.error { + background-image: url(img/feedback-error-arrow-down.svg); + p { border-color: #b91415; background-image: url(img/feedback-error-sign.svg); background-color: #f8e7e7; - color: #4d5258; } } - p { - padding: 1em 3.63636363636364em; - border-style: solid; - border-width: 1px 1px 0px 1px; - background-repeat: no-repeat; - background-position: 1.27272727272727em 1.63636363636364em; - font-size: 1.1em; - line-height: 1.27272727272727em; - border-radius: 2px; + &.success { + background-image: url(img/feedback-success-arrow-down.svg); + + p { + border-color: #4b9e39; + background-image: url(img/feedback-success-sign.svg); + background-color: #e4f1e1; + } + } + + &.warning { + + p { + border-color: #f17528; + background-image: url(img/feedback-warning-sign.svg); + background-color: #fef1e9; + } + } } @@ -273,6 +348,22 @@ button[class^="icon-"] { } } +button.link { + border: none; + background-color: transparent; + background: none; + box-shadow: none; + font-weight: normal; + font-size: 1em; + color: #0099D3; + letter-spacing: 0; + padding: 0; + + &:hover { + text-decoration: underline; + } +} + legend { font-size: 1em; border-width: 1px 0 0 0; @@ -304,6 +395,13 @@ legend { } } +fieldset.border-top { + border-width: 1px 0 0 0; + border-style: solid; + border-color: #e9e8e8; + padding-top: 2em; +} + .form-group { display: block; margin-bottom: 1em; @@ -338,8 +436,6 @@ legend { } .required { - position: absolute; - left: 10em; font-size: 1.1em; color: #CB2915; } @@ -349,6 +445,10 @@ legend + .form-group { padding-top: 1em; } +legend + table { + margin-top: 1em; +} + .code { font-family: Courier, ​monospace; } @@ -521,32 +621,42 @@ input[type="email"] { } } -.select2-container .select2-choice { +.select2-container { + float: left; + margin-top: 0.3em; + margin-bottom: 0.3em; - &> .select2-chosen { - line-height: 2.1em; - padding-left: 0.90909090909091em; - margin-right: 0; - font-size: 1.1em; - padding-right: 2.36363636363636em; - padding-right: 26px; - } + .select2-choice { - .select2-arrow { - display: none; + &> .select2-chosen { + line-height: 2.1em; + padding-left: 0.90909090909091em; + margin-right: 0; + font-size: 1.1em; + padding-right: 2.36363636363636em; + padding-right: 26px; + } + + .select2-arrow { + display: none; + } } } .select2-dropdown-open { - + .select2-choice, .select2-choices { - background-image: url(img/chosen-arrow-down.png), -moz-linear-gradient(center top , #eee 0%, #fff 50%); border-bottom: none; border-radius: 2px 2px 0 0; + background-image: url(img/chosen-arrow-down.png); + background-color: transparent; background-repeat: no-repeat; background-position: right top; + box-shadow: none; } + + background-color: #fff; } .select2-dropdown-open, @@ -564,21 +674,22 @@ input[type="email"] { .select2-drop-active { border-radius: 0 0 2px 2px; - margin-top: -3px; - padding-top: 3px; + margin-top: -1px; + padding-top: 4px; } .select2-container.select2-drop-above .select2-choice { border-radius: 0 0 2px 2px; - background-image: url(img/chosen-arrow-up.png), -moz-linear-gradient(center top , #eee 0%, #fff 50%); + background-image: url(img/chosen-arrow-up.png); background-repeat: no-repeat; - background-position: right -1px; + background-position: right 0; + box-shadow: none; } .select2-drop.select2-drop-above { border-radius: 2px 2px 0 0; padding-top: 0; - margin-top: 0; + margin-top: 2px; } .select2-drop.select2-drop-above.select2-drop-active, @@ -586,6 +697,39 @@ input[type="email"] { border-color: #62AFDB; } +.select2-results { + padding-left: 0; + margin-right: 0; + + li { + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + } + + .select2-result-label, + .select2-no-results, + .select2-searching, + .select2-selection-limit { + font-size: 1.1em; + padding-left: 1.09090909090909em; + } + + .select2-no-results, + .select2-searching, + .select2-selection-limit { + color: #838383; + padding-top: 3px; + padding-bottom: 4px; + } + + .select2-highlighted { + background-color: #d5ecf9; + border-top: 1px solid #a7d7f1; + border-bottom: 1px solid #a7d7f1; + color: #4d5258; + } +} + .input-group input + .select-rcue { border-radius: 0 2px 2px 0; border-left: 0; @@ -649,6 +793,7 @@ input[type="email"] { margin: 0.272727272727273em 0; box-shadow: none; outline: 0 none; + float: left; } &:hover { @@ -662,7 +807,7 @@ input[type="email"] { } .token { - display: inline-block; + float: left; background-color: #d4ecf8; border: 1px solid #a3d7f0; border-radius: 1px; @@ -673,13 +818,13 @@ input[type="email"] { outline: 0 none; span { - display: inline-block; + float: left; font-size: 1.1em; line-height: 1.45454545454545em; } .close { - text-indent: -99999em; + text-indent: -9999999em; width: 1.6em; height: 1.6em; line-height: 1.6em; @@ -699,6 +844,99 @@ input[type="email"] { .primary { float: right; - margin-left: 8px; + margin-left: 0.90909090909091em; + } + + a { + font-size: 1.1em; + margin-right: 0.90909090909091em; + } +} + +.changing-selectors { + + .select-title { + + display: inline-block; + + label { + font-size: 1.1em; + margin-left: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.5em; + display: block; + font-weight: normal; + } + + } + + select { + min-height: 150px; + font-size: 1.1em; + padding: 0.545454545454545em; + min-width: 18.1818181818182em; + border: 1px #b6b6b6 solid; + border-radius: 2px; + box-shadow: inset 0px 2px 2px rgba(0,0,0,0.1); + color: #333; + max-width: 200px; + width: auto; + min-width: 150px; + display: inline-block; + + &:hover { + border-color: #62afdb; + } + + option { + padding: 0.36363636363636em 0.45454545454545em; + display: block; + + &[disabled="disabled"] { + color: #aaa; + } + } + } + + .middle-buttons { + display: inline-block; + width: 4.5em; + margin-left: 0.8em; + margin-right: 0.8em; + vertical-align: middle; + + button { + + &:first-child { + margin-bottom: 0.5em; + } + + &.disabled span { + opacity: 0.4; + filter: alpha(opacity=40); + } + + span { + margin-right: 0; + } + } + } +} + +.breadcrumb { + + background: none; + margin: 5px 0 5px 0; + padding: 0; + + li { + + a { + font-size: 1.1em; + } + } + + > li + li:before { + content: "» "; } } \ No newline at end of file diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/img/sprite-arrow-down.png b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/img/sprite-arrow-down.png new file mode 100644 index 00000000000..fb658194152 Binary files /dev/null and b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/img/sprite-arrow-down.png differ diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/img/sprite-arrow-down.svg b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/img/sprite-arrow-down.svg new file mode 100644 index 00000000000..87c4cda27fe --- /dev/null +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/img/sprite-arrow-down.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css index 1b0f3da9c49..15a1f343bf7 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css @@ -88,25 +88,26 @@ table tbody tr:hover { table tbody tr { border-top: 1px solid #ededed; } -table tbody tr.selected { - background-color: #eaf5fb; -} -table tbody tr.selected td:first-child { - background-image: url(img/icon-row-selected.svg); - background-position: 0.2em center; - background-repeat: no-repeat; -} table tbody tr:hover { cursor: default; } +table tbody tr td { + vertical-align: middle; +} table tbody tr td.token-cell { padding: 0 0.7em; + line-height: 0; } table tbody tr td.token-cell .token { padding-top: 0.18181818181818em; padding-bottom: 0.18181818181818em; - margin-top: 7px; + margin-top: 6px; + margin-bottom: 5px; +} +table tbody tr td.token-cell button { + float: left; margin-bottom: 7px; + margin-top: 8px; } table tbody.selectable-rows tr:hover { cursor: pointer; @@ -115,6 +116,16 @@ table tbody.selectable-rows tr:hover { table tbody.selectable-rows tr:first-child td { padding-top: 9px; } +table tbody.selectable-rows tr.selected, +table tbody.selectable-rows tr.selected:hover { + background-color: #eaf5fb; +} +table tbody.selectable-rows tr.selected td:first-child, +table tbody.selectable-rows tr.selected:hover td:first-child { + background-image: url(img/icon-row-selected.svg); + background-position: 0.2em center; + background-repeat: no-repeat; +} table tfoot tr { border-top: 1px solid #cecece; } diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.less b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.less index 0a35bc53318..6426f290ea8 100644 --- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.less +++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.less @@ -120,28 +120,29 @@ table { border-top: 1px solid #ededed; - &.selected { - background-color: #eaf5fb; - - td:first-child { - background-image: url(img/icon-row-selected.svg); - background-position: 0.2em center; - background-repeat: no-repeat; - } - } - &:hover { cursor: default; } + td { + vertical-align: middle; + } + td.token-cell { padding: 0 0.7em; + line-height: 0; .token { padding-top: 0.18181818181818em; padding-bottom: 0.18181818181818em; - margin-top: 7px; + margin-top: 6px; + margin-bottom: 5px; + } + + button { + float: left; margin-bottom: 7px; + margin-top: 8px; } } } @@ -156,6 +157,17 @@ table { tr:first-child td { padding-top: 9px; } + + tr.selected, + tr.selected:hover { + background-color: #eaf5fb; + + td:first-child { + background-image: url(img/icon-row-selected.svg); + background-position: 0.2em center; + background-repeat: no-repeat; + } + } } } diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js index ff6d9cc658e..93a1593c5de 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js @@ -103,9 +103,9 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht Current.realm = Current.realms[i]; } } + $location.url("/realms/" + id); + Notifications.success("Created realm"); }); - $location.url("/realms/" + id); - Notifications.success("Created realm"); }); } else { console.log('updating realm...'); @@ -155,7 +155,7 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm, console.log('RealmRequiredCredentialsCtrl'); $scope.realm = { - id : realm.id, realm : realm.realm, + id : realm.id, realm : realm.realm, social : realm.social, requiredCredentials : realm.requiredCredentials, requiredApplicationCredentials : realm.requiredApplicationCredentials, requiredOAuthClientCredentials : realm.requiredOAuthClientCredentials @@ -200,7 +200,7 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm, module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications) { console.log('RealmTokenDetailCtrl'); - $scope.realm = { id : realm.id, realm : realm.realm, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan }; + $scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan }; $scope.realm.tokenLifespanUnit = 'Seconds'; $scope.realm.accessCodeLifespanUnit = 'Seconds'; diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/menu.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/menu.html index 67864661ad5..2d0c9bdfbd4 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/menu.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/menu.html @@ -19,16 +19,25 @@ diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html index 84aa0c63924..fcb89f14308 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-detail.html @@ -4,22 +4,27 @@
-

New Realm

-

Realm: {{realm.realm}}

+ +

Add Realm

+

{{realm.realm}} General Settings

* Required fields

- Required Settings + Required Settings
- * +
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html index 5d0c1c41d95..1c2ccba8357 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html @@ -1,5 +1,5 @@
    -
  • Realm Settings
  • +
  • Settings
  • Users
  • Applications
  • diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-list.html index a8194521cf1..f2d0a3e5f65 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-list.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-list.html @@ -4,32 +4,47 @@
    -

    Realm Roles

    + +

    {{realm.realm}} Roles

    - + - - - - - - - + + + + + + + + + + diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html index d4ffcceb352..902394db626 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html +++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/role-mappings.html @@ -4,36 +4,48 @@
    -

    User Role Mappings for {{user.username}}

    -

    + +

    {{user.username}}'s Role Mappings

    +

    All fields required

    -
    - Realm Roles +
    -
    - - - - + +
    +
    + + +
    +
    + + + +
    +
    Application Roles
    diff --git a/forms/src/main/java/org/keycloak/forms/UrlBean.java b/forms/src/main/java/org/keycloak/forms/UrlBean.java index fdb11feec36..cb82aaa6bad 100644 --- a/forms/src/main/java/org/keycloak/forms/UrlBean.java +++ b/forms/src/main/java/org/keycloak/forms/UrlBean.java @@ -120,6 +120,10 @@ public class UrlBean { return Urls.accountTotpPage(baseURI, realm.getId()).toString(); } + public String getTotpRemoveUrl() { + return Urls.accountTotpRemove(baseURI, realm.getId()).toString(); + } + public String getEmailVerificationUrl() { return Urls.accountEmailVerification(baseURI, realm.getId()).toString(); } diff --git a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java index 1cd60fa13a6..27c2df1bd8b 100644 --- a/forms/src/main/java/org/keycloak/service/FormServiceImpl.java +++ b/forms/src/main/java/org/keycloak/service/FormServiceImpl.java @@ -81,6 +81,10 @@ public class FormServiceImpl implements FormService { Map attributes = new HashMap(); + if (dataBean.getError() != null){ + attributes.put("message", new ErrorBean(dataBean.getError(), dataBean.getErrorType())); + } + RealmBean realm = new RealmBean(dataBean.getRealm()); attributes.put("template", new TemplateBean(realm, dataBean.getContextPath())); @@ -145,10 +149,6 @@ public class FormServiceImpl implements FormService { private class CommandPassword implements Command { public void exec(Map attributes, FormServiceDataBean dataBean) { - if (dataBean.getError() != null){ - attributes.put("message", new ErrorBean(dataBean.getError(), dataBean.getErrorType())); - } - RealmBean realm = new RealmBean(dataBean.getRealm()); attributes.put("realm", realm); diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css index 082dbdd0043..f3fadb5a8cf 100644 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/forms.css @@ -75,19 +75,23 @@ a.button.btn-primary { border-style: solid; } input[type="button"].btn-primary:hover, +input[type="submit"].btn-primary:hover, button.btn-primary:hover, a.button.btn-primary:hover, input[type="button"].btn-primary:focus, +input[type="submit"].btn-primary:focus, button.btn-primary:focus, a.button.btn-primary:focus { background-color: #009BD3; } input[type="button"].btn-primary:active, +input[type="submit"].btn-primary:active, button.btn-primary:active, a.button.btn-primary:active { background-color: #0099d4; } input[type="button"].disabled, +input[type="submit"].disabled, button.disabled, a.button.disabled { border-color: #cfcdcd; @@ -99,25 +103,30 @@ a.button.disabled { letter-spacing: 0.06363636363636em; } input[type="button"].disabled:hover, +input[type="submit"].disabled:hover, button.disabled:hover, a.button.disabled:hover { cursor: default; } input[type="button"].disabled:active, +input[type="submit"].disabled:active, button.disabled:active, a.button.disabled:active { box-shadow: none; } input[type="button"]:hover, +input[type="submit"]:hover, button:hover, a.button:hover, input[type="button"]:focus, +input[type="submit"]:focus, button:focus, a.button:focus { background-image: none; cursor: pointer; } input[type="button"]:active, +input[type="submit"]:active, button:active, a.button:active { background-image: none; @@ -616,7 +625,7 @@ input[type="email"].tiny { line-height: 1.45454545454545em; } .tokenfield.form-control .token .close { - text-indent: -9999999em; + text-indent: -99999em; width: 1.6em; height: 1.6em; line-height: 1.6em; diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css index c3b1bd7b1d4..04e3340ab32 100644 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/header.css @@ -60,6 +60,7 @@ body { } .header.rcue .navbar { margin-bottom: 0; + z-index:auto; } .header.rcue .navbar.primary { font-size: 13px; diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/sprites.css b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/sprites.css index fcfb0cc1901..1a35f748bd1 100755 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/css/sprites.css +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/css/sprites.css @@ -5,7 +5,7 @@ height: 16px; background-image: url(img/sprites.png); /* Modified by Gabriel */ background-repeat: no-repeat; - text-indent: -9999999em; + text-indent: -99999em; margin-right: 0.5em; vertical-align: text-top; } diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl index e45628490cb..253ab75508c 100755 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/login-totp.ftl @@ -25,7 +25,7 @@
    - + <#elseif section = "info"> diff --git a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl index d8cca1f1c7f..de023e1bfb8 100644 --- a/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl +++ b/forms/src/main/resources/META-INF/resources/forms/theme/default/template-login.ftl @@ -31,17 +31,16 @@

    Application login area

    + <#if error?has_content> + + <#nested "form">
    - <#if error?has_content> - - - <#if social.displaySocialProviders>
    - - +
    Table of realm roles No configured realm roles...
    -
    - - -
    -
    Role NameDescription
    +
    + Add + +
    +
    +
    + + +
    +
    Role NameDescription
    Connected as john@google.comRemove Google + Remove Google +
    +

    + If the totp authentication is required by the realm and you remove your configured authenticator, + you will have to reconfigure it immediately or on the next login. +

diff --git a/forms/src/main/resources/org/keycloak/forms/messages.properties b/forms/src/main/resources/org/keycloak/forms/messages.properties index 92722f94e7f..03943dc0e16 100644 --- a/forms/src/main/resources/org/keycloak/forms/messages.properties +++ b/forms/src/main/resources/org/keycloak/forms/messages.properties @@ -37,6 +37,9 @@ invalidPasswordExisting=Invalid existing password invalidPasswordConfirm=Password confirmation doesn't match invalidTotp=Invalid authenticator code +successTotp=Google authenticator configured. +successTotpRemoved=Google authenticator removed. + usernameExists=Username already exists error=A system error has occured, contact admin @@ -50,4 +53,5 @@ emailForgotHeader=Forgot Your Password? emailUpdateHeader=Update password emailSent=You should receive an email shortly with further instructions. emailError=Invalid username or email. +emailErrorInfo=Please, fill in the fields again. emailInstruction=Enter your username and email address and we will send you instructions on how to create a new password. \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index b78d9edc4ec..2874971439d 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -164,6 +164,15 @@ public class AccountService { return accessCodeEntry; } + @Path("totp-remove") + @GET + public Response processTotpRemove() { + UserModel user = getUserFromAuthManager(); + user.setTotp(false); + return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.ErrorType.SUCCESS) + .setUser(user).forwardToTotp(); + } + @Path("totp") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @@ -206,7 +215,8 @@ public class AccountService { if (accessCodeEntry != null) { return redirectOauth(user, accessCodeEntry); } else { - return Flows.forms(realm, request, uriInfo).setUser(user).forwardToTotp(); + return Flows.forms(realm, request, uriInfo).setError("successTotp").setErrorType(FormFlows.ErrorType.SUCCESS) + .setUser(user).forwardToTotp(); } } diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index d3322d4bf92..89742bf7528 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -192,6 +192,11 @@ public class TokenService { String username = formData.getFirst("username"); UserModel user = realm.getUser(username); + if (user == null){ + return Flows.forms(realm, request, uriInfo).setError(Messages.INVALID_USER).setFormData(formData) + .forwardToLogin(); + } + isTotpConfigurationRequired(user); isEmailVerificationRequired(user); diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java index 1da6a61ef36..b6a86a2cd4b 100755 --- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java +++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java @@ -55,6 +55,10 @@ public class Urls { return accountBase(baseUri).path(AccountService.class, "totpPage").build(realmId); } + public static URI accountTotpRemove(URI baseUri, String realmId) { + return accountBase(baseUri).path(AccountService.class, "processTotpRemove").build(realmId); + } + public static URI accountEmailVerification(URI baseUri, String realmId) { return accountBase(baseUri).path(AccountService.class, "emailVerification").build(realmId); } diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index f0f3eeab35a..3614016c9b6 100644 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -200,6 +200,13 @@ 1.6 + + org.codehaus.mojo + exec-maven-plugin + + ${project.basedir} + + diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java index b7f23186683..17e755ee7fb 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java @@ -112,17 +112,27 @@ public class KeycloakServer { if (System.getProperties().containsKey("resources")) { String resources = System.getProperty("resources"); - if (resources == null || resources.equals("")) { - for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) { - if (c.contains("keycloak" + File.separator + "testsuite" + File.separator + "integration")) { - config.setResourcesHome(c.replaceFirst("testsuite.integration.*", "")); + if (resources == null || resources.equals("") || resources.equals("true")) { + if (System.getProperties().containsKey("maven.home")) { + resources = System.getProperty("user.dir").replaceFirst("testsuite.integration.*", ""); + } else { + for (String c : System.getProperty("java.class.path").split(File.pathSeparator)) { + if (c.contains(File.separator + "testsuite" + File.separator + "integration")) { + resources = c.replaceFirst("testsuite.integration.*", ""); + } } } - } else { - config.setResourcesHome(resources); } + + File dir = new File(resources).getAbsoluteFile(); + if (!dir.isDirectory() || !new File(dir, "admin-ui").isDirectory()) { + throw new RuntimeException("Invalid resources directory"); + } + + config.setResourcesHome(dir.getAbsolutePath()); } + final KeycloakServer keycloak = new KeycloakServer(config); keycloak.sysout = true; keycloak.start(); @@ -291,9 +301,9 @@ public class KeycloakServer { if (path.startsWith("/forms")) { file = file(resourcesHome, "forms", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar)); } else if (path.startsWith("/admin")) { - file = file(resourcesHome, "admin-ui", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar)); + file = file(resourcesHome, "admin-ui", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar)); if (!file.isFile()) { - file = file(resourcesHome, "admin-ui-styles", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar)); + file = file(resourcesHome, "admin-ui-styles", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar)); } } else { throw new IOException("Unknown resource " + path); @@ -305,10 +315,9 @@ public class KeycloakServer { private static File file(String... path) { StringBuilder s = new StringBuilder(); - s.append(path[0]); - for (int i = 1; i < path.length; i++) { + for (String p : path) { s.append(File.separator); - s.append(path[i]); + s.append(p); } return new File(s.toString()); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java index 916513d7ea4..bb70d976cfe 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java @@ -30,6 +30,8 @@ import org.keycloak.services.managers.RealmManager; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; +import org.keycloak.testsuite.OAuthClient; +import org.keycloak.testsuite.pages.AccountTotpPage; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginConfigTotpPage; @@ -54,9 +56,6 @@ public class RequiredActionTotpSetupTest { public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) { appRealm.addRequiredCredential(CredentialRepresentation.TOTP); appRealm.setResetPasswordAllowed(true); - - UserModel user = appRealm.getUser("test-user@localhost"); - user.addRequiredAction(RequiredAction.CONFIGURE_TOTP); } }); @@ -76,6 +75,12 @@ public class RequiredActionTotpSetupTest { @WebResource protected LoginConfigTotpPage totpPage; + @WebResource + protected AccountTotpPage accountTotpPage; + + @WebResource + protected OAuthClient oauth; + @WebResource protected RegisterPage registerPage; @@ -101,9 +106,69 @@ public class RequiredActionTotpSetupTest { totpPage.assertCurrent(); - totpPage.configure(totp.generate(totpPage.getTotpSecret())); + String totpSecret = totpPage.getTotpSecret(); + + totpPage.configure(totp.generate(totpSecret)); + + Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + + oauth.openLogout(); + + loginPage.open(); + loginPage.loginTotp("test-user@localhost", "password", totp.generate(totpSecret)); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); } + @Test + public void setupTotpRegisteredAfterTotpRemoval() { + // Register new user + loginPage.open(); + loginPage.clickRegister(); + registerPage.register("firstName2", "lastName2", "email2", "setupTotp2", "password2", "password2"); + + // Configure totp + totpPage.assertCurrent(); + + String totpCode = totpPage.getTotpSecret(); + totpPage.configure(totp.generate(totpCode)); + + // After totp config, user should be on the app page + Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + + // Logout + oauth.openLogout(); + + // Try to login after logout + loginPage.open(); + loginPage.login("setupTotp2", "password2"); + + // Totp is already configured, thus one-time password is needed, login page should be loaded + Assert.assertTrue(loginPage.isCurrent()); + Assert.assertFalse(totpPage.isCurrent()); + + // Login with one-time password + loginPage.loginTotp("setupTotp2", "password2", totp.generate(totpCode)); + + // Open account page + accountTotpPage.open(); + accountTotpPage.assertCurrent(); + + // Remove google authentificator + accountTotpPage.removeTotp(); + + // Logout + oauth.openLogout(); + + // Try to login + loginPage.open(); + loginPage.login("setupTotp2", "password2"); + + // Since the authentificator was removed, it has to be set up again + totpPage.assertCurrent(); + totpPage.configure(totp.generate(totpPage.getTotpSecret())); + + Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + + } } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java index 32cfb806899..11cc502362d 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountTotpPage.java @@ -41,6 +41,9 @@ public class AccountTotpPage extends Page { @FindBy(css = "button[type=\"submit\"]") private WebElement submitButton; + @FindBy(linkText = "Remove Google") + private WebElement removeLink; + public void configure(String totp) { totpInput.sendKeys(totp); submitButton.click(); @@ -51,11 +54,15 @@ public class AccountTotpPage extends Page { } public boolean isCurrent() { - return driver.getPageSource().contains("Google Authenticator Setup"); + return driver.getTitle().contains("Edit Account - Google Authenticator"); } public void open() { driver.navigate().to(PATH); } + public void removeTotp() { + removeLink.click(); + } + } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java index 1d9c94998d4..d0996907636 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java @@ -41,6 +41,9 @@ public class LoginPage extends Page { @FindBy(id = "password") private WebElement passwordInput; + @FindBy(id = "totp") + private WebElement totp; + @FindBy(css = "input[type=\"submit\"]") private WebElement submitButton; @@ -63,6 +66,19 @@ public class LoginPage extends Page { submitButton.click(); } + public void loginTotp(String username, String password, String code) { + usernameInput.clear(); + usernameInput.sendKeys(username); + + passwordInput.clear(); + passwordInput.sendKeys(password); + + totp.clear(); + totp.sendKeys(code); + + submitButton.click(); + } + public String getError() { return loginErrorMessage != null ? loginErrorMessage.getText() : null; } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java index cb56000a99a..d38e2acfc14 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/Page.java @@ -35,7 +35,7 @@ public abstract class Page { public void assertCurrent() { String name = getClass().getSimpleName(); - Assert.assertTrue("Exptected " + name + " but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + ")", + Assert.assertTrue("Expected " + name + " but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + ")", isCurrent()); }