nasty merge

This commit is contained in:
Bill Burke 2013-11-05 23:12:07 -05:00
commit 2861ea0e96
97 changed files with 2133 additions and 1870 deletions

View file

@ -43,7 +43,34 @@ body {
background-image: url(img/feedback-info-sign.png); background-image: url(img/feedback-info-sign.png);
background-color: #e4f3fa; background-color: #e4f3fa;
} }
#idletimeout {
background: #CC5100;
color: #FFFFFF;
font-size: 1.1em;
padding: 0.90909090909091em;
text-align: center;
display: none;
}
#idletimeout a {
color: #fff;
font-weight: bold;
}
.loading span {
background: url(img/loader.gif) no-repeat center top;
position: fixed;
z-index: 1000;
top: 50%;
left: 50%;
margin-top: -2.27272727272727em;
margin-left: -2.27272727272727em;
padding-top: 2.90909090909091em;
font-size: 1.1em;
color: #666;
}
/* Header */ /* Header */
.header.rcue {
z-index: 50;
}
.header.rcue .navbar.utility { .header.rcue .navbar.utility {
background-color: #393F45; background-color: #393F45;
border-bottom: 1px solid #53565B; border-bottom: 1px solid #53565B;
@ -65,36 +92,6 @@ body {
min-height: 42px; min-height: 42px;
max-width: 1170px; max-width: 1170px;
} }
.header.rcue .navbar.primary .select-rcue {
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
display: inline-block;
vertical-align: middle;
background-color: #555a5e;
background-image: none;
background-image: url(img/sprite-arrow-down.png);
background-repeat: no-repeat;
background-position: right -26px;
border: 1px solid #676c6e;
border-radius: 2px;
padding-left: 0;
}
.header.rcue .navbar.primary .select-rcue:hover {
border-color: #7e8385;
}
.header.rcue .navbar.primary .select-rcue select {
color: #fff;
}
.header.rcue .navbar.primary .select-rcue select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #fff;
}
.header.rcue .navbar.primary .select-rcue select option {
background-color: #fff;
color: #333;
padding: 0.36363636363636em 0.90909090909091em;
}
.header.rcue .navbar.primary .nav > li { .header.rcue .navbar.primary .nav > li {
/* /*
.dropdown { .dropdown {
@ -215,7 +212,6 @@ body {
.header.rcue .navbar.primary .nav > li .select-rcue select option { .header.rcue .navbar.primary .nav > li .select-rcue select option {
background-color: #fff; background-color: #fff;
color: #333; color: #333;
padding: 0.36363636363636em 0.90909090909091em;
} }
.header.rcue .navbar.primary .nav > li a#refresh { .header.rcue .navbar.primary .nav > li a#refresh {
border: none; border: none;
@ -469,6 +465,42 @@ table + .feedback.inline.warning {
td.token-cell button { td.token-cell button {
margin-top: -1px; margin-top: -1px;
} }
/* Modal boxes */
.modal .modal-dialog {
padding-top: 10em;
}
.modal .modal-content {
box-shadow: none;
border-radius: 0;
border: 1px solid #666;
}
.modal .modal-header {
background-color: #f8f8f8;
padding: 1.5em 2em;
border-bottom: none;
}
.modal .modal-header h3 {
font-size: 1.3em;
font-weight: bold;
font-family: "Open Sans", sans-serif;
margin: 0;
}
.modal .modal-body p {
font-size: 1.1em;
}
.modal .modal-body p.primary {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 0.45454545454545em;
}
.modal .modal-footer {
border-top: 0;
}
.modal .modal-footer .primary,
.modal .modal-footer .destructive {
float: right;
margin-left: 0.90909090909091em;
}
/* Page: User Account */ /* Page: User Account */
.user form fieldset div:first-child { .user form fieldset div:first-child {
margin-top: 1em; margin-top: 1em;

View file

@ -60,11 +60,43 @@ body {
} }
} }
#idletimeout {
background: #CC5100;
color: #FFFFFF;
font-size: 1.1em;
padding: 0.90909090909091em;
text-align: center;
display: none;
a {
color: #fff;
font-weight: bold;
}
}
.loading {
span {
background: url(img/loader.gif) no-repeat center top;
position: fixed;
z-index: 1000;
top: 50%;
left: 50%;
margin-top: -2.27272727272727em;
margin-left: -2.27272727272727em;
padding-top: 2.90909090909091em;
font-size: 1.1em;
color: #666;
}
}
/* Header */ /* Header */
.header.rcue { .header.rcue {
z-index: 50;
.navbar.utility { .navbar.utility {
background-color: #393F45; background-color: #393F45;
border-bottom: 1px solid #53565B; border-bottom: 1px solid #53565B;
@ -88,41 +120,6 @@ body {
min-height: 42px; min-height: 42px;
max-width: 1170px; max-width: 1170px;
} }
.select-rcue {
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
display: inline-block;
vertical-align: middle;
background-color: #555a5e;
background-image: none;
background-image: url(img/sprite-arrow-down.png);
background-repeat: no-repeat;
background-position: right -26px;
border: 1px solid #676c6e;
border-radius: 2px;
padding-left: 0;
&:hover {
border-color: #7e8385;
}
select {
color: #fff;
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #fff;
}
option {
background-color: #fff;
color: #333;
padding: 0.36363636363636em 0.90909090909091em;
}
}
}
.nav > li { .nav > li {
@ -165,7 +162,6 @@ body {
option { option {
background-color: #fff; background-color: #fff;
color: #333; color: #333;
padding: 0.36363636363636em 0.90909090909091em;
} }
} }
} }
@ -567,6 +563,59 @@ td.token-cell button {
} }
/* Modal boxes */
.modal {
.modal-dialog {
padding-top: 10em;
}
.modal-content {
box-shadow: none;
border-radius: 0;
border: 1px solid #666;
}
.modal-header {
background-color: #f8f8f8;
padding: 1.5em 2em;
border-bottom: none;
h3 {
font-size: 1.3em;
font-weight: bold;
font-family: @open-sans;
margin: 0;
}
}
.modal-body {
p {
font-size: 1.1em;
&.primary {
font-size: 1.1em;
font-weight: bold;
margin-bottom: 0.45454545454545em;
}
}
}
.modal-footer {
border-top: 0;
.primary,
.destructive {
float: right;
margin-left: 0.90909090909091em;
}
}
}
/* Page: User Account */ /* Page: User Account */
.user { .user {

View file

@ -16,12 +16,14 @@ input[type="email"],
textarea { textarea {
font-size: 1.1em; font-size: 1.1em;
padding: 0 0.545454545454545em; padding: 0 0.545454545454545em;
min-width: 18.1818181818182em;
height: 2.36363636363636em; height: 2.36363636363636em;
/* 26px */
border: 1px #b6b6b6 solid; border: 1px #b6b6b6 solid;
border-radius: 2px; border-radius: 2px;
box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.1);
color: #333; color: #333;
width: 18.1818em;
} }
input[type="text"]:hover, input[type="text"]:hover,
input[type="password"]:hover, input[type="password"]:hover,
@ -51,6 +53,36 @@ input[type="email"].error:focus,
textarea.error:focus { textarea.error:focus {
box-shadow: 0 0 5px #ba1212; box-shadow: 0 0 5px #ba1212;
} }
input[type="text"].tiny,
input[type="password"].tiny,
input[type="email"].tiny,
textarea.tiny {
width: 4.54545454545455em;
}
input[type="text"].small,
input[type="password"].small,
input[type="email"].small,
textarea.small {
width: 9.09090909090909em;
}
input[type="text"].medium,
input[type="password"].medium,
input[type="email"].medium,
textarea.medium {
width: 18.1818em;
}
input[type="text"].large,
input[type="password"].large,
input[type="email"].large,
textarea.large {
width: 27.2727272727273em;
}
input[type="text"].xlarge,
input[type="password"].xlarge,
input[type="email"].xlarge,
textarea.xlarge {
width: 36.3636363636364em;
}
textarea { textarea {
padding: 0.45em 0.545454545454545em; padding: 0.45em 0.545454545454545em;
height: auto; height: auto;
@ -252,7 +284,7 @@ button.primary:focus,
.search-comp .icon-search { .search-comp .icon-search {
position: absolute; position: absolute;
right: 0.2em; right: 0.2em;
top: 0.4em; top: 0.6em;
opacity: 0.5; opacity: 0.5;
filter: alpha(opacity=50); filter: alpha(opacity=50);
} }
@ -457,6 +489,8 @@ fieldset.border-top {
.form-group .required { .form-group .required {
font-size: 1.1em; font-size: 1.1em;
color: #CB2915; color: #CB2915;
display: inline-block;
margin-top: -0.09090909090909em;
} }
legend + .form-group { legend + .form-group {
padding-top: 1em; padding-top: 1em;
@ -550,7 +584,7 @@ input[type="email"].tiny {
} }
.select-rcue, .select-rcue,
.select2-container .select2-choice { .select2-container .select2-choice {
height: 26px; height: 2.6em;
border: 1px #b6b6b6 solid; border: 1px #b6b6b6 solid;
border-radius: 2px; border-radius: 2px;
box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.1);
@ -593,7 +627,7 @@ input[type="email"].tiny {
} }
.select-rcue option { .select-rcue option {
line-height: 2em; line-height: 2em;
padding-left: 0.90909090909091em; padding: 0.363636em 0.909091em;
} }
.select-rcue option:hover { .select-rcue option:hover {
background-color: #d5ecf9; background-color: #d5ecf9;
@ -788,7 +822,8 @@ input[type="email"].tiny {
margin-top: 3em; margin-top: 3em;
margin-bottom: 5em; margin-bottom: 5em;
} }
.form-actions .primary { .form-actions .primary,
.form-actions .destructive {
float: right; float: right;
margin-left: 0.90909090909091em; margin-left: 0.90909090909091em;
} }

View file

@ -23,12 +23,12 @@ input[type="email"],
textarea { textarea {
font-size: 1.1em; font-size: 1.1em;
padding: 0 0.545454545454545em; padding: 0 0.545454545454545em;
min-width: 18.1818181818182em; height: 2.36363636363636em; /* 26px */
height: 2.36363636363636em;
border: 1px #b6b6b6 solid; border: 1px #b6b6b6 solid;
border-radius: 2px; border-radius: 2px;
box-shadow: inset 0px 2px 2px rgba(0,0,0,0.1); box-shadow: inset 0px 2px 2px rgba(0,0,0,0.1);
color: #333; color: #333;
width: 18.1818em;
&:hover { &:hover {
border-color: #62afdb; border-color: #62afdb;
@ -49,6 +49,26 @@ textarea {
box-shadow: 0 0 5px #ba1212; box-shadow: 0 0 5px #ba1212;
} }
} }
&.tiny {
width: 4.54545454545455em;
}
&.small {
width: 9.09090909090909em;
}
&.medium {
width: 18.1818em;
}
&.large {
width: 27.2727272727273em;
}
&.xlarge {
width: 36.3636363636364em
}
} }
textarea { textarea {
@ -280,7 +300,7 @@ button.primary,
.icon-search { .icon-search {
position: absolute; position: absolute;
right: 0.2em; right: 0.2em;
top: 0.4em; top: 0.6em;
opacity: 0.5; opacity: 0.5;
filter: alpha(opacity=50); filter: alpha(opacity=50);
@ -532,6 +552,8 @@ fieldset.border-top {
.required { .required {
font-size: 1.1em; font-size: 1.1em;
color: #CB2915; color: #CB2915;
display: inline-block;
margin-top: -0.09090909090909em;
} }
} }
@ -656,7 +678,7 @@ input[type="email"] {
.select-rcue, .select-rcue,
.select2-container .select2-choice { .select2-container .select2-choice {
height: 26px; height: 2.6em;
border: 1px #b6b6b6 solid; border: 1px #b6b6b6 solid;
border-radius: 2px; border-radius: 2px;
box-shadow: inset 0px 2px 2px rgba(0,0,0,0.1); box-shadow: inset 0px 2px 2px rgba(0,0,0,0.1);
@ -707,7 +729,7 @@ input[type="email"] {
option { option {
line-height: 2em; line-height: 2em;
padding-left: 0.90909090909091em; padding: 0.363636em 0.909091em;
&:hover { &:hover {
background-color: #d5ecf9; background-color: #d5ecf9;
@ -940,7 +962,8 @@ input[type="email"] {
margin-top: 3em; margin-top: 3em;
margin-bottom: 5em; margin-bottom: 5em;
.primary { .primary,
.destructive {
float: right; float: right;
margin-left: 0.90909090909091em; margin-left: 0.90909090909091em;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="12px" height="400px" viewBox="0 0 12 400" enable-background="new 0 0 12 400" xml:space="preserve">
<rect x="6.001" opacity="0.07" fill="#FFFFFF" enable-background="new " width="0.997" height="190"/>
<rect x="6" y="209" opacity="0.07" fill="#FFFFFF" enable-background="new " width="1" height="191"/>
<g opacity="0.15">
<path fill="#FFFFFF" d="M6.501,200.066c0,1.047-0.264,1.864-0.791,2.452S4.454,203.4,3.524,203.4c-0.574,0-1.084-0.135-1.529-0.404
c-0.445-0.27-0.789-0.656-1.031-1.16c-0.242-0.504-0.363-1.094-0.363-1.77c0-1.047,0.262-1.862,0.785-2.446
c0.523-0.584,1.25-0.876,2.18-0.876c0.898,0,1.612,0.299,2.142,0.896C6.238,198.237,6.501,199.047,6.501,200.066z M1.608,200.066
c0,0.821,0.164,1.446,0.492,1.875s0.811,0.645,1.447,0.645c0.636,0,1.12-0.214,1.45-0.643c0.33-0.428,0.495-1.053,0.495-1.877
c0-0.816-0.165-1.437-0.495-1.86s-0.817-0.636-1.462-0.636c-0.637,0-1.117,0.209-1.441,0.627
C1.77,198.615,1.608,199.238,1.608,200.066z"/>
<path fill="#FFFFFF" d="M11.136,196.744c0.285,0,0.541,0.023,0.768,0.07l-0.135,0.902c-0.266-0.059-0.5-0.088-0.703-0.088
c-0.52,0-0.964,0.211-1.333,0.633c-0.369,0.422-0.554,0.947-0.554,1.576v3.445H8.206v-6.422h0.803l0.111,1.189h0.047
c0.238-0.418,0.525-0.74,0.861-0.967C10.364,196.855,10.733,196.744,11.136,196.744z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,284 +0,0 @@
body {
font-size: 62.5%;
min-height: 60em;
min-width: 120em;
}
.rcue-login-register {
background-color: #1D2226;
background-image: url("img/login-screen-background.jpg");
background-position: top left;
background-size: auto;
background-repeat: no-repeat;
color: #fff;
/*
.form-area {
background-image: url(img/login-register-separator.svg);
background-repeat: no-repeat;
background-position: 40.2em center;
}
*/
/* Login area */
/* Social login area */
/* Info area */
}
.rcue-login-register h1 a {
position: absolute;
top: 5em;
right: 6.4em;
}
.rcue-login-register .content {
position: absolute;
bottom: 10%;
width: 100%;
min-width: 76em;
}
.rcue-login-register h2 {
padding-left: 4.34782608695652em;
font-family: "Overpass", sans-serif;
font-size: 2.3em;
font-weight: 100;
text-transform: uppercase;
letter-spacing: 0.005em;
}
.rcue-login-register h2 strong {
font-weight: bold;
}
.rcue-login-register .background-area {
border-top: 0.1em rgba(255, 255, 255, 0.05) solid;
border-bottom: 0.1em rgba(255, 255, 255, 0.05) solid;
background-color: rgba(0, 0, 0, 0.3);
padding: 3em 0 3em 10em;
margin-top: 2.7em;
width: 100%;
min-width: 120em;
}
.rcue-login-register .background-area section {
float: left;
padding: 1.5em 4.5em 1.5em 4.6em;
width: auto;
position: relative;
}
.rcue-login-register .background-area section h3 {
display: none;
}
.rcue-login-register .background-area section:first-child {
padding-right: 4.5em;
}
.rcue-login-register .form-area.social {
background-image: url(img/login-register-social-separators.svg);
background-position: 39.6em center;
}
.rcue-login-register section.app-form {
padding-left: 0;
position: relative;
}
.rcue-login-register form > div {
margin-bottom: 1em;
}
.rcue-login-register label,
.rcue-login-register .social-login > p {
display: inline-block;
font-size: 1.4em;
font-weight: 400;
}
.rcue-login-register label {
width: 6.07142857142857em;
/* 85px */
}
.rcue-login-register label.two-lines {
float: left;
margin-top: -0.28571428571429em;
/* -4px */
line-height: 1.1em;
}
.rcue-login-register input[type="text"],
.rcue-login-register input[type="password"] {
width: 24.7272727272727em;
/* 272px */
}
.rcue-login-register form > div.aside-btn {
float: left;
font-size: 1.1em;
margin-left: 7.72727272727273em;
/* 85px */
margin-top: 0.90909090909091em;
/* 10px */
margin-bottom: 0;
}
.rcue-login-register form > div.aside-btn label {
font-size: 1em;
width: auto;
}
.rcue-login-register form > div.aside-btn input[type="checkbox"] {
margin-bottom: 0.54545454545455em;
/* 6px */
}
.rcue-login-register form > input[type="button"] {
float: right;
margin-top: 0.76923076923077em;
/* 10px */
}
.rcue-login-register p.subtitle {
font-size: 1.1em;
color: #999;
position: absolute;
right: 4.09090909090909em;
top: -0.636363636363636em;
}
.rcue-login-register .feedback {
left: 32.7em;
top: -9.2em;
min-width: 35em;
}
.rcue-login-register.reset .feedback {
left: 35.7em;
}
.rcue-login-register section.social-login > span {
display: none;
}
.rcue-login-register section.social-login > p {
float: left;
margin-top: 0.28571428571429em;
/* 14px */
width: 6.78571428571429em;
/* 95px */
}
.rcue-login-register section.social-login > ul {
float: left;
}
.rcue-login-register section.social-login li {
margin-bottom: 2em;
}
.rcue-login-register section.social-login li:last-child {
margin-bottom: 0;
}
.rcue-login-register section.info-area {
padding-right: 0;
}
.rcue-login-register section.info-area p,
.rcue-login-register section.info-area li {
font-size: 1.4em;
margin-bottom: 1.64285714285714em;
/* 23px */
}
.rcue-login-register section.info-area li {
color: #999;
margin-bottom: 1em;
}
.rcue-login-register section.info-area li:last-child {
margin-bottom: 0;
}
@media screen and (min-width: 1280px) {
.rcue-login-register {
background-size: 100% auto;
}
}
/* Social buttons */
.zocial,
a.zocial {
padding: 0;
line-height: 2.3em;
height: 2.3em;
width: 131px;
border-radius: 2px;
box-shadow: none;
background-image: none;
text-shadow: none;
}
.zocial .text,
a.zocial .text {
font-size: 1.2em;
line-height: 1.25em;
text-align: center;
display: block;
font-family: "Open Sans", sans-serif;
font-weight: normal;
border-left: 1px solid rgba(0, 0, 0, 0.15);
margin-left: 3em;
/* 36 px */
margin-top: 0.25em;
/* 3px */
}
.zocial:hover,
a.zocial:hover,
.zocial:active,
a.zocial:active,
.zocial:focus,
a.zocial:focus {
text-decoration: none;
background-image: none;
}
.zocial:hover,
a.zocial:hover {
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
}
.zocial:before,
a.zocial:before {
margin: 0;
padding: 0;
box-shadow: none;
border: none;
width: 3em;
/* 36px */
}
.zocial.facebook:before {
width: 2.66666666666667em;
/* 32px */
}
/* Register page */
.rcue-login-register.register label {
width: 7.5em;
/* 105px */
}
.rcue-login-register.register input[type="text"],
.rcue-login-register.register input[type="email"],
.rcue-login-register.register input[type="password"] {
width: 22.9090909090909em;
/* 252px */
}
.rcue-login-register.register form > div.aside-btn {
margin-left: 9.54545454545454em;
/* 105px */
width: 12.5454545454546em;
/* 138px */
}
.rcue-login-register.register form > div.aside-btn p {
line-height: 1.3em;
}
/* Customer login */
.rcue-login-register.customer {
background-image: url("img/customer-login-screen-bg2.jpg");
}
.rcue-login-register.customer p.powered {
font-size: 1.1em;
margin-top: 1.27272727272727em;
text-align: right;
margin-right: 5.81818181818182em;
}
.rcue-login-register.customer p.powered a {
color: #666;
}
.rcue-login-register.customer p.powered a:hover {
color: #0099D3;
}

View file

@ -1,322 +0,0 @@
body {
font-size: 62.5%;
min-height: 60em;
min-width: 120em;
}
.rcue-login-register {
background-color: #1D2226;
background-image: url("img/login-screen-background.jpg");
background-position: top left;
background-size: auto;
background-repeat: no-repeat;
color: #fff;
h1 a {
position: absolute;
top: 5em;
right: 6.4em;
}
.content {
position: absolute;
bottom: 10%;
width: 100%;
min-width: 76em;
}
h2 {
padding-left: 4.34782608695652em;
font-family: "Overpass", sans-serif;
font-size: 2.3em;
font-weight: 100;
text-transform: uppercase;
letter-spacing: 0.005em;
strong {
font-weight: bold;
}
}
.background-area {
border-top: 0.1em rgba(255, 255, 255, 0.05) solid;
border-bottom: 0.1em rgba(255, 255, 255, 0.05) solid;
background-color: rgba(0, 0, 0, 0.3);
padding: 3em 0 3em 10em;
margin-top: 2.7em;
width: 100%;
min-width: 120em;
section {
float: left;
padding: 1.5em 4.5em 1.5em 4.6em;
width: auto;
position: relative;
h3 {
display: none;
}
&:first-child {
padding-right: 4.5em;
}
}
}
/*
.form-area {
background-image: url(img/login-register-separator.svg);
background-repeat: no-repeat;
background-position: 40.2em center;
}
*/
.form-area.social {
background-image: url(img/login-register-social-separators.svg);
background-position: 39.6em center;
}
/* Login area */
section.app-form {
padding-left: 0;
position: relative;
}
form > div {
margin-bottom: 1em;
}
label,
.social-login > p {
display: inline-block;
font-size: 1.4em;
font-weight: 400;
}
label {
width: 6.07142857142857em; /* 85px */
}
label.two-lines {
float: left;
margin-top: -0.28571428571429em; /* -4px */
line-height: 1.1em;
}
input[type="text"],
input[type="password"] {
width: 24.7272727272727em; /* 272px */
}
form > div.aside-btn {
float: left;
font-size: 1.1em;
margin-left: 7.72727272727273em; /* 85px */
margin-top: 0.90909090909091em; /* 10px */
margin-bottom: 0;
label {
font-size: 1em;
width: auto;
}
input[type="checkbox"] {
margin-bottom: 0.54545454545455em; /* 6px */
}
}
form > input[type="button"] {
float: right;
margin-top: 0.76923076923077em; /* 10px */
}
p.subtitle {
font-size: 1.1em;
color: #999;
position: absolute;
right: 4.09090909090909em;
top: -0.636363636363636em;
}
.feedback {
left: 32.7em;
top: -9.2em;
min-width: 35em;
}
&.reset .feedback {
left: 35.7em;
}
/* Social login area */
section.social-login {
> span {
display: none;
}
> p {
float: left;
margin-top: 0.28571428571429em; /* 14px */
width: 6.78571428571429em; /* 95px */
}
> ul {
float: left;
}
li {
margin-bottom: 2em;
&:last-child {
margin-bottom: 0;
}
}
}
/* Info area */
section.info-area {
padding-right: 0;
p,
li {
font-size: 1.4em;
margin-bottom: 1.64285714285714em; /* 23px */
}
li {
color: #999;
margin-bottom: 1em;
}
li:last-child {
margin-bottom: 0;
}
}
}
@media screen and (min-width: 1280px) {
.rcue-login-register {
background-size: 100% auto;
}
}
/* Social buttons */
.zocial,
a.zocial {
padding: 0;
line-height: 2.3em;
height: 2.3em;
width: 131px;
border-radius: 2px;
box-shadow: none;
background-image: none;
text-shadow: none;
.text {
font-size: 1.2em;
line-height: 1.25em;
text-align: center;
display: block;
font-family: "Open Sans", sans-serif;
font-weight: normal;
border-left: 1px solid rgba(0, 0, 0, 0.15);
margin-left: 3em; /* 36 px */
margin-top: 0.25em; /* 3px */
}
&:hover,
&:active,
&:focus {
text-decoration: none;
background-image: none;
}
&:hover {
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
}
&:before {
margin: 0;
padding: 0;
box-shadow: none;
border: none;
width: 3em; /* 36px */
}
}
.zocial.facebook:before {
width: 2.66666666666667em; /* 32px */
}
/* Register page */
.rcue-login-register.register {
label {
width: 7.5em; /* 105px */
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 22.9090909090909em; /* 252px */
}
form > div.aside-btn {
margin-left: 9.54545454545454em; /* 105px */
width: 12.5454545454546em; /* 138px */
p {
line-height: 1.3em;
}
}
}
/* Customer login */
.rcue-login-register.customer {
background-image: url("img/customer-login-screen-bg2.jpg");
p.powered {
font-size: 1.1em;
margin-top: 1.27272727272727em;
text-align: right;
margin-right: 5.81818181818182em;
a {
color: #666;
&:hover {
color: #0099D3;
}
}
}
}

View file

@ -5,12 +5,6 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>Keycloak</title> <title>Keycloak</title>
<style type="text/css">
#idletimeout { background:#CC5100; border:3px solid #FF6500; color:#fff; font-family:arial, sans-serif; text-align:center; font-size:12px; padding:10px; position:relative; top:0px; left:0; right:0; z-index:100000; display:none; }
#idletimeout a { color:#fff; font-weight:bold }
#idletimeout span { font-weight:bold }
</style>
<link rel="icon" href="/auth-server/admin-ui/img/favicon.ico"> <link rel="icon" href="/auth-server/admin-ui/img/favicon.ico">
<!-- Frameworks --> <!-- Frameworks -->
@ -59,7 +53,7 @@
<body class="admin-console" data-ng-controller="GlobalCtrl"> <body class="admin-console" data-ng-controller="GlobalCtrl">
<div id="idletimeout"> <div id="idletimeout">
You will be logged off in <span></span>&nbsp;seconds due to inactivity. You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
<a id="idletimeout-resume" href="#">Click here to continue using this web page</a>. <a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
</div> </div>

View file

@ -4,29 +4,31 @@ var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders
var resourceRequests = 0; var resourceRequests = 0;
module.config([ '$routeProvider', function($routeProvider) { module.config([ '$routeProvider', function($routeProvider) {
$routeProvider
.when('/create/realm', { $routeProvider
templateUrl : 'partials/realm-detail.html', .when('/create/realm', {
resolve : { templateUrl : 'partials/realm-detail.html',
realm : function(RealmLoader) { resolve : {
return {}; realm : function(RealmLoader) {
} return {};
}, }
controller : 'RealmDetailCtrl' },
}).when('/realms/:realm', { controller : 'RealmDetailCtrl'
templateUrl : 'partials/realm-detail.html', })
resolve : { .when('/realms/:realm', {
realm : function(RealmLoader) { templateUrl : 'partials/realm-detail.html',
return RealmLoader(); resolve : {
} realm : function(RealmLoader) {
}, return RealmLoader();
controller : 'RealmDetailCtrl' }
}).when('/realms', { },
templateUrl : 'partials/realm-list.html', controller : 'RealmDetailCtrl'
controller : 'RealmListCtrl' })
}).when('/realms/:realm/token-settings', { .when('/realms', {
templateUrl : 'partials/realm-list.html',
controller : 'RealmListCtrl'
})
.when('/realms/:realm/token-settings', {
templateUrl : 'partials/realm-tokens.html', templateUrl : 'partials/realm-tokens.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -34,7 +36,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'RealmTokenDetailCtrl' controller : 'RealmTokenDetailCtrl'
}).when('/realms/:realm/social-settings', { })
.when('/realms/:realm/social-settings', {
templateUrl : 'partials/realm-social.html', templateUrl : 'partials/realm-social.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -52,18 +55,28 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'RealmRequiredCredentialsCtrl' controller : 'RealmRequiredCredentialsCtrl'
}) })
.when('/create/user/:realm', { .when('/realms/:realm/smtp-settings', {
templateUrl : 'partials/user-detail.html', templateUrl : 'partials/realm-smtp.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
return RealmLoader(); return RealmLoader();
}, }
user : function() { },
return {}; controller : 'RealmSMTPSettingsCtrl'
} })
}, .when('/create/user/:realm', {
controller : 'UserDetailCtrl' templateUrl : 'partials/user-detail.html',
}).when('/realms/:realm/users/:user', { resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
user : function() {
return {};
}
},
controller : 'UserDetailCtrl'
})
.when('/realms/:realm/users/:user', {
templateUrl : 'partials/user-detail.html', templateUrl : 'partials/user-detail.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -74,7 +87,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'UserDetailCtrl' controller : 'UserDetailCtrl'
}).when('/realms/:realm/users/:user/role-mappings', { })
.when('/realms/:realm/users/:user/role-mappings', {
templateUrl : 'partials/role-mappings.html', templateUrl : 'partials/role-mappings.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -91,15 +105,16 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'UserRoleMappingCtrl' controller : 'UserRoleMappingCtrl'
}).when('/realms/:realm/users', { })
templateUrl : 'partials/user-list.html', .when('/realms/:realm/users', {
resolve : { templateUrl : 'partials/user-list.html',
realm : function(RealmLoader) { resolve : {
return RealmLoader(); realm : function(RealmLoader) {
} return RealmLoader();
}, }
controller : 'UserListCtrl' },
}) controller : 'UserListCtrl'
})
.when('/create/role/:realm', { .when('/create/role/:realm', {
templateUrl : 'partials/role-detail.html', templateUrl : 'partials/role-detail.html',
@ -112,7 +127,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'RoleDetailCtrl' controller : 'RoleDetailCtrl'
}).when('/realms/:realm/roles/:role', { })
.when('/realms/:realm/roles/:role', {
templateUrl : 'partials/role-detail.html', templateUrl : 'partials/role-detail.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -123,7 +139,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'RoleDetailCtrl' controller : 'RoleDetailCtrl'
}).when('/realms/:realm/roles', { })
.when('/realms/:realm/roles', {
templateUrl : 'partials/role-list.html', templateUrl : 'partials/role-list.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -150,7 +167,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'ApplicationRoleDetailCtrl' controller : 'ApplicationRoleDetailCtrl'
}).when('/realms/:realm/applications/:application/roles/:role', { })
.when('/realms/:realm/applications/:application/roles/:role', {
templateUrl : 'partials/application-role-detail.html', templateUrl : 'partials/application-role-detail.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -164,7 +182,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'ApplicationRoleDetailCtrl' controller : 'ApplicationRoleDetailCtrl'
}).when('/realms/:realm/applications/:application/credentials', { })
.when('/realms/:realm/applications/:application/credentials', {
templateUrl : 'partials/application-credentials.html', templateUrl : 'partials/application-credentials.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -175,7 +194,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'ApplicationCredentialsCtrl' controller : 'ApplicationCredentialsCtrl'
}).when('/realms/:realm/applications/:application/roles', { })
.when('/realms/:realm/applications/:application/roles', {
templateUrl : 'partials/application-role-list.html', templateUrl : 'partials/application-role-list.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -189,7 +209,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'ApplicationRoleListCtrl' controller : 'ApplicationRoleListCtrl'
}).when('/realms/:realm/applications/:application/scope-mappings', { })
.when('/realms/:realm/applications/:application/scope-mappings', {
templateUrl : 'partials/application-scope-mappings.html', templateUrl : 'partials/application-scope-mappings.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -208,8 +229,6 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'ApplicationScopeMappingCtrl' controller : 'ApplicationScopeMappingCtrl'
}) })
.when('/create/application/:realm', { .when('/create/application/:realm', {
templateUrl : 'partials/application-detail.html', templateUrl : 'partials/application-detail.html',
resolve : { resolve : {
@ -224,7 +243,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'ApplicationDetailCtrl' controller : 'ApplicationDetailCtrl'
}).when('/realms/:realm/applications/:application', { })
.when('/realms/:realm/applications/:application', {
templateUrl : 'partials/application-detail.html', templateUrl : 'partials/application-detail.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -238,7 +258,8 @@ module.config([ '$routeProvider', function($routeProvider) {
} }
}, },
controller : 'ApplicationDetailCtrl' controller : 'ApplicationDetailCtrl'
}).when('/realms/:realm/applications', { })
.when('/realms/:realm/applications', {
templateUrl : 'partials/application-list.html', templateUrl : 'partials/application-list.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -250,33 +271,33 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'ApplicationListCtrl' controller : 'ApplicationListCtrl'
}) })
.otherwise({ .otherwise({
templateUrl : 'partials/home.html' templateUrl : 'partials/home.html'
}); });
} ]); } ]);
module.config(function($httpProvider) { module.config(function($httpProvider) {
$httpProvider.responseInterceptors.push('errorInterceptor'); $httpProvider.responseInterceptors.push('errorInterceptor');
var spinnerFunction = function(data, headersGetter) { var spinnerFunction = function(data, headersGetter) {
if (resourceRequests == 0) { if (resourceRequests == 0) {
$('#loading').show(); $('#loading').show();
} }
resourceRequests++; resourceRequests++;
return data; return data;
}; };
$httpProvider.defaults.transformRequest.push(spinnerFunction); $httpProvider.defaults.transformRequest.push(spinnerFunction);
$httpProvider.responseInterceptors.push('spinnerInterceptor'); $httpProvider.responseInterceptors.push('spinnerInterceptor');
}); });
module.factory('errorInterceptor', function($q, $window, $rootScope, $location, Auth) { module.factory('errorInterceptor', function($q, $window, $rootScope, $location, Auth) {
return function(promise) { return function(promise) {
return promise.then(function(response) { return promise.then(function(response) {
$rootScope.httpProviderError = null; $rootScope.httpProviderError = null;
return response; return response;
}, function(response) { }, function(response) {
if (response.status == 401) { if (response.status == 401) {
console.log('session timeout?'); console.log('session timeout?');
Auth.loggedIn = false; Auth.loggedIn = false;
@ -284,28 +305,28 @@ module.factory('errorInterceptor', function($q, $window, $rootScope, $location,
} else { } else {
$rootScope.httpProviderError = response.status; $rootScope.httpProviderError = response.status;
} }
return $q.reject(response); return $q.reject(response);
}); });
}; };
}); });
module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) { module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
return function(promise) { return function(promise) {
return promise.then(function(response) { return promise.then(function(response) {
resourceRequests--; resourceRequests--;
if (resourceRequests == 0) { if (resourceRequests == 0) {
$('#loading').hide(); $('#loading').hide();
} }
return response; return response;
}, function(response) { }, function(response) {
resourceRequests--; resourceRequests--;
if (resourceRequests == 0) { if (resourceRequests == 0) {
$('#loading').hide(); $('#loading').hide();
} }
return $q.reject(response); return $q.reject(response);
}); });
}; };
}); });
// collapsable form fieldsets // collapsable form fieldsets
@ -354,80 +375,80 @@ module.directive('collapsed', function() {
module.directive('kcInput', function() { module.directive('kcInput', function() {
var d = { var d = {
scope : true, scope : true,
replace : false, replace : false,
link : function(scope, element, attrs) { link : function(scope, element, attrs) {
var form = element.children('form'); var form = element.children('form');
var label = element.children('label'); var label = element.children('label');
var input = element.children('input'); var input = element.children('input');
var id = form.attr('name') + '.' + input.attr('name'); var id = form.attr('name') + '.' + input.attr('name');
element.attr('class', 'control-group'); element.attr('class', 'control-group');
label.attr('class', 'control-label'); label.attr('class', 'control-label');
label.attr('for', id); label.attr('for', id);
input.wrap('<div class="controls"/>'); input.wrap('<div class="controls"/>');
input.attr('id', id); input.attr('id', id);
if (!input.attr('placeHolder')) { if (!input.attr('placeHolder')) {
input.attr('placeHolder', label.text()); input.attr('placeHolder', label.text());
} }
if (input.attr('required')) { if (input.attr('required')) {
label.append(' <span class="required">*</span>'); label.append(' <span class="required">*</span>');
} }
} }
}; };
return d; return d;
}); });
module.directive('kcEnter', function() { module.directive('kcEnter', function() {
return function(scope, element, attrs) { return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) { element.bind("keydown keypress", function(event) {
if (event.which === 13) { if (event.which === 13) {
scope.$apply(function() { scope.$apply(function() {
scope.$eval(attrs.kcEnter); scope.$eval(attrs.kcEnter);
}); });
event.preventDefault(); event.preventDefault();
} }
}); });
}; };
}); });
module.filter('remove', function() { module.filter('remove', function() {
return function(input, remove, attribute) { return function(input, remove, attribute) {
if (!input || !remove) { if (!input || !remove) {
return input; return input;
} }
var out = []; var out = [];
for ( var i = 0; i < input.length; i++) { for ( var i = 0; i < input.length; i++) {
var e = input[i]; var e = input[i];
for (var j = 0; j < remove.length; j++) { for (var j = 0; j < remove.length; j++) {
if (attribute) { if (attribute) {
if (remove[j][attribute] == e[attribute]) { if (remove[j][attribute] == e[attribute]) {
e = null; e = null;
break; break;
} }
} else { } else {
if (remove[j] == e) { if (remove[j] == e) {
e = null; e = null;
break; break;
} }
} }
} }
if (e != null) { if (e != null) {
out.push(e); out.push(e);
} }
} }
return out; return out;
}; };
}); });

View file

@ -453,3 +453,38 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
}); });
}; };
}); });
module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, realm, $http, $location, Dialog, Notifications) {
$scope.realm = {
id : realm.id, realm : realm.realm, social : realm.social,
smtpServer: 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() {
if ($scope.realmForm.$valid) {
var realmCopy = angular.copy($scope.realm);
$scope.changed = false;
Realm.update(realmCopy, function () {
$location.url("/realms/" + realm.id + "/smtp-settings");
Notifications.success("Your changes have been saved to the realm.");
});
} else {
$scope.realmForm.showErrors = true;
}
};
$scope.reset = function() {
$scope.realm = angular.copy(oldCopy);
$scope.changed = false;
$scope.realmForm.showErrors = false;
};
});

View file

@ -16,15 +16,16 @@ module.service('Auth', function() {
module.service('Dialog', function($dialog) { module.service('Dialog', function($dialog) {
var dialog = {}; var dialog = {};
dialog.confirmDelete = function(name, type, success) { dialog.confirmDelete = function(name, type, success) {
var title = 'Delete ' + name; var title = 'Delete ' + type.charAt(0).toUpperCase() + type.slice(1);
var msg = 'Are you sure you want to permanently delete this ' + type + '?'; var msg = '<p class="primary">Are you sure you want to permanently delete the ' + type + ' "' + name + '"?</p>' +
'<p>This action can\'t be undone.</p>';
var btns = [ { var btns = [ {
result : 'cancel', result : 'cancel',
label : 'Cancel' label : 'Cancel'
}, { }, {
result : 'ok', result : 'ok',
label : 'Delete this ' + type, label : 'Delete',
cssClass : 'btn-primary' cssClass : 'destructive'
} ]; } ];
$dialog.messageBox(title, msg, btns).open().then(function(result) { $dialog.messageBox(title, msg, btns).open().then(function(result) {

View file

@ -3004,10 +3004,10 @@ angular.module("template/dialog/message.html", []).run(["$templateCache", functi
" <h3>{{ title }}</h3>\n" + " <h3>{{ title }}</h3>\n" +
"</div>\n" + "</div>\n" +
"<div class=\"modal-body\">\n" + "<div class=\"modal-body\">\n" +
" <p>{{ message }}</p>\n" + " <p ng-bind-html-unsafe=\"message\"></p>\n" +
"</div>\n" + "</div>\n" +
"<div class=\"modal-footer\">\n" + "<div class=\"modal-footer\">\n" +
" <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=\"btn\" ng-class=\"btn.cssClass\">{{ btn.label }}</button>\n" + " <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=\"\" ng-class=\"btn.cssClass\">{{ btn.label }}</button> \n" +
"</div>\n" + "</div>\n" +
"</div>\n" + "</div>\n" +
"</div>\n" + "</div>\n" +

View file

@ -1,7 +1,7 @@
<div class="header rcue"> <div class="header rcue">
<div class="navbar utility"> <div class="navbar utility">
<div class="navbar-inner clearfix container"> <div class="navbar-inner clearfix container">
<h1><a href="#"><strong>Keycloak</strong> Central Login</a></h1> <h1><a href="#/realms/{{realm.id}}"><strong>Keycloak</strong> Central Login</a></h1>
<ul class="nav pull-right" data-ng-hide="auth.loggedIn"> <ul class="nav pull-right" data-ng-hide="auth.loggedIn">
<li><a href="/auth-server/rest/saas/login">Login</a></li> <li><a href="/auth-server/rest/saas/login">Login</a></li>
<li><a href="/auth-server/rest/saas/registrations">Register</a></li> <li><a href="/auth-server/rest/saas/registrations">Register</a></li>

View file

@ -9,6 +9,7 @@
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li class="active"><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul> </ul>
</div> </div>
<div id="content"> <div id="content">

View file

@ -9,6 +9,7 @@
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul> </ul>
</div> </div>
<div id="content"> <div id="content">
@ -75,6 +76,32 @@
</label> </label>
</div> </div>
</div> </div>
<div class="form-group clearfix block">
<label for="resetPasswordAllowed" class="control-label">Reset password</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.resetPasswordAllowed" class="onoffswitch-checkbox" name="resetPasswordAllowed" id="resetPasswordAllowed">
<label for="resetPasswordAllowed" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<div class="form-group clearfix block">
<label for="verifyEmail" class="control-label">Verify email</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.verifyEmail" class="onoffswitch-checkbox" name="verifyEmail" id="verifyEmail">
<label for="verifyEmail" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="accountManagement" class="control-label two-lines">User account management</label> <label for="accountManagement" class="control-label two-lines">User account management</label>
<div class="onoffswitch"> <div class="onoffswitch">

View file

@ -0,0 +1,134 @@
<div id="wrapper" class="container">
<div class="row">
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav" data-ng-hide="createRealm">
<ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul>
</div>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}">Settings</a></li>
<li class="active">SMTP Configuration</li>
</ol>
<h2><span>{{realm.realm}}</span> SMTP Settings</h2>
<form name="realmForm" novalidate>
<fieldset>
<legend uncollapsed><span class="text">Required Settings</span></legend>
<div class="form-group clearfix">
<label for="smtpHost" class="control-label">Host <span class="required">*</span></label>
<div class="controls">
<input id="smtpHost" type="text" ng-model="realm.smtpServer.host" placeholder="SMTP Host" required>
</div>
</div>
<div class="form-group clearfix">
<label for="smtpPort" class="control-label">Port <span class="required">*</span></label>
<div class="controls">
<input id="smtpPort" type="text" ng-model="realm.smtpServer.port" placeholder="SMTP Port (defaults to 25)" required>
</div>
</div>
<div class="form-group clearfix">
<label for="smtpFrom" class="control-label">From <span class="required">*</span></label>
<div class="controls">
<input id="smtpFrom" type="email" ng-model="realm.smtpServer.from" placeholder="SMTP From" required>
</div>
</div>
<div class="form-group clearfix">
<label for="smtpSSL" class="control-label">Enable SSL</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.smtpServer.ssl" class="onoffswitch-checkbox" name="smtpSSL" id="smtpSSL">
<label for="smtpSSL" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<div class="form-group clearfix">
<label for="smtpStartTLS" class="control-label">Enable StartTLS</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.smtpServer.starttls" class="onoffswitch-checkbox" name="smtpStartTLS" id="smtpStartTLS">
<label for="smtpStartTLS" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</fieldset>
<fieldset>
<legend collapsed><span class="text">Authentication</span></legend>
<div class="form-group clearfix">
<label for="smtpAuth" class="control-label">Enabled</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.smtpServer.auth" class="onoffswitch-checkbox" name="smtpAuth" id="smtpAuth">
<label for="smtpAuth" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<div class="form-group clearfix">
<label for="smtpUsername" class="control-label">Username <span class="required" ng-show="realm.smtpServer.auth">*</span></label>
<div class="controls">
<input id="smtpUsername" type="text" ng-model="realm.smtpServer.user" placeholder="Login Username" ng-disabled="!realm.smtpServer.auth" ng-required="realm.smtpServer.auth">
</div>
</div>
<div class="form-group clearfix">
<label for="smtpPassword" class="control-label">Password <span class="required" ng-show="realm.smtpServer.auth">*</span></label>
<div class="controls">
<input id="smtpPassword" type="password" ng-model="realm.smtpServer.password" placeholder="Login Password" ng-disabled="!realm.smtpServer.auth" ng-required="realm.smtpServer.auth">
</div>
</div>
</fieldset>
<!--
<fieldset class="border-top">
<div class="form-group clearfix" ng-repeat="(name, setting) in smtpSettings | orderBy: setting.required">
<label for="{{name}}" class="control-label">{{name.replace('mail.smtp.','')}} <span class="required" data-ng-show="setting.required">*</span></label>
<div ng-show="setting.type == 'boolean'" class="onoffswitch">
<input type="checkbox" data-ng-model="realm.smtp[name]" class="onoffswitch-checkbox" name="{{name}}" id="{{name}}">
<label for="{{name}}" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
<div ng-show="setting.type == 'String'" class="controls">
<input id="{{name}}" type="text" ng-model="realm.smtp[name]" placeholder="SMTP {{name}} / {{ setting.required }}">
</div>
<div ng-show="setting.type == 'int'" class="controls">
<input id="{{name}}" type="number" ng-model="realm.smtp[name]" placeholder="SMTP {{name}} / {{ setting.required }}">
</div>
</div>
</fieldset>
-->
<div class="form-actions">
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
</button>
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
</div>
</form>
</div>
</div>
<div id="container-right-bg"></div>
</div>
</div>

View file

@ -9,6 +9,7 @@
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul> </ul>
</div> </div>
<div id="content"> <div id="content">
@ -92,7 +93,7 @@
<div ng-include src="'partials/provider/'+ helpPId +'-help.html'"></div> <div ng-include src="'partials/provider/'+ helpPId +'-help.html'"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn" ng-click="closeHelp()">Close</button> <button ng-click="closeHelp()">Close</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -9,6 +9,7 @@
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li class="active"><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul> </ul>
</div> </div>
<div id="content"> <div id="content">
@ -49,7 +50,7 @@
</div> </div>
</div> </div>
<div class="form-group input-select"> <div class="form-group input-select">
<label for="accessCodeLifespanUserAction">Access code user action lifespan</label> <label for="accessCodeLifespanUserAction" class="two-lines">Access code user action lifespan</label>
<div class="input-group"> <div class="input-group">
<input type="text" data-ng-model="realm.accessCodeLifespanUserAction" id="accessCodeLifespanUserAction" name="accessCodeLifespanUserAction" class="tiny"> <input type="text" data-ng-model="realm.accessCodeLifespanUserAction" id="accessCodeLifespanUserAction" name="accessCodeLifespanUserAction" class="tiny">
<div class="select-rcue"> <div class="select-rcue">

View file

@ -9,6 +9,7 @@
<li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul> </ul>
</div> </div>
<div id="content"> <div id="content">

View file

@ -9,6 +9,7 @@
<li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul> </ul>
</div> </div>
<div id="content"> <div id="content">

View file

@ -18,6 +18,7 @@ public class ApplicationRepresentation {
protected boolean enabled; protected boolean enabled;
protected List<CredentialRepresentation> credentials; protected List<CredentialRepresentation> credentials;
protected List<RoleRepresentation> roles; protected List<RoleRepresentation> roles;
protected String[] defaultRoles;
protected List<UserRoleMappingRepresentation> roleMappings; protected List<UserRoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings; protected List<ScopeMappingRepresentation> scopeMappings;
protected List<String> redirectUris; protected List<String> redirectUris;
@ -164,4 +165,12 @@ public class ApplicationRepresentation {
public void setWebOrigins(List<String> webOrigins) { public void setWebOrigins(List<String> webOrigins) {
this.webOrigins = webOrigins; this.webOrigins = webOrigins;
} }
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
} }

View file

@ -28,7 +28,7 @@ public class RealmRepresentation {
protected String privateKey; protected String privateKey;
protected String publicKey; protected String publicKey;
protected List<RoleRepresentation> roles; protected List<RoleRepresentation> roles;
protected String[] defaultRoles; protected List<String> defaultRoles;
protected Set<String> requiredCredentials; protected Set<String> requiredCredentials;
protected Set<String> requiredApplicationCredentials; protected Set<String> requiredApplicationCredentials;
protected Set<String> requiredOAuthClientCredentials; protected Set<String> requiredOAuthClientCredentials;
@ -220,11 +220,11 @@ public class RealmRepresentation {
this.roles = roles; this.roles = roles;
} }
public String[] getDefaultRoles() { public List<String> getDefaultRoles() {
return defaultRoles; return defaultRoles;
} }
public void setDefaultRoles(String[] defaultRoles) { public void setDefaultRoles(List<String> defaultRoles) {
this.defaultRoles = defaultRoles; this.defaultRoles = defaultRoles;
} }

50
dist/assembly.xml vendored Normal file
View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:maven:assembly:1.1.0-SNAPSHOT">
<id>distro</id>
<formats>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${build.target.dir}</directory>
<outputDirectory>keycloak-${project.version}</outputDirectory>
<excludes>
<exclude>**/*.sh</exclude>
<exclude>domain/tmp/auth</exclude>
<exclude>domain/tmp/auth</exclude>
<exclude>**/*-users.properties</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${build.target.dir}</directory>
<outputDirectory>keycloak-${project.version}</outputDirectory>
<includes>
<include>**/*.sh</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${build.target.dir}</directory>
<outputDirectory>keycloak-${project.version}</outputDirectory>
<includes>
<include>**/*-users.properties</include>
</includes>
<fileMode>0600</fileMode>
</fileSet>
<fileSet>
<directory>${build.target.dir}</directory>
<outputDirectory>keycloak-${project.version}</outputDirectory>
<includes>
<include>domain/tmp/auth</include>
<include>standalone/tmp/auth</include>
</includes>
<directoryMode>0700</directoryMode>
</fileSet>
</fileSets>
</assembly>

37
dist/build.xml vendored Normal file
View file

@ -0,0 +1,37 @@
<project name="keycloak-dist" basedir=".">
<target name="jboss">
<unzip src="${org.jboss.as:jboss-as-dist:zip}" dest="${project.build.directory}"/>
<chmod perm="755">
<fileset dir="${project.build.directory}/jboss-as-${jboss.version}/bin">
<include name="**/*.sh"/>
</fileset>
</chmod>
<move todir="${build.target.dir}" overwrite="true">
<fileset dir="${project.build.directory}/jboss-as-${jboss.version}">
<include name="**/*"/>
</fileset>
</move>
<delete dir="${project.build.directory}/jboss-as-${jboss.version}"/>
</target>
<target name="resteasy-modules">
<get src="http://sourceforge.net/projects/resteasy/files/Resteasy%20JAX-RS/${resteasy.version}/resteasy-jaxrs-${resteasy.version}-all.zip"
dest="${project.build.directory}" skipexisting="true"/>
<unzip src="${project.build.directory}/resteasy-jaxrs-${resteasy.version}-all.zip"
dest="${project.build.directory}">
<patternset>
<include name="resteasy-jaxrs-${resteasy.version}/resteasy-jboss-modules-${resteasy.version}.zip"/>
</patternset>
<mapper type="flatten"/>
</unzip>
<unzip src="${project.build.directory}/resteasy-jboss-modules-${resteasy.version}.zip"
dest="${build.target.dir}/modules"/>
</target>
<target name="keycloak-server">
<copy file="${org.keycloak:keycloak-server:war}"
tofile="${build.target.dir}/standalone/deployments/auth-server.war" overwrite="true"/>
</target>
<target name="all" depends="jboss, resteasy-modules, keycloak-server"/>
</project>

96
dist/pom.xml vendored Normal file
View file

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>1.0-alpha-1</version>
</parent>
<artifactId>keycloak-dist</artifactId>
<name>Keycloak Dist</name>
<packaging>pom</packaging>
<properties>
<build.target.dir>${project.build.directory}/keycloak-${project.version}</build.target.dir>
</properties>
<profiles>
<profile>
<id>release</id>
<activation>
<property>
<name>release</name>
<value>true</value>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server</artifactId>
<version>1.0-alpha-1</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-dist</artifactId>
<version>${jboss.version}</version>
<type>zip</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>build</id>
<phase>compile</phase>
<configuration>
<target>
<ant antfile="build.xml" inheritRefs="false">
<target name="all"/>
</ant>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<finalName>keycloak-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View file

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="keycloak.js"></script>
</head>
<body>
<script>
keycloak.init({
clientId : '57572475438.apps.googleusercontent.com',
clientSecret : 'xyfsPS9maRTz5fj0pOxf0zjD'
});
if (keycloak.authenticated) {
document.write('<h2>Token</h2><pre>' + keycloak.token + '</pre>');
document.write('<h2>Token info</h2><pre>' + JSON.stringify(keycloak.tokenInfo, undefined, 4) + '</pre>');
document.write('<h2>Profile</h2><pre>' + JSON.stringify(keycloak.profile(true), undefined, 4) + '</pre>');
document.write('<h2>Contacts</h2><pre>' + keycloak.contacts(true) + '</pre>');
} else {
document.write('<a href="#" id="login" onclick="keycloak.login()">Login</a>');
}
</script>
</body>
</html>

View file

@ -1,139 +0,0 @@
window.keycloak = (function () {
var kc = {};
var config = {
clientId: null,
clientSecret: null
};
kc.init = function (c) {
for (var prop in config) {
if (c[prop]) {
config[prop] = c[prop];
}
if (!config[prop]) {
throw new Error(prop + ' not defined');
}
}
loadToken();
if (kc.token) {
kc.user = kc.tokenInfo.user_id;
kc.authenticated = true;
} else {
kc.authenticated = false;
kc.user = null;
}
}
kc.login = function () {
var clientId = encodeURIComponent(config.clientId);
var redirectUri = encodeURIComponent(window.location.href);
var state = encodeURIComponent(createUUID());
var scope = encodeURIComponent('https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/plus.login');
var url = 'https://accounts.google.com/o/oauth2/auth?response_type=token&client_id=' + clientId + '&redirect_uri=' + redirectUri
+ '&state=' + state + '&scope=' + scope;
sessionStorage.state = state;
window.location.href = url;
}
function parseToken(token) {
return JSON.parse(atob(token.split('.')[1]));
}
kc.profile = function(header) {
var url = 'https://www.googleapis.com/oauth2/v1/userinfo'
if (!header) {
url = url + '?access_token=' + kc.token;
}
var http = new XMLHttpRequest();
http.open('GET', url, false);
if (header) {
http.setRequestHeader('Authorization', 'Bearer ' + kc.token);
}
http.send();
if (http.status == 200) {
return JSON.parse(http.responseText);
}
}
kc.contacts = function(header) {
var url = 'https://www.googleapis.com/plus/v1/people/me';
if (!header) {
url = url + '?access_token=' + kc.token;
}
var http = new XMLHttpRequest();
http.open('GET', url, false);
if (header) {
http.setRequestHeader('Authorization', 'Bearer ' + kc.token);
}
http.send();
if (http.status == 200) {
return http.responseText;
}
}
return kc;
function loadToken() {
var params = {}
var queryString = location.hash.substring(1)
var regex = /([^&=]+)=([^&]*)/g, m;
while (m = regex.exec(queryString)) {
params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
var token = params['access_token'];
var state = params['state'];
if (token && state === sessionStorage.state) {
window.history.replaceState({}, document.title, location.protocol + "//" + location.host + location.pathname);
kc.token = token;
var url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' + token;
var http = new XMLHttpRequest();
http.open('GET', url, false);
http.send();
if (http.status == 200) {
kc.tokenInfo = JSON.parse(http.responseText);
}
}
return undefined;
}
function getQueryParam(name) {
console.debug(window.location.hash);
var params = window.location.hash.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
if (decodeURIComponent(p[0]) == name) {
return p[1];
}
}
}
function createUUID() {
var s = [];
var hexDigits = '0123456789abcdef';
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = '4';
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = '-';
var uuid = s.join('');
return uuid;
}
})();

View file

@ -1,222 +0,0 @@
<<<<<<< Updated upstream
window.keycloak = (function() {
var kc = {};
var config = null;
kc.init = function(c) {
config = c;
var token = getTokenFromCode();
if (token) {
var t = parseToken(token);
kc.user = t.prn;
kc.authenticated = true;
} else {
kc.authenticated = false;
}
}
kc.login = function() {
var clientId = encodeURIComponent(config.clientId);
var redirectUri = encodeURIComponent(window.location.href);
var state = encodeURIComponent(createUUID());
var realm = encodeURIComponent(config.realm);
var url = config.baseUrl + '/rest/realms/' + realm + '/tokens/login?response_type=code&client_id=' + clientId + '&redirect_uri=' + redirectUri
+ '&state=' + state;
window.location.href = url;
}
return kc;
function parseToken(token) {
return JSON.parse(atob(token.split('.')[1]));
}
function getTokenFromCode() {
var code = getQueryParam('code');
if (code) {
window.history.replaceState({}, document.title, location.protocol + "//" + location.host + location.pathname);
var clientId = encodeURIComponent(config.clientId);
var clientSecret = encodeURIComponent(config.clientSecret);
var realm = encodeURIComponent(config.realm);
var params = 'code=' + code + '&client_id=' + config.clientId + '&password=' + config.clientSecret;
var url = config.baseUrl + '/rest/realms/' + realm + '/tokens/access/codes'
var http = new XMLHttpRequest();
http.open('POST', url, false);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.send(params);
if (http.status == 200) {
return JSON.parse(http.responseText)['access_token'];
}
}
return undefined;
}
function getQueryParam(name) {
var params = window.location.search.substring(1).split('&');
for ( var i = 0; i < params.length; i++) {
var p = params[i].split('=');
if (decodeURIComponent(p[0]) == name) {
return p[1];
}
}
}
function createUUID() {
var s = [];
var hexDigits = '0123456789abcdef';
for ( var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = '4';
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = '-';
var uuid = s.join('');
return uuid;
}
=======
window.keycloak = (function () {
var kc = {};
var config = {
baseUrl : null,
clientId : null,
clientSecret: null,
realm: null
};
kc.init = function (c) {
for (var prop in config) {
if (c[prop]) {
config[prop] = c[prop];
}
if (!config[prop]) {
throw new Error(prop + 'not defined');
}
}
var token = getTokenFromCode();
if (token) {
var t = parseToken(token);
kc.user = t.prn;
kc.authenticated = true;
} else {
kc.authenticated = false;
}
}
kc.login = function () {
var clientId = encodeURIComponent(config.clientId);
var redirectUri = encodeURIComponent(window.location.href);
var realm = encodeURIComponent(config.realm);
var state = encodeURIComponent(createUUID());
var url = config.baseUrl + '/rest/realms/' + realm + '/tokens/login?response_type=code&client_id=' + clientId + '&redirect_uri=' + redirectUri
+ '&state=' + state;
sessionStorage.state = state;
window.location.href = url;
}
return kc;
function parseToken(token) {
var t = base64Decode(token.split('.')[1]);
return JSON.parse(t);
}
function getTokenFromCode() {
var code = getQueryParam('code');
var state = getQueryParam('state');
if (code) {
if (state && state === sessionStorage.state) {
window.history.replaceState({}, document.title, location.protocol + "//" + location.host + location.pathname);
var clientId = encodeURIComponent(config.clientId);
var clientSecret = encodeURIComponent(config.clientSecret);
var realm = encodeURIComponent(config.realm);
var params = 'code=' + code + '&client_id=' + clientId + '&password=' + clientSecret;
var url = config.baseUrl + '/rest/realms/' + realm + '/tokens/access/codes'
var http = new XMLHttpRequest();
http.open('POST', url, false);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.send(params);
if (http.status == 200) {
return JSON.parse(http.responseText)['access_token'];
}
}
}
return undefined;
}
function getQueryParam(name) {
var params = window.location.search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
if (decodeURIComponent(p[0]) == name) {
return p[1];
}
}
}
function createUUID() {
var s = [];
var hexDigits = '0123456789abcdef';
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = '4';
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = '-';
var uuid = s.join('');
return uuid;
}
function base64Decode(data) {
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
dec = "",
tmp_arr = [];
if (!data) {
return data;
}
data += '';
do {
h1 = b64.indexOf(data.charAt(i++));
h2 = b64.indexOf(data.charAt(i++));
h3 = b64.indexOf(data.charAt(i++));
h4 = b64.indexOf(data.charAt(i++));
bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
o1 = bits >> 16 & 0xff;
o2 = bits >> 8 & 0xff;
o3 = bits & 0xff;
if (h3 == 64) {
tmp_arr[ac++] = String.fromCharCode(o1);
} else if (h4 == 64) {
tmp_arr[ac++] = String.fromCharCode(o1, o2);
} else {
tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
}
} while (i < data.length);
dec = tmp_arr.join('');
return dec;
}
>>>>>>> Stashed changes
})();

View file

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-2.0.3.js"></script>
</head>
<body>
<script>
$.ajax('https://baas.kinvey.com/appdata/kid_PVD-jo1HqO');
</script>
</body>
</html>

View file

@ -1,60 +0,0 @@
{
"id": "test",
"realm": "test",
"enabled": true,
"tokenLifespan": 300,
"accessCodeLifespan": 10,
"accessCodeLifespanUserAction": 600,
"sslNotRequired": true,
"cookieLoginAllowed": true,
"registrationAllowed": true,
"resetPasswordAllowed": true,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"defaultRoles": [ "user" ],
"users" : [
{
"username" : "test-user@localhost",
"enabled": true,
"email" : "test-user@localhost",
"credentials" : [
{ "type" : "password",
"value" : "password" }
]
}
],
"roles": [
{
"name": "user",
"description": "Have User privileges"
},
{
"name": "admin",
"description": "Have Administrator privileges"
}
],
"roleMappings": [
{
"username": "test-user@localhost",
"roles": ["user"]
}
],
"applications": [
{
"name": "test-app",
"enabled": true,
"adminUrl": "http://localhost:8081/app/logout",
"useRealmMappings": true,
"webOrigins": [ "http://localhost", "http://localhost:8000", "http://localhost:8080" ],
"credentials": [
{
"type": "password",
"value": "password"
}
]
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 B

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

View file

@ -49,13 +49,30 @@ body {
width: 100%; width: 100%;
min-width: 120em; min-width: 120em;
} }
.rcue-login-register .form-area.separator,
.rcue-login-register .form-area.social,
.rcue-login-register .form-area.social.separator {
background-repeat: no-repeat;
background-position: 42.7em center;
}
.rcue-login-register .form-area.separator {
background-image: url(img/login-register-separator.png);
background-position: 43.2em center;
}
.rcue-login-register .form-area.social {
background-image: url(img/login-register-social.png);
}
.rcue-login-register .form-area.social.separator {
background-image: url(img/login-register-social-separator.png);
}
.rcue-login-register .background-area .section { .rcue-login-register .background-area .section {
float: left; float: left;
padding: 0 4.5em 0 4.6em; padding: 0 4.5em 0 4.6em;
width: auto; width: auto;
position: relative; position: relative;
} }
.rcue-login-register .background-area .separator .section { .rcue-login-register .background-area .separator .section,
.rcue-login-register .background-area .social .section {
padding-top: 1.5em; padding-top: 1.5em;
padding-bottom: 1.5em; padding-bottom: 1.5em;
} }
@ -65,15 +82,6 @@ body {
.rcue-login-register .background-area .section:first-child { .rcue-login-register .background-area .section:first-child {
padding-right: 4.5em; padding-right: 4.5em;
} }
.rcue-login-register .form-area.separator {
background-image: url(img/login-register-separator.png);
background-repeat: no-repeat;
background-position: 43.2em center;
}
.rcue-login-register .form-area.social {
background-image: url(img/login-register-social-separators.png);
background-position: 39.6em center;
}
.rcue-login-register .section > p { .rcue-login-register .section > p {
font-size: 1.3em; font-size: 1.3em;
margin-bottom: 1.53846153846154em; margin-bottom: 1.53846153846154em;
@ -120,7 +128,6 @@ body {
.rcue-login-register form > div.aside-btn input[type="checkbox"] { .rcue-login-register form > div.aside-btn input[type="checkbox"] {
margin-bottom: 0.54545454545455em; margin-bottom: 0.54545454545455em;
/* 6px */ /* 6px */
} }
.rcue-login-register form > input[type="button"], .rcue-login-register form > input[type="button"],
.rcue-login-register form > input[type="submit"]{ .rcue-login-register form > input[type="submit"]{
@ -137,13 +144,10 @@ body {
top: -0.636363636363636em; top: -0.636363636363636em;
} }
.rcue-login-register .feedback.bottom-left { .rcue-login-register .feedback.bottom-left {
left: 32.7em; left: 35.7em;
top: -9.2em; top: -9.2em;
min-width: 35em; min-width: 35em;
} }
.rcue-login-register.reset .feedback.bottom-left {
left: 35.7em;
}
.rcue-login-register input.error[type="text"], .rcue-login-register input.error[type="text"],
.rcue-login-register input.error[type="password"], .rcue-login-register input.error[type="password"],
.rcue-login-register input.error[type="email"] { .rcue-login-register input.error[type="email"] {

View file

@ -18,18 +18,20 @@
<span>${role.description}</span> <span>${role.description}</span>
</li> </li>
</#list> </#list>
</ul>
<#list oauth.resourceRolesRequested?keys as resourceRole> <#list oauth.resourceRolesRequested?keys as resourceRole>
<p class="instruction"><strong>${resourceRole}</strong> requests access to:</p> <li>
<ul> <strong>${resourceRole}</strong>
<#list oauth.resourceRolesRequested[resourceRole] as role> <ul>
<li> <#list oauth.resourceRolesRequested[resourceRole] as role>
<span>${role.description}</span> <li>
</li> <span><#if role.description??>${role.description}<#else>${role.name}</#if></span>
</#list> </li>
</ul> </#list>
</#list> </ul>
</li>
</#list>
</ul>
<p class="terms">Keycloak Central Login and Google will use this information in accordance with their respective terms of service and privacy policies.</p> <p class="terms">Keycloak Central Login and Google will use this information in accordance with their respective terms of service and privacy policies.</p>
<form class="form-actions" action="${oauth.action}" method="POST"> <form class="form-actions" action="${oauth.action}" method="POST">

View file

@ -39,4 +39,4 @@
</div> </div>
</#if> </#if>
</@layout.registrationLayout> </@layout.registrationLayout>

View file

@ -1,5 +1,7 @@
package org.keycloak.models; package org.keycloak.models;
import java.util.List;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -31,4 +33,9 @@ public interface ApplicationModel extends RoleContainerModel, RoleMapperModel, S
void setBaseUrl(String url); void setBaseUrl(String url);
List<String> getDefaultRoles();
void addDefaultRole(String name);
void updateDefaultRoles(String[] defaultRoles);
} }

View file

@ -12,5 +12,9 @@ public interface Constants {
String IDENTITY_REQUESTER_ROLE = "KEYCLOAK_IDENTITY_REQUESTER"; String IDENTITY_REQUESTER_ROLE = "KEYCLOAK_IDENTITY_REQUESTER";
String WILDCARD_ROLE = "*"; String WILDCARD_ROLE = "*";
String ACCOUNT_APPLICATION = "Account";
String ACCOUNT_PROFILE_ROLE = "view-profile";
String ACCOUNT_MANAGE_ROLE = "manage-account";
String ACCOUNT_MANAGEMENT_APPLICATION = "Account Management"; String ACCOUNT_MANAGEMENT_APPLICATION = "Account Management";
} }

View file

@ -84,7 +84,7 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
UserModel addUser(String username); UserModel addUser(String username);
List<RoleModel> getDefaultRoles(); List<String> getDefaultRoles();
void addDefaultRole(String name); void addDefaultRole(String name);

View file

@ -272,4 +272,61 @@ public class ApplicationAdapter implements ApplicationModel {
return query; return query;
} }
@Override
public List<String> getDefaultRoles() {
Collection<RoleEntity> entities = application.getDefaultRoles();
List<String> roles = new ArrayList<String>();
if (entities == null) return roles;
for (RoleEntity entity : entities) {
roles.add(entity.getName());
}
return roles;
}
@Override
public void addDefaultRole(String name) {
RoleModel role = getRole(name);
if (role == null) {
role = addRole(name);
}
Collection<RoleEntity> entities = application.getDefaultRoles();
for (RoleEntity entity : entities) {
if (entity.getId().equals(role.getId())) {
return;
}
}
entities.add(((RoleAdapter) role).getRole());
em.flush();
}
public static boolean contains(String str, String[] array) {
for (String s : array) {
if (str.equals(s)) return true;
}
return false;
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
Collection<RoleEntity> entities = application.getDefaultRoles();
Set<String> already = new HashSet<String>();
List<RoleEntity> remove = new ArrayList<RoleEntity>();
for (RoleEntity rel : entities) {
if (!contains(rel.getName(), defaultRoles)) {
remove.add(rel);
} else {
already.add(rel.getName());
}
}
for (RoleEntity entity : remove) {
entities.remove(entity);
}
em.flush();
for (String roleName : defaultRoles) {
if (!already.contains(roleName)) {
addDefaultRole(roleName);
}
}
em.flush();
}
} }

View file

@ -457,12 +457,12 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public List<RoleModel> getDefaultRoles() { public List<String> getDefaultRoles() {
Collection<RoleEntity> entities = realm.getDefaultRoles(); Collection<RoleEntity> entities = realm.getDefaultRoles();
List<RoleModel> roles = new ArrayList<RoleModel>(); List<String> roles = new ArrayList<String>();
if (entities == null) return roles; if (entities == null) return roles;
for (RoleEntity entity : entities) { for (RoleEntity entity : entities) {
roles.add(new RoleAdapter(entity)); roles.add(entity.getName());
} }
return roles; return roles;
} }
@ -504,8 +504,8 @@ public class RealmAdapter implements RealmModel {
} }
for (RoleEntity entity : remove) { for (RoleEntity entity : remove) {
entities.remove(entity); entities.remove(entity);
em.remove(entity);
} }
em.flush();
for (String roleName : defaultRoles) { for (String roleName : defaultRoles) {
if (!already.contains(roleName)) { if (!already.contains(roleName)) {
addDefaultRole(roleName); addDefaultRole(roleName);
@ -543,6 +543,7 @@ public class RealmAdapter implements RealmModel {
em.persist(user); em.persist(user);
applicationData.setApplicationUser(user); applicationData.setApplicationUser(user);
applicationData.setName(name); applicationData.setName(name);
applicationData.setEnabled(true);
realm.getApplications().add(applicationData); realm.getApplications().add(applicationData);
em.persist(applicationData); em.persist(applicationData);
em.flush(); em.flush();

View file

@ -33,6 +33,10 @@ public class ApplicationEntity {
@JoinTable(name="APPLICATION_ROLES") @JoinTable(name="APPLICATION_ROLES")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>(); Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="APPLICATION_DEFAULT_ROLES")
Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
public String getId() { public String getId() {
return id; return id;
} }
@ -84,4 +88,12 @@ public class ApplicationEntity {
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public Collection<RoleEntity> getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
this.defaultRoles = defaultRoles;
}
} }

View file

@ -281,4 +281,19 @@ public class ApplicationAdapter implements ApplicationModel {
} }
return result; return result;
} }
@Override
public List<String> getDefaultRoles() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void addDefaultRole(String name) {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
//To change body of implemented methods use File | Settings | File Templates.
}
} }

View file

@ -15,10 +15,7 @@ import org.picketlink.idm.model.sample.SampleModel;
import org.picketlink.idm.query.IdentityQuery; import org.picketlink.idm.query.IdentityQuery;
import org.picketlink.idm.query.RelationshipQuery; import org.picketlink.idm.query.RelationshipQuery;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -257,4 +254,44 @@ public class ApplicationAdapter implements ApplicationModel {
return roles; return roles;
} }
@Override
public List<String> getDefaultRoles() {
if ( applicationData.getDefaultRoles() != null) {
return Arrays.asList(applicationData.getDefaultRoles());
}
else {
return Collections.emptyList();
}
}
@Override
public void addDefaultRole(String name) {
if (getRole(name) == null) {
addRole(name);
}
String[] defaultRoles = applicationData.getDefaultRoles();
if (defaultRoles == null) {
defaultRoles = new String[1];
} else {
defaultRoles = Arrays.copyOf(defaultRoles, defaultRoles.length + 1);
}
defaultRoles[defaultRoles.length - 1] = name;
applicationData.setDefaultRoles(defaultRoles);
updateApplication();
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
for (String name : defaultRoles) {
if (getRole(name) == null) {
addRole(name);
}
}
applicationData.setDefaultRoles(defaultRoles);
updateApplication();
}
} }

View file

@ -43,13 +43,7 @@ import java.io.StringWriter;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* Meant to be a per-request object * Meant to be a per-request object
@ -767,17 +761,15 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public List<RoleModel> getDefaultRoles() { public List<String> getDefaultRoles() {
List<RoleModel> defaultRoleModels = new ArrayList<RoleModel>(); if (realm.getDefaultRoles() == null) return Collections.emptyList();
if (realm.getDefaultRoles() != null) { List<String> list = new ArrayList<String>();
for (String name : realm.getDefaultRoles()) { for (String role : realm.getDefaultRoles()) {
RoleAdapter role = getRole(name); RoleModel model = getRole(role);
if (role != null) { if (model == null) throw new RuntimeException("default role missing");
defaultRoleModels.add(role); list.add(role);
}
}
} }
return defaultRoleModels; return list;
} }
@Override @Override

View file

@ -15,6 +15,7 @@ public class ApplicationData extends AbstractPartition {
private String managementUrl; private String managementUrl;
private String baseUrl; private String baseUrl;
private User resourceUser; private User resourceUser;
private String[] defaultRoles;
public ApplicationData() { public ApplicationData() {
super(null); super(null);
@ -76,4 +77,13 @@ public class ApplicationData extends AbstractPartition {
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
@AttributeProperty
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
} }

View file

@ -34,6 +34,9 @@ public class ApplicationEntity implements Serializable {
@AttributeValue @AttributeValue
private String baseUrl; private String baseUrl;
@AttributeValue
private String[] defaultRoles;
@OneToOne @OneToOne
@AttributeValue @AttributeValue
AccountTypeEntity resourceUser; AccountTypeEntity resourceUser;
@ -94,4 +97,13 @@ public class ApplicationEntity implements Serializable {
public void setResourceUser(AccountTypeEntity resourceUser) { public void setResourceUser(AccountTypeEntity resourceUser) {
this.resourceUser = resourceUser; this.resourceUser = resourceUser;
} }
public String[] getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
} }

View file

@ -37,6 +37,6 @@
<module>api</module> <module>api</module>
<module>picketlink</module> <module>picketlink</module>
<module>jpa</module> <module>jpa</module>
<module>mongo</module> <!-- <module>mongo</module> -->
</modules> </modules>
</project> </project>

View file

@ -20,6 +20,7 @@
<dom4j.version>1.6.1</dom4j.version> <dom4j.version>1.6.1</dom4j.version>
<mysql.version>5.1.25</mysql.version> <mysql.version>5.1.25</mysql.version>
<slf4j.version>1.6.1</slf4j.version> <slf4j.version>1.6.1</slf4j.version>
<jboss.version>7.1.1.Final</jboss.version>
</properties> </properties>
<url>http://keycloak.org</url> <url>http://keycloak.org</url>
@ -72,7 +73,8 @@
<module>admin-ui</module> <module>admin-ui</module>
<module>examples</module> <module>examples</module>
<module>testsuite</module> <module>testsuite</module>
<!--<module>ui</module> --> <module>server</module>
<module>dist</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>

149
server/pom.xml Executable file
View file

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server</artifactId>
<packaging>war</packaging>
<name>Keycloak Server</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jose-jwt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-ui</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-ui-styles</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-picketlink</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-google</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-twitter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-facebook</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-forms</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-config</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.161</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>auth-server</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,28 @@
package org.keycloak.server;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class KeycloakServerApplication extends KeycloakApplication {
public KeycloakServerApplication(@Context ServletContext servletContext) {
super(servletContext);
KeycloakSession session = factory.createSession();
session.getTransaction().begin();
ApplianceBootstrap bootstrap = new ApplianceBootstrap();
bootstrap.bootstrap(session);
session.getTransaction().commit();
}
}

View file

@ -0,0 +1,33 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
<class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
<class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="false" />
</properties>
</persistence-unit>
</persistence>

View file

@ -0,0 +1,10 @@
<jboss-deployment-structure>
<deployment>
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<dependencies>
<module name="org.jboss.resteasy.jose-jwt"/>
<module name="org.jboss.resteasy.resteasy-crypto"/>
<module name="org.bouncycastle"/>
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>auth-server</module-name>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.keycloak.server.KeycloakServerApplication</param-value>
</init-param>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<filter>
<filter-name>Keycloak Session Management</filter-name>
<filter-class>org.keycloak.services.filters.KeycloakSessionServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Keycloak Session Management</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>

View file

@ -48,26 +48,53 @@ public class EmailSender {
private static final Logger log = Logger.getLogger(EmailSender.class); private static final Logger log = Logger.getLogger(EmailSender.class);
private Properties properties; private Map<String, String> config;
public EmailSender(Map<String, String> config) { public EmailSender(Map<String, String> config) {
properties = new Properties(); this.config = config;
for (Entry<String, String> e : config.entrySet()) {
properties.put("mail.smtp." + e.getKey(), e.getValue());
}
} }
public void send(String address, String subject, String body) throws MessagingException { public void send(String address, String subject, String body) throws MessagingException {
Session session = Session.getInstance(properties); Properties props = new Properties();
props.setProperty("mail.smtp.host", config.get("host"));
boolean auth = "true".equals(config.get("auth"));
boolean ssl = "true".equals(config.get("ssl"));
boolean starttls = "true".equals(config.get("starttls"));
if (config.containsKey("port")) {
props.setProperty("mail.smtp.port", config.get("port"));
}
if (auth) {
props.put("mail.smtp.auth", "true");
}
if (ssl) {
props.put("mail.smtp.socketFactory.port", config.get("port"));
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
if (starttls) {
props.put("mail.smtp.starttls.enable", "true");
}
String from = config.get("from");
Session session = Session.getInstance(props);
Message msg = new MimeMessage(session); Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(properties.getProperty("mail.smtp.from"))); msg.setFrom(new InternetAddress(from));
msg.setSubject(subject); msg.setSubject(subject);
msg.setText(body); msg.setText(body);
msg.saveChanges(); msg.saveChanges();
Transport transport = session.getTransport("smtp"); Transport transport = session.getTransport("smtp");
transport.connect(properties.getProperty("mail.smtp.user"), properties.getProperty("mail.smtp.password")); if (auth) {
transport.connect(config.get("user"), config.get("password"));
} else {
transport.connect();
}
transport.sendMessage(msg, new InternetAddress[] { new InternetAddress(address) }); transport.sendMessage(msg, new InternetAddress[] { new InternetAddress(address) });
} }

View file

@ -70,6 +70,11 @@ public class ApplicationManager {
if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription()); if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
} }
} }
if (resourceRep.getDefaultRoles() != null) {
applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles());
}
if (resourceRep.getRoleMappings() != null) { if (resourceRep.getRoleMappings() != null) {
for (UserRoleMappingRepresentation mapping : resourceRep.getRoleMappings()) { for (UserRoleMappingRepresentation mapping : resourceRep.getRoleMappings()) {
UserModel user = realm.getUser(mapping.getUsername()); UserModel user = realm.getUser(mapping.getUsername());
@ -114,6 +119,10 @@ public class ApplicationManager {
resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired()); resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
resource.updateApplication(); resource.updateApplication();
if (rep.getDefaultRoles() != null) {
resource.updateDefaultRoles(rep.getDefaultRoles());
}
List<String> redirectUris = rep.getRedirectUris(); List<String> redirectUris = rep.getRedirectUris();
if (redirectUris != null) { if (redirectUris != null) {
resource.getApplicationUser().setRedirectUris(new HashSet<String>(redirectUris)); resource.getApplicationUser().setRedirectUris(new HashSet<String>(redirectUris));
@ -144,6 +153,10 @@ public class ApplicationManager {
rep.setWebOrigins(new LinkedList<String>(webOrigins)); rep.setWebOrigins(new LinkedList<String>(webOrigins));
} }
if (!applicationModel.getDefaultRoles().isEmpty()) {
rep.setDefaultRoles(applicationModel.getDefaultRoles().toArray(new String[0]));
}
return rep; return rep;
} }

View file

@ -56,24 +56,27 @@ public class AuthenticationManager {
String cookieName = KEYCLOAK_IDENTITY_COOKIE; String cookieName = KEYCLOAK_IDENTITY_COOKIE;
URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId()); URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
String cookiePath = uri.getPath(); String cookiePath = uri.getPath();
return createLoginCookie(realm, user, cookieName, cookiePath); return createLoginCookie(realm, user, null, cookieName, cookiePath);
} }
public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) { public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
String cookieName = SaasService.SAAS_IDENTITY_COOKIE; String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
URI uri = SaasService.saasCookiePath(uriInfo).build(); URI uri = SaasService.saasCookiePath(uriInfo).build();
String cookiePath = uri.getPath(); String cookiePath = uri.getPath();
return createLoginCookie(realm, user, cookieName, cookiePath); return createLoginCookie(realm, user, null, cookieName, cookiePath);
} }
public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, URI uri) { public NewCookie createAccountIdentityCookie(RealmModel realm, UserModel user, UserModel client, URI uri) {
String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE; String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE;
String cookiePath = uri.getPath(); String cookiePath = uri.getPath();
return createLoginCookie(realm, user, cookieName, cookiePath); return createLoginCookie(realm, user, client, cookieName, cookiePath);
} }
protected NewCookie createLoginCookie(RealmModel realm, UserModel user, String cookieName, String cookiePath) { protected NewCookie createLoginCookie(RealmModel realm, UserModel user, UserModel client, String cookieName, String cookiePath) {
SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName()); SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
if (client != null) {
identityToken.issuedFor(client.getLoginName());
}
String encoded = encodeToken(realm, identityToken); String encoded = encodeToken(realm, identityToken);
boolean secureOnly = !realm.isSslNotRequired(); boolean secureOnly = !realm.isSslNotRequired();
logger.debug("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath); logger.debug("creatingLoginCookie - name: {0} path: {1}", cookieName, cookiePath);
@ -127,15 +130,17 @@ public class AuthenticationManager {
public UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { public UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
String cookieName = KEYCLOAK_IDENTITY_COOKIE; String cookieName = KEYCLOAK_IDENTITY_COOKIE;
return authenticateIdentityCookie(realm, uriInfo, headers, cookieName); Auth auth = authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
return auth != null ? auth.getUser() : null;
} }
public UserModel authenticateSaasIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { public UserModel authenticateSaasIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
String cookieName = SaasService.SAAS_IDENTITY_COOKIE; String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
return authenticateIdentityCookie(realm, uriInfo, headers, cookieName); Auth auth = authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
return auth != null ? auth.getUser() : null;
} }
public UserModel authenticateAccountIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) { public Auth authenticateAccountIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE; String cookieName = AccountService.ACCOUNT_IDENTITY_COOKIE;
return authenticateIdentityCookie(realm, uriInfo, headers, cookieName); return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
} }
@ -144,11 +149,19 @@ public class AuthenticationManager {
UserModel user = authenticateSaasIdentityCookie(realm, uriInfo, headers); UserModel user = authenticateSaasIdentityCookie(realm, uriInfo, headers);
if (user != null) return user; if (user != null) return user;
Auth auth = authenticateBearerToken(realm, headers);
return auth != null ? auth.getUser() : null;
}
public Auth authenticateAccountIdentity(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
Auth auth = authenticateAccountIdentityCookie(realm, uriInfo, headers);
if (auth != null) return auth;
return authenticateBearerToken(realm, headers); return authenticateBearerToken(realm, headers);
} }
protected UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) { protected Auth authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) {
Cookie cookie = headers.getCookies().get(cookieName); Cookie cookie = headers.getCookies().get(cookieName);
if (cookie == null) { if (cookie == null) {
logger.debug("authenticateCookie could not find cookie: {0}", cookieName); logger.debug("authenticateCookie could not find cookie: {0}", cookieName);
@ -163,13 +176,28 @@ public class AuthenticationManager {
expireIdentityCookie(realm, uriInfo); expireIdentityCookie(realm, uriInfo);
return null; return null;
} }
Auth auth = new Auth(token);
UserModel user = realm.getUser(token.getPrincipal()); UserModel user = realm.getUser(token.getPrincipal());
if (user == null || !user.isEnabled()) { if (user == null || !user.isEnabled()) {
logger.debug("Unknown user in identity cookie"); logger.debug("Unknown user in identity cookie");
expireIdentityCookie(realm, uriInfo); expireIdentityCookie(realm, uriInfo);
return null; return null;
} }
return user; auth.setUser(user);
if (token.getIssuedFor() != null) {
UserModel client = realm.getUser(token.getIssuedFor());
if (client == null || !client.isEnabled()) {
logger.debug("Unknown client in identity cookie");
expireIdentityCookie(realm, uriInfo);
return null;
}
auth.setClient(client);
}
return auth;
} catch (VerificationException e) { } catch (VerificationException e) {
logger.debug("Failed to verify identity cookie", e); logger.debug("Failed to verify identity cookie", e);
expireIdentityCookie(realm, uriInfo); expireIdentityCookie(realm, uriInfo);
@ -177,11 +205,11 @@ public class AuthenticationManager {
return null; return null;
} }
public UserModel authenticateBearerToken(RealmModel realm, HttpHeaders headers) { public Auth authenticateBearerToken(RealmModel realm, HttpHeaders headers) {
String tokenString = null; String tokenString = null;
String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION); String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authHeader == null) { if (authHeader == null) {
throw new NotAuthorizedException("Bearer"); return null;
} else { } else {
String[] split = authHeader.trim().split("\\s+"); String[] split = authHeader.trim().split("\\s+");
if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer"); if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer");
@ -195,11 +223,24 @@ public class AuthenticationManager {
if (!token.isActive()) { if (!token.isActive()) {
throw new NotAuthorizedException("token_expired"); throw new NotAuthorizedException("token_expired");
} }
Auth auth = new Auth(token);
UserModel user = realm.getUser(token.getPrincipal()); UserModel user = realm.getUser(token.getPrincipal());
if (user == null || !user.isEnabled()) { if (user == null || !user.isEnabled()) {
throw new NotAuthorizedException("invalid_user"); throw new NotAuthorizedException("invalid_user");
} }
return user; auth.setUser(user);
if (token.getIssuedFor() != null) {
UserModel client = realm.getUser(token.getIssuedFor());
if (client == null || !client.isEnabled()) {
throw new NotAuthorizedException("invalid_user");
}
auth.setClient(client);
}
return auth;
} catch (VerificationException e) { } catch (VerificationException e) {
logger.error("Failed to verify token", e); logger.error("Failed to verify token", e);
throw new NotAuthorizedException("invalid_token"); throw new NotAuthorizedException("invalid_token");
@ -271,4 +312,34 @@ public class AuthenticationManager {
SUCCESS, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED SUCCESS, ACCOUNT_DISABLED, ACTIONS_REQUIRED, INVALID_USER, INVALID_CREDENTIALS, MISSING_PASSWORD, MISSING_TOTP, FAILED
} }
public static class Auth {
private SkeletonKeyToken token;
private UserModel user;
private UserModel client;
public Auth(SkeletonKeyToken token) {
this.token = token;
}
public SkeletonKeyToken getToken() {
return token;
}
public UserModel getUser() {
return user;
}
public UserModel getClient() {
return client;
}
void setUser(UserModel user) {
this.user = user;
}
void setClient(UserModel client) {
this.client = client;
}
}
} }

View file

@ -110,7 +110,7 @@ public class RealmManager {
realm.updateRequiredApplicationCredentials(rep.getRequiredApplicationCredentials()); realm.updateRequiredApplicationCredentials(rep.getRequiredApplicationCredentials());
} }
if (rep.getDefaultRoles() != null) { if (rep.getDefaultRoles() != null) {
realm.updateDefaultRoles(rep.getDefaultRoles()); realm.updateDefaultRoles(rep.getDefaultRoles().toArray(new String[rep.getDefaultRoles().size()]));
} }
if (rep.getAccountManagement() != null && rep.getAccountManagement()) { if (rep.getAccountManagement() != null && rep.getAccountManagement()) {
@ -129,9 +129,11 @@ public class RealmManager {
} }
private void enableAccountManagement(RealmModel realm) { private void enableAccountManagement(RealmModel realm) {
ApplicationModel application = realm.getApplicationById(Constants.ACCOUNT_MANAGEMENT_APPLICATION); ApplicationModel application = realm.getApplicationById(Constants.ACCOUNT_APPLICATION);
if (application == null) { if (application == null) {
application = realm.addApplication(Constants.ACCOUNT_MANAGEMENT_APPLICATION); application = realm.addApplication(Constants.ACCOUNT_APPLICATION);
application.addDefaultRole(Constants.ACCOUNT_PROFILE_ROLE);
application.addDefaultRole(Constants.ACCOUNT_MANAGE_ROLE);
UserCredentialModel password = new UserCredentialModel(); UserCredentialModel password = new UserCredentialModel();
password.setType(UserCredentialModel.PASSWORD); password.setType(UserCredentialModel.PASSWORD);
@ -146,7 +148,7 @@ public class RealmManager {
} }
private void disableAccountManagement(RealmModel realm) { private void disableAccountManagement(RealmModel realm) {
ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_MANAGEMENT_APPLICATION); ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_APPLICATION);
if (application != null) { if (application != null) {
application.setEnabled(false); // TODO Should we delete the application instead? application.setEnabled(false); // TODO Should we delete the application instead?
} }
@ -410,9 +412,11 @@ public class RealmManager {
rep.setLastName(user.getLastName()); rep.setLastName(user.getLastName());
rep.setFirstName(user.getFirstName()); rep.setFirstName(user.getFirstName());
rep.setEmail(user.getEmail()); rep.setEmail(user.getEmail());
Map<String, String> attrs = new HashMap<String, String>(); if (user.getAttributes() != null && !user.getAttributes().isEmpty()) {
attrs.putAll(user.getAttributes()); Map<String, String> attrs = new HashMap<String, String>();
rep.setAttributes(attrs); attrs.putAll(user.getAttributes());
rep.setAttributes(attrs);
}
return rep; return rep;
} }
@ -444,16 +448,14 @@ public class RealmManager {
rep.setSmtpServer(realm.getSmtpConfig()); rep.setSmtpServer(realm.getSmtpConfig());
rep.setSocialProviders(realm.getSocialConfig()); rep.setSocialProviders(realm.getSocialConfig());
ApplicationModel accountManagementApplication = realm.getApplicationNameMap().get(Constants.ACCOUNT_MANAGEMENT_APPLICATION); ApplicationModel accountManagementApplication = realm.getApplicationNameMap().get(Constants.ACCOUNT_APPLICATION);
rep.setAccountManagement(accountManagementApplication != null && accountManagementApplication.isEnabled()); rep.setAccountManagement(accountManagementApplication != null && accountManagementApplication.isEnabled());
List<RoleModel> defaultRoles = realm.getDefaultRoles(); List<String> defaultRoles = realm.getDefaultRoles();
if (defaultRoles.size() > 0) { if (!defaultRoles.isEmpty()) {
String[] d = new String[defaultRoles.size()]; List<String> roleStrings = new ArrayList<String>();
for (int i = 0; i < d.length; i++) { roleStrings.addAll(defaultRoles);
d[i] = defaultRoles.get(i).getName(); rep.setDefaultRoles(roleStrings);
}
rep.setDefaultRoles(d);
} }
List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials(); List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials();

View file

@ -19,7 +19,9 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/ */
package org.keycloak.social; package org.keycloak.services.managers;
import org.keycloak.social.RequestDetails;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;

View file

@ -15,6 +15,7 @@ import org.keycloak.representations.SkeletonKeyToken;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -51,12 +52,16 @@ public class TokenManager {
List<RoleModel> realmRolesRequested = code.getRealmRolesRequested(); List<RoleModel> realmRolesRequested = code.getRealmRolesRequested();
MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested(); MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested();
Set<String> realmMapping = realm.getRoleMappingValues(user); Set<String> realmMapping = realm.getRoleMappingValues(user);
realmMapping.addAll(realm.getDefaultRoles());
if (realmMapping != null && realmMapping.size() > 0 && (scopeMap == null || scopeMap.containsKey("realm"))) { if (realmMapping != null && realmMapping.size() > 0 && (scopeMap == null || scopeMap.containsKey("realm"))) {
Set<String> scope = realm.getScopeMappingValues(client); Set<String> scope = realm.getScopeMappingValues(client);
if (scope.size() > 0) { if (scope.size() > 0) {
Set<String> scopeRequest = null; Set<String> scopeRequest = null;
if (scopeMap != null) { if (scopeMap != null) {
if (scopeRequest == null) {
scopeRequest = new HashSet<String>();
}
scopeRequest.addAll(scopeMap.get("realm")); scopeRequest.addAll(scopeMap.get("realm"));
if (scopeRequest.contains(Constants.WILDCARD_ROLE)) scopeRequest = null; if (scopeRequest.contains(Constants.WILDCARD_ROLE)) scopeRequest = null;
} }
@ -71,11 +76,15 @@ public class TokenManager {
} }
for (ApplicationModel resource : realm.getApplications()) { for (ApplicationModel resource : realm.getApplications()) {
Set<String> mapping = resource.getRoleMappingValues(user); Set<String> mapping = resource.getRoleMappingValues(user);
mapping.addAll(resource.getDefaultRoles());
if (mapping != null && mapping.size() > 0 && (scopeMap == null || scopeMap.containsKey(resource.getName()))) { if (mapping != null && mapping.size() > 0 && (scopeMap == null || scopeMap.containsKey(resource.getName()))) {
Set<String> scope = resource.getScopeMappingValues(client); Set<String> scope = resource.getScopeMappingValues(client);
if (scope.size() > 0) { if (scope.size() > 0) {
Set<String> scopeRequest = null; Set<String> scopeRequest = null;
if (scopeMap != null) { if (scopeMap != null) {
if (scopeRequest == null) {
scopeRequest = new HashSet<String>();
}
scopeRequest.addAll(scopeMap.get(resource.getName())); scopeRequest.addAll(scopeMap.get(resource.getName()));
if (scopeRequest.contains(Constants.WILDCARD_ROLE)) scopeRequest = null; if (scopeRequest.contains(Constants.WILDCARD_ROLE)) scopeRequest = null;
} }

View file

@ -27,15 +27,13 @@ import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.AbstractOAuthClient; import org.keycloak.AbstractOAuthClient;
import org.keycloak.jaxrs.JaxrsOAuthClient; import org.keycloak.jaxrs.JaxrsOAuthClient;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.*;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager; import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
@ -44,23 +42,11 @@ import org.keycloak.services.resources.flows.Pages;
import org.keycloak.services.resources.flows.Urls; import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.validation.Validation; import org.keycloak.services.validation.Validation;
import javax.ws.rs.BadRequestException; import javax.ws.rs.*;
import javax.ws.rs.Consumes; import javax.ws.rs.core.*;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers; import javax.ws.rs.ext.Providers;
import java.net.URI; import java.net.URI;
import java.util.List;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -98,18 +84,42 @@ public class AccountService {
} }
private Response forwardToPage(String path, String template) { private Response forwardToPage(String path, String template) {
UserModel user = getUser(false); AuthenticationManager.Auth auth = getAuth(false);
if (user != null) { if (auth != null) {
return Flows.forms(realm, request, uriInfo).setUser(user).forwardToForm(template); if (!hasAccess(auth)) {
return noAccess();
}
return Flows.forms(realm, request, uriInfo).setUser(auth.getUser()).forwardToForm(template);
} else { } else {
return login(path); return login(path);
} }
} }
@Path("") private Response noAccess() {
return Flows.forms(realm, request, uriInfo).setError("No access").forwardToErrorPage();
}
@Path("/")
@OPTIONS
public Response accountPreflight() {
return Cors.add(request, Response.ok()).auth().preflight().build();
}
@Path("/")
@GET @GET
public Response accountPage() { public Response accountPage() {
return forwardToPage(null, Pages.ACCOUNT); List<MediaType> types = headers.getAcceptableMediaTypes();
if (types.contains(MediaType.WILDCARD_TYPE) || (types.contains(MediaType.TEXT_HTML_TYPE))) {
return forwardToPage(null, Pages.ACCOUNT);
} else if (types.contains(MediaType.APPLICATION_JSON_TYPE)) {
AuthenticationManager.Auth auth = getAuth(true);
if (!hasAccess(auth, Constants.ACCOUNT_PROFILE_ROLE)) {
return Response.status(Response.Status.FORBIDDEN).build();
}
return Cors.add(request, Response.ok(RealmManager.toRepresentation(auth.getUser()))).auth().allowedOrigins(auth.getClient()).build();
} else {
return Response.notAcceptable(Variant.VariantListBuilder.newInstance().mediaTypes(MediaType.TEXT_HTML_TYPE, MediaType.APPLICATION_JSON_TYPE).build()).build();
}
} }
@Path("social") @Path("social")
@ -136,12 +146,16 @@ public class AccountService {
return forwardToPage("access", Pages.ACCESS); return forwardToPage("access", Pages.ACCESS);
} }
@Path("") @Path("/")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processAccountUpdate(final MultivaluedMap<String, String> formData) { public Response processAccountUpdate(final MultivaluedMap<String, String> formData) {
AuthenticationManager.Auth auth = getAuth(true);
if (!hasAccess(auth)) {
return noAccess();
}
UserModel user = getUser(true); UserModel user = auth.getUser();
String error = Validation.validateUpdateProfileForm(formData); String error = Validation.validateUpdateProfileForm(formData);
if (error != null) { if (error != null) {
@ -159,7 +173,13 @@ public class AccountService {
@Path("totp-remove") @Path("totp-remove")
@GET @GET
public Response processTotpRemove() { public Response processTotpRemove() {
UserModel user = getUser(true); AuthenticationManager.Auth auth = getAuth(true);
if (!hasAccess(auth)) {
return noAccess();
}
UserModel user = auth.getUser();
user.setTotp(false); user.setTotp(false);
return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.MessageType.SUCCESS) return Flows.forms(realm, request, uriInfo).setError("successTotpRemoved").setErrorType(FormFlows.MessageType.SUCCESS)
.setUser(user).forwardToTotp(); .setUser(user).forwardToTotp();
@ -169,7 +189,12 @@ public class AccountService {
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processTotpUpdate(final MultivaluedMap<String, String> formData) { public Response processTotpUpdate(final MultivaluedMap<String, String> formData) {
UserModel user = getUser(true); AuthenticationManager.Auth auth = getAuth(true);
if (!hasAccess(auth)) {
return noAccess();
}
UserModel user = auth.getUser();
String totp = formData.getFirst("totp"); String totp = formData.getFirst("totp");
String totpSecret = formData.getFirst("totpSecret"); String totpSecret = formData.getFirst("totpSecret");
@ -196,7 +221,12 @@ public class AccountService {
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) { public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
UserModel user = getUser(true); AuthenticationManager.Auth auth = getAuth(true);
if (!hasAccess(auth)) {
return noAccess();
}
UserModel user = auth.getUser();
FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(user); FormFlows forms = Flows.forms(realm, request, uriInfo).setUser(user);
@ -297,7 +327,7 @@ public class AccountService {
} }
URI redirectUri = redirectBuilder.build(realm.getId()); URI redirectUri = redirectBuilder.build(realm.getId());
NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId())); NewCookie cookie = authManager.createAccountIdentityCookie(realm, accessCode.getUser(), client, Urls.accountBase(uriInfo.getBaseUri()).build(realm.getId()));
return Response.status(302).cookie(cookie).location(redirectUri).build(); return Response.status(302).cookie(cookie).location(redirectUri).build();
} finally { } finally {
authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath()); authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath());
@ -319,7 +349,7 @@ public class AccountService {
String authUrl = Urls.realmLoginPage(uriInfo.getBaseUri(), realm.getId()).toString(); String authUrl = Urls.realmLoginPage(uriInfo.getBaseUri(), realm.getId()).toString();
oauth.setAuthUrl(authUrl); oauth.setAuthUrl(authUrl);
oauth.setClientId(Constants.ACCOUNT_MANAGEMENT_APPLICATION); oauth.setClientId(Constants.ACCOUNT_APPLICATION);
URI accountUri = Urls.accountPageBuilder(uriInfo.getBaseUri()).path(AccountService.class, "loginRedirect").build(realm.getId()); URI accountUri = Urls.accountPageBuilder(uriInfo.getBaseUri()).path(AccountService.class, "loginRedirect").build(realm.getId());
@ -327,11 +357,42 @@ public class AccountService {
return oauth.redirect(uriInfo, accountUri.toString(), path); return oauth.redirect(uriInfo, accountUri.toString(), path);
} }
private UserModel getUser(boolean required) { private AuthenticationManager.Auth getAuth(boolean error) {
UserModel user = authManager.authenticateAccountIdentityCookie(realm, uriInfo, headers); AuthenticationManager.Auth auth = authManager.authenticateAccountIdentity(realm, uriInfo, headers);
if (user == null && required) { if (auth == null && error) {
throw new ForbiddenException(); throw new ForbiddenException();
} }
return user; return auth;
} }
private boolean hasAccess(AuthenticationManager.Auth auth) {
return hasAccess(auth, null);
}
private boolean hasAccess(AuthenticationManager.Auth auth, String role) {
UserModel client = auth.getClient();
if (realm.hasRole(client, Constants.APPLICATION_ROLE)) {
// Tokens from cookies don't have roles
if (hasRole(client, Constants.ACCOUNT_MANAGE_ROLE) || (role != null && hasRole(client, role))) {
return true;
}
}
SkeletonKeyToken.Access access = auth.getToken().getResourceAccess(application.getName());
if (access != null) {
if (access.isUserInRole(Constants.ACCOUNT_MANAGE_ROLE) || (role != null && access.isUserInRole(role))) {
return true;
}
}
return false;
}
private boolean hasRole(UserModel user, String role) {
if (application.getDefaultRoles().contains(role)) {
return true;
}
return application.hasRole(user, role);
}
} }

View file

@ -1,19 +1,37 @@
package org.keycloak.services.resources; package org.keycloak.services.resources;
import org.jboss.resteasy.spi.HttpRequest; import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.ResponseBuilder;
import java.util.Set;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.models.UserModel;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class Cors { public class Cors {
public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1);
public static final String DEFAULT_ALLOW_METHODS = "GET, OPTIONS";
public static final String ORIGIN = "Origin";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
private HttpRequest request; private HttpRequest request;
private ResponseBuilder response; private ResponseBuilder response;
private Set<String> allowedOrigins; private Set<String> allowedOrigins;
private String[] allowedMethods;
private boolean preflight;
private boolean auth;
public Cors(HttpRequest request, ResponseBuilder response) { public Cors(HttpRequest request, ResponseBuilder response) {
this.request = request; this.request = request;
@ -24,18 +42,60 @@ public class Cors {
return new Cors(request, response); return new Cors(request, response);
} }
public Cors allowedOrigins(Set<String> allowedOrigins) { public Cors preflight() {
this.allowedOrigins = allowedOrigins; preflight = true;
return this;
}
public Cors auth() {
auth = true;
return this;
}
public Cors allowedOrigins(UserModel client) {
if (client != null) {
allowedOrigins = client.getWebOrigins();
}
return this;
}
public Cors allowedMethods(String... allowedMethods) {
this.allowedMethods = allowedMethods;
return this; return this;
} }
public Response build() { public Response build() {
String origin = request.getHttpHeaders().getHeaderString("Origin"); String origin = request.getHttpHeaders().getHeaderString(ORIGIN);
if (origin == null || allowedOrigins == null || (!allowedOrigins.contains(origin))) { if (origin == null) {
return response.build(); return response.build();
} }
response.header("Access-Control-Allow-Origin", origin); if (!preflight && (allowedOrigins == null || !allowedOrigins.contains(origin))) {
return response.build();
}
response.header(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
if (allowedMethods != null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < allowedMethods.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(allowedMethods[i]);
}
response.header(ACCESS_CONTROL_ALLOW_METHODS, sb.toString());
} else {
response.header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
}
response.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
if (auth) {
response.header(ACCESS_CONTROL_ALLOW_HEADERS, "Authorization");
}
response.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE);
return response.build(); return response.build();
} }

View file

@ -3,9 +3,9 @@ package org.keycloak.services.resources;
import org.keycloak.SkeletonKeyContextResolver; import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider; import org.keycloak.models.ModelProvider;
import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager; import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.utils.PropertiesManager; import org.keycloak.services.utils.PropertiesManager;
import org.keycloak.social.SocialRequestManager;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;

View file

@ -69,7 +69,7 @@ public class RealmsResource {
throw new NotFoundException(); throw new NotFoundException();
} }
ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_MANAGEMENT_APPLICATION); ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_APPLICATION);
if (application == null || !application.isEnabled()) { if (application == null || !application.isEnabled()) {
logger.debug("account management not enabled"); logger.debug("account management not enabled");
throw new NotFoundException(); throw new NotFoundException();

View file

@ -39,12 +39,11 @@ import org.keycloak.services.resources.flows.Urls;
import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest; import org.keycloak.social.AuthRequest;
import org.keycloak.social.RequestDetails; import org.keycloak.social.RequestDetails;
import org.keycloak.social.RequestDetailsBuilder;
import org.keycloak.social.SocialConstants; import org.keycloak.social.SocialConstants;
import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialProviderException;
import org.keycloak.social.SocialRequestManager; import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.social.SocialUser; import org.keycloak.social.SocialUser;
import javax.imageio.spi.ServiceRegistry; import javax.imageio.spi.ServiceRegistry;
@ -181,19 +180,12 @@ public class SocialResource {
} }
realm.addSocialLink(user, socialLink); realm.addSocialLink(user, socialLink);
for (RoleModel role : realm.getDefaultRoles()) {
realm.grantRole(user, role);
}
} else { } else {
// Redirect user to registration screen with prefilled data from social provider // Redirect user to registration screen with prefilled data from social provider
MultivaluedMap<String, String> formData = fillRegistrationFormWithSocialData(socialUser); MultivaluedMap<String, String> formData = fillRegistrationFormWithSocialData(socialUser);
RequestDetailsBuilder reqDetailsBuilder = RequestDetailsBuilder.createFromRequestDetails(requestData);
reqDetailsBuilder.putSocialAttribute(SocialConstants.ATTR_SOCIAL_LINK, socialLink);
String requestId = UUID.randomUUID().toString(); String requestId = UUID.randomUUID().toString();
socialRequestManager.addRequest(requestId, reqDetailsBuilder.build()); socialRequestManager.addRequest(requestId, RequestDetails.create(requestData).build());
boolean secureOnly = !realm.isSslNotRequired(); boolean secureOnly = !realm.isSslNotRequired();
String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getPath(); String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getPath();
logger.debug("creating cookie for social registration - name: {0} path: {1}", SocialConstants.SOCIAL_REGISTRATION_COOKIE, logger.debug("creating cookie for social registration - name: {0} path: {1}", SocialConstants.SOCIAL_REGISTRATION_COOKIE,
@ -241,7 +233,7 @@ public class SocialResource {
try { try {
AuthRequest authRequest = provider.getAuthUrl(config); AuthRequest authRequest = provider.getAuthUrl(config);
RequestDetails socialRequest = RequestDetailsBuilder.create(providerId) RequestDetails socialRequest = RequestDetails.create(providerId)
.putSocialAttributes(authRequest.getAttributes()).putClientAttribute("realmId", realmId) .putSocialAttributes(authRequest.getAttributes()).putClientAttribute("realmId", realmId)
.putClientAttribute("clientId", clientId).putClientAttribute("scope", scope) .putClientAttribute("clientId", clientId).putClientAttribute("scope", scope)
.putClientAttribute("state", state).putClientAttribute("redirectUri", redirectUri).build(); .putClientAttribute("state", state).putClientAttribute("redirectUri", redirectUri).build();
@ -285,7 +277,6 @@ public class SocialResource {
String scope = requestData.getClientAttribute("scope"); String scope = requestData.getClientAttribute("scope");
String state = requestData.getClientAttribute("state"); String state = requestData.getClientAttribute("state");
String redirectUri = requestData.getClientAttribute("redirectUri"); String redirectUri = requestData.getClientAttribute("redirectUri");
SocialLinkModel socialLink = (SocialLinkModel)requestData.getSocialAttribute(SocialConstants.ATTR_SOCIAL_LINK);
Response response1 = tokenService.processRegisterImpl(clientId, scope, state, redirectUri, formData, true); Response response1 = tokenService.processRegisterImpl(clientId, scope, state, redirectUri, formData, true);
@ -301,7 +292,7 @@ public class SocialResource {
// Normally shouldn't happen // Normally shouldn't happen
throw new IllegalStateException("User " + username + " not found in the realm"); throw new IllegalStateException("User " + username + " not found in the realm");
} }
realm.addSocialLink(user, socialLink); realm.addSocialLink(user, new SocialLinkModel(requestData.getProviderId(), username));
// Expire cookie and invalidate requestData // Expire cookie and invalidate requestData
String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getPath(); String cookiePath = Urls.socialBase(uriInfo.getBaseUri()).build().getPath();

View file

@ -323,10 +323,6 @@ public class TokenService {
realm.updateCredential(user, credentials); realm.updateCredential(user, credentials);
} }
for (RoleModel role : realm.getDefaultRoles()) {
realm.grantRole(user, role);
}
return null; return null;
} }
@ -427,7 +423,7 @@ public class TokenService {
logger.debug("accessRequest SUCCESS"); logger.debug("accessRequest SUCCESS");
AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken()); AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
return Cors.add(request, Response.ok(res)).allowedOrigins(client.getWebOrigins()).build(); return Cors.add(request, Response.ok(res)).allowedOrigins(client).build();
} }
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) { protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) {
@ -468,7 +464,7 @@ public class TokenService {
} }
UserModel client = realm.getUser(clientId); UserModel client = realm.getUser(clientId);
if (client == null) { if (client == null) {
logger.warn("Unknown login requester."); logger.warn("Unknown login requester: " + clientId);
oauth.forwardToSecurityFailure("Unknown login requester."); oauth.forwardToSecurityFailure("Unknown login requester.");
transaction.rollback(); transaction.rollback();
return null; return null;

View file

@ -55,7 +55,7 @@ public class EmailSenderTest {
} }
@Test @Test
public void sendMail() throws AddressException, MessagingException, IOException { public void sendMail() throws MessagingException, IOException {
emailSender.send("test@test.com", "Test subject", "Test body"); emailSender.send("test@test.com", "Test subject", "Test body");
MimeMessage[] receivedMessages = greenMail.getReceivedMessages(); MimeMessage[] receivedMessages = greenMail.getReceivedMessages();

View file

@ -77,7 +77,7 @@ public class AdapterTest extends AbstractKeycloakTest {
Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234"); Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
Assert.assertEquals(realmModel.isAutomaticRegistrationAfterSocialLogin(), true); Assert.assertEquals(realmModel.isAutomaticRegistrationAfterSocialLogin(), true);
Assert.assertEquals(1, realmModel.getDefaultRoles().size()); Assert.assertEquals(1, realmModel.getDefaultRoles().size());
Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0).getName()); Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0));
} }
@Test @Test
@ -106,7 +106,7 @@ public class AdapterTest extends AbstractKeycloakTest {
Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234"); Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
Assert.assertEquals(realmModel.isAutomaticRegistrationAfterSocialLogin(), true); Assert.assertEquals(realmModel.isAutomaticRegistrationAfterSocialLogin(), true);
Assert.assertEquals(1, realmModel.getDefaultRoles().size()); Assert.assertEquals(1, realmModel.getDefaultRoles().size());
Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0).getName()); Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0));
String id = realmModel.getId(); String id = realmModel.getId();
System.out.println("id: " + id); System.out.println("id: " + id);

View file

@ -45,6 +45,8 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
application.setName("app-name"); application.setName("app-name");
application.addRole("role-1"); application.addRole("role-1");
application.addRole("role-2"); application.addRole("role-2");
application.addDefaultRole("role-1");
application.addDefaultRole("role-2");
application.getApplicationUser().addRedirectUri("redirect-1"); application.getApplicationUser().addRedirectUri("redirect-1");
application.getApplicationUser().addRedirectUri("redirect-2"); application.getApplicationUser().addRedirectUri("redirect-2");
@ -83,6 +85,7 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
Assert.assertEquals(expected.getName(), actual.getName()); Assert.assertEquals(expected.getName(), actual.getName());
Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl()); Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl());
Assert.assertEquals(expected.getManagementUrl(), actual.getManagementUrl()); Assert.assertEquals(expected.getManagementUrl(), actual.getManagementUrl());
Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
UserModel auser = actual.getApplicationUser(); UserModel auser = actual.getApplicationUser();
UserModel euser = expected.getApplicationUser(); UserModel euser = expected.getApplicationUser();

View file

@ -85,7 +85,7 @@ public class ModelTest extends AbstractKeycloakServerTest {
Assert.assertEquals(expected.getPublicKeyPem(), actual.getPublicKeyPem()); Assert.assertEquals(expected.getPublicKeyPem(), actual.getPublicKeyPem());
Assert.assertEquals(expected.getPrivateKeyPem(), actual.getPrivateKeyPem()); Assert.assertEquals(expected.getPrivateKeyPem(), actual.getPrivateKeyPem());
assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles()); Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
Assert.assertEquals(expected.getSmtpConfig(), actual.getSmtpConfig()); Assert.assertEquals(expected.getSmtpConfig(), actual.getSmtpConfig());
Assert.assertEquals(expected.getSocialConfig(), actual.getSocialConfig()); Assert.assertEquals(expected.getSocialConfig(), actual.getSocialConfig());

View file

@ -28,11 +28,11 @@ import java.util.Map;
*/ */
public class AuthCallback { public class AuthCallback {
private Map<String, Object> attributes; private Map<String, String> attributes;
private Map<String, String[]> queryParams; private Map<String, String[]> queryParams;
public AuthCallback(Map<String, Object> attributes, Map<String, String[]> queryParams) { public AuthCallback(Map<String, String> attributes, Map<String, String[]> queryParams) {
this.attributes = attributes; this.attributes = attributes;
this.queryParams = queryParams; this.queryParams = queryParams;
} }

View file

@ -21,7 +21,9 @@
*/ */
package org.keycloak.social; package org.keycloak.social;
import javax.ws.rs.core.UriBuilder;
import java.net.URI; import java.net.URI;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@ -33,9 +35,17 @@ public class AuthRequest {
private URI authUri; private URI authUri;
private Map<String, Object> attributes; private Map<String, String> attributes;
AuthRequest(String id, URI authUri, Map<String, Object> attributes) { public static AuthRequestBuilder create(String id, String path) {
AuthRequestBuilder req = new AuthRequestBuilder();
req.id = id;
req.b = UriBuilder.fromUri(path);
req.attributes = new HashMap<String, String>();
return req;
}
private AuthRequest(String id, URI authUri, Map<String, String> attributes) {
this.id = id; this.id = id;
this.authUri = authUri; this.authUri = authUri;
this.attributes = attributes; this.attributes = attributes;
@ -49,8 +59,35 @@ public class AuthRequest {
return authUri; return authUri;
} }
public Map<String, Object> getAttributes() { public Map<String, String> getAttributes() {
return attributes; return attributes;
} }
public static class AuthRequestBuilder {
private UriBuilder b;
private Map<String, String> attributes;
private String id;
private AuthRequestBuilder() {
}
public AuthRequestBuilder setQueryParam(String name, String value) {
b.queryParam(name, value);
return this;
}
public AuthRequestBuilder setAttribute(String name, String value) {
attributes.put(name, value);
return this;
}
public AuthRequest build() {
return new AuthRequest(id, b.build(), attributes);
}
}
} }

View file

@ -1,64 +0,0 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.social;
import javax.ws.rs.core.UriBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuthRequestBuilder {
private UriBuilder b;
private Map<String, Object> attributes;
private String id;
private AuthRequestBuilder() {
}
public static AuthRequestBuilder create(String id, String path) {
AuthRequestBuilder req = new AuthRequestBuilder();
req.id = id;
req.b = UriBuilder.fromUri(path);
req.attributes = new HashMap<String, Object>();
return req;
}
public AuthRequestBuilder setQueryParam(String name, String value) {
b.queryParam(name, value);
return this;
}
public AuthRequestBuilder setAttribute(String name, Object value) {
attributes.put(name, value);
return this;
}
public AuthRequest build() {
return new AuthRequest(id, b.build(), attributes);
}
}

View file

@ -21,6 +21,7 @@
*/ */
package org.keycloak.social; package org.keycloak.social;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@ -32,9 +33,27 @@ public class RequestDetails {
private Map<String, String> clientAttributes; private Map<String, String> clientAttributes;
private Map<String, Object> socialAttributes; private Map<String, String> socialAttributes;
RequestDetails(String providerId, Map<String, String> clientAttributes, Map<String, Object> socialAttributes) { public static RequestDetailsBuilder create(String providerId) {
RequestDetailsBuilder req = new RequestDetailsBuilder();
req.providerId = providerId;
req.clientAttributes = new HashMap<String, String>();
req.socialAttributes = new HashMap<String, String>();
return req;
}
public static RequestDetailsBuilder create(RequestDetails from) {
RequestDetailsBuilder req = new RequestDetailsBuilder();
req.providerId = from.getProviderId();
req.clientAttributes = new HashMap<String, String>();
req.clientAttributes.putAll(from.getClientAttributes());
req.socialAttributes = new HashMap<String, String>();
req.socialAttributes.putAll(from.getSocialAttributes());
return req;
}
private RequestDetails(String providerId, Map<String, String> clientAttributes, Map<String, String> socialAttributes) {
this.providerId = providerId; this.providerId = providerId;
this.clientAttributes = clientAttributes; this.clientAttributes = clientAttributes;
this.socialAttributes = socialAttributes; this.socialAttributes = socialAttributes;
@ -52,12 +71,50 @@ public class RequestDetails {
return clientAttributes; return clientAttributes;
} }
public Object getSocialAttribute(String name) { public String getSocialAttribute(String name) {
return socialAttributes.get(name); return socialAttributes.get(name);
} }
public Map<String, Object> getSocialAttributes() { public Map<String, String> getSocialAttributes() {
return socialAttributes; return socialAttributes;
} }
public static class RequestDetailsBuilder {
private String providerId;
private Map<String, String> clientAttributes;
private Map<String, String> socialAttributes;
private RequestDetailsBuilder() {
}
public RequestDetailsBuilder putClientAttribute(String name, String value) {
clientAttributes.put(name, value);
return this;
}
public RequestDetailsBuilder putClientAttributes(Map<String, String> attributes) {
clientAttributes.putAll(attributes);
return this;
}
public RequestDetailsBuilder putSocialAttribute(String name, String value) {
socialAttributes.put(name, value);
return this;
}
public RequestDetailsBuilder putSocialAttributes(Map<String, String> attributes) {
socialAttributes.putAll(attributes);
return this;
}
public RequestDetails build() {
return new RequestDetails(providerId, clientAttributes, socialAttributes);
}
}
} }

View file

@ -1,83 +0,0 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.social;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RequestDetailsBuilder {
private String providerId;
private Map<String, String> clientAttributes;
private Map<String, Object> socialAttributes;
private RequestDetailsBuilder() {
}
public static RequestDetailsBuilder create(String providerId) {
RequestDetailsBuilder req = new RequestDetailsBuilder();
req.providerId = providerId;
req.clientAttributes = new HashMap<String, String>();
req.socialAttributes = new HashMap<String, Object>();
return req;
}
public static RequestDetailsBuilder createFromRequestDetails(RequestDetails from) {
RequestDetailsBuilder req = new RequestDetailsBuilder();
req.providerId = from.getProviderId();
req.clientAttributes = new HashMap<String, String>();
req.clientAttributes.putAll(from.getClientAttributes());
req.socialAttributes = new HashMap<String, Object>();
req.socialAttributes.putAll(from.getSocialAttributes());
return req;
}
public RequestDetailsBuilder putClientAttribute(String name, String value) {
clientAttributes.put(name, value);
return this;
}
public RequestDetailsBuilder putClientAttributes(Map<String, String> attributes) {
clientAttributes.putAll(attributes);
return this;
}
public RequestDetailsBuilder putSocialAttribute(String name, Object value) {
socialAttributes.put(name, value);
return this;
}
public RequestDetailsBuilder putSocialAttributes(Map<String, Object> attributes) {
socialAttributes.putAll(attributes);
return this;
}
public RequestDetails build() {
return new RequestDetails(providerId, clientAttributes, socialAttributes);
}
}

View file

@ -50,8 +50,4 @@ public class SocialProviderConfig {
return secret; return secret;
} }
public void setSecret(String secret) {
this.secret = secret;
}
} }

View file

@ -4,7 +4,6 @@ import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest; import org.keycloak.social.AuthRequest;
import org.keycloak.social.AuthRequestBuilder;
import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialProviderException;
@ -45,13 +44,9 @@ public class FacebookProvider implements SocialProvider {
public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException { public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException {
String state = UUID.randomUUID().toString(); String state = UUID.randomUUID().toString();
AuthRequestBuilder b = AuthRequestBuilder.create(state, AUTHENTICATION_ENDPOINT_URL).setQueryParam("client_id", config.getKey()) return AuthRequest.create(state, AUTHENTICATION_ENDPOINT_URL).setQueryParam("client_id", config.getKey())
.setQueryParam("response_type", DEFAULT_RESPONSE_TYPE).setQueryParam("scope", DEFAULT_SCOPE) .setQueryParam("response_type", DEFAULT_RESPONSE_TYPE).setQueryParam("scope", DEFAULT_SCOPE)
.setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state); .setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state).setAttribute("state", state).build();
b.setAttribute("state", state);
return b.build();
} }
@Override @Override

View file

@ -21,6 +21,15 @@
*/ */
package org.keycloak.social.google; package org.keycloak.social.google;
import java.util.UUID;
import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest;
import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException;
import org.keycloak.social.SocialUser;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
@ -29,15 +38,6 @@ import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.oauth2.Oauth2; import com.google.api.services.oauth2.Oauth2;
import com.google.api.services.oauth2.model.Tokeninfo; import com.google.api.services.oauth2.model.Tokeninfo;
import com.google.api.services.oauth2.model.Userinfo; import com.google.api.services.oauth2.model.Userinfo;
import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest;
import org.keycloak.social.AuthRequestBuilder;
import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException;
import org.keycloak.social.SocialUser;
import java.util.UUID;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -62,14 +62,10 @@ public class GoogleProvider implements SocialProvider {
@Override @Override
public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException { public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException {
String state = UUID.randomUUID().toString(); String state = UUID.randomUUID().toString();
AuthRequestBuilder b = AuthRequestBuilder.create(state, AUTH_PATH).setQueryParam("client_id", config.getKey()) return AuthRequest.create(state, AUTH_PATH).setQueryParam("client_id", config.getKey())
.setQueryParam("response_type", DEFAULT_RESPONSE_TYPE).setQueryParam("scope", DEFAULT_SCOPE) .setQueryParam("response_type", DEFAULT_RESPONSE_TYPE).setQueryParam("scope", DEFAULT_SCOPE)
.setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state); .setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state).setAttribute("state", state).build();
b.setAttribute("state", state);
return b.build();
} }
@Override @Override

View file

@ -23,7 +23,6 @@ package org.keycloak.social.twitter;
import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest; import org.keycloak.social.AuthRequest;
import org.keycloak.social.AuthRequestBuilder;
import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialProviderException;
@ -50,7 +49,7 @@ public class TwitterProvider implements SocialProvider {
RequestToken requestToken = twitter.getOAuthRequestToken(request.getCallbackUrl()); RequestToken requestToken = twitter.getOAuthRequestToken(request.getCallbackUrl());
return AuthRequestBuilder.create(requestToken.getToken(), requestToken.getAuthenticationURL()) return AuthRequest.create(requestToken.getToken(), requestToken.getAuthenticationURL())
.setAttribute("token", requestToken.getToken()).setAttribute("tokenSecret", requestToken.getTokenSecret()) .setAttribute("token", requestToken.getToken()).setAttribute("tokenSecret", requestToken.getTokenSecret())
.build(); .build();
} catch (Exception e) { } catch (Exception e) {

View file

@ -191,6 +191,10 @@
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId> <artifactId>selenium-java</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.mongodb</groupId> <groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId> <artifactId>mongo-java-driver</artifactId>

View file

@ -1,5 +1,7 @@
log4j.rootLogger=debug, stdout log4j.rootLogger=info, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
log4j.logger.org.keycloak=debug

View file

@ -2,7 +2,6 @@ package org.keycloak.testsuite;
import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest; import org.keycloak.social.AuthRequest;
import org.keycloak.social.AuthRequestBuilder;
import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException; import org.keycloak.social.SocialProviderException;
@ -23,12 +22,8 @@ public class DummySocial implements SocialProvider {
public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException { public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException {
String state = UUID.randomUUID().toString(); String state = UUID.randomUUID().toString();
AuthRequestBuilder b = AuthRequestBuilder.create(state, AUTH_PATH).setQueryParam("response_type", "token") return AuthRequest.create(state, AUTH_PATH).setQueryParam("response_type", "token")
.setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state); .setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state).setAttribute("state", state).build();
b.setAttribute("state", state);
return b.build();
} }
@Override @Override

View file

@ -30,10 +30,14 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.jboss.resteasy.jose.Base64Url;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.security.PemUtils; import org.jboss.resteasy.security.PemUtils;
import org.json.JSONObject; import org.json.JSONObject;
import org.junit.Assert; import org.junit.Assert;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken; import org.keycloak.representations.SkeletonKeyToken;
import org.openqa.selenium.By; import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
@ -67,17 +71,21 @@ public class OAuthClient {
private String redirectUri = "http://localhost:8081/app/auth"; private String redirectUri = "http://localhost:8081/app/auth";
private String scope; private SkeletonKeyScope scope;
private String state; private String state;
private PublicKey realmPublicKey; private PublicKey realmPublicKey;
public OAuthClient(WebDriver driver) throws Exception { public OAuthClient(WebDriver driver) {
this.driver = driver; this.driver = driver;
JSONObject realmJson = new JSONObject(IOUtils.toString(getClass().getResourceAsStream("/testrealm.json"))); try {
realmPublicKey = PemUtils.decodePublicKey(realmJson.getString("publicKey")); JSONObject realmJson = new JSONObject(IOUtils.toString(getClass().getResourceAsStream("/testrealm.json")));
realmPublicKey = PemUtils.decodePublicKey(realmJson.getString("publicKey"));
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve realm public key", e);
}
} }
public AuthorizationCodeResponse doLogin(String username, String password) { public AuthorizationCodeResponse doLogin(String username, String password) {
@ -90,7 +98,15 @@ public class OAuthClient {
return new AuthorizationCodeResponse(this); return new AuthorizationCodeResponse(this);
} }
public AccessTokenResponse doAccessTokenRequest(String code, String password) throws Exception { public void doLoginGrant(String username, String password) {
openLoginForm();
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.cssSelector("input[type=\"submit\"]")).click();
}
public AccessTokenResponse doAccessTokenRequest(String code, String password) {
HttpClient client = new DefaultHttpClient(); HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(getAccessTokenUrl()); HttpPost post = new HttpPost(getAccessTokenUrl());
@ -114,27 +130,19 @@ public class OAuthClient {
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, Charset.forName("UTF-8")); UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, Charset.forName("UTF-8"));
post.setEntity(formEntity); post.setEntity(formEntity);
return new AccessTokenResponse(client.execute(post)); try {
return new AccessTokenResponse(client.execute(post));
} catch (Exception e) {
throw new RuntimeException("Failed to retrieve access token", e);
}
} }
public SkeletonKeyToken verifyToken(String token) throws Exception { public SkeletonKeyToken verifyToken(String token) {
return RSATokenVerifier.verifyToken(token, realmPublicKey, realm); try {
} return RSATokenVerifier.verifyToken(token, realmPublicKey, realm);
} catch (VerificationException e) {
public boolean isAuthorizationResponse() { throw new RuntimeException("Failed to verify token", e);
return getCurrentRequest().equals(redirectUri) && getCurrentQuery().containsKey("code"); }
}
public String getState() {
return state;
}
public String getClientId() {
return clientId;
}
public String getResponseType() {
return responseType;
} }
public String getCurrentRequest() { public String getCurrentRequest() {
@ -174,10 +182,6 @@ public class OAuthClient {
return redirectUri; return redirectUri;
} }
public String getScope() {
return scope;
}
public String getLoginFormUrl() { public String getLoginFormUrl() {
UriBuilder b = UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/login"); UriBuilder b = UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/login");
if (responseType != null) { if (responseType != null) {
@ -190,7 +194,12 @@ public class OAuthClient {
b.queryParam("redirect_uri", redirectUri); b.queryParam("redirect_uri", redirectUri);
} }
if (scope != null) { if (scope != null) {
b.queryParam("scope", scope); try {
b.queryParam("scope", Base64Url.encode(JsonSerialization.toByteArray(scope, false)));
} catch (Exception e) {
throw new RuntimeException("Failed to serialize scope", e);
}
} }
if (state != null) { if (state != null) {
b.queryParam("state", state); b.queryParam("state", state);
@ -223,8 +232,11 @@ public class OAuthClient {
return this; return this;
} }
public OAuthClient scope(String scope) { public OAuthClient addScope(String resource, String... roles) {
this.scope = scope; if (scope == null) {
scope = new SkeletonKeyScope();
}
scope.addAll(resource, roles);
return this; return this;
} }

View file

@ -1,111 +0,0 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.testsuite;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.keycloak.PemUtils;
import org.keycloak.RSATokenVerifier;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.servlet.ServletOAuthClient;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.PublicKey;
import java.util.Map;
/**
* @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
*/
public class OAuthGrantServlet extends HttpServlet {
public static ServletOAuthClient client;
private static String baseUrl = Constants.AUTH_SERVER_ROOT + "/rest";
private static String realm = "test";
private static String realmKeyPem = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvg" +
"cwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/" +
"p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
public void init() {
client = new ServletOAuthClient();
client.setClientId("third-party");
client.setPassword("password");
client.setAuthUrl(UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/login").build().toString());
client.setCodeUrl(UriBuilder.fromUri(baseUrl + "/realms/" + realm + "/tokens/access/codes").build().toString());
client.setClient(new ResteasyClientBuilder().build());
client.start();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
PrintWriter pw = resp.getWriter();
// Error "access_denied" happens after clicking on cancel when asked for granting permission
if (req.getParameterValues("error") != null){
pw.print("<html><head><title></title></head><body>Access rights not granted.</body></html>");
// Code is sent as a parameter in case that access was granted
} else if(req.getParameterValues("code") != null) {
String token = client.getBearerToken(req);
pw.print("<html><head><title></title></head><body>Access rights granted.<br/>Token:"+token+"<br/>");
// Check whether the token itself is relevant
try {
PublicKey realmKey = PemUtils.decodePublicKey(realmKeyPem);
SkeletonKeyToken skeletonToken = RSATokenVerifier.verifyToken(token, realmKey, realm);
// Writing app/role information on a page in format which is easy to assert in a test.
pw.print("Role:");
for(String role: skeletonToken.getRealmAccess().getRoles()){
pw.print(role);
}
pw.print(".<br/>");
for(Map.Entry<String, SkeletonKeyToken.Access> entry: skeletonToken.getResourceAccess().entrySet()){
pw.print("App:"+entry.getKey()+";");
for(String role: entry.getValue().getRoles()){
pw.print(role);
}
}
pw.print(".<br/>");
} catch (Exception e){
}
pw.print("</body></html>");
// If no code was sent or error happened, it's 1st visit to servlet and we need to ask for permissions
} else {
client.redirectRelative("", req, resp);
}
pw.flush();
}
}

View file

@ -0,0 +1,245 @@
package org.keycloak.testsuite.account;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.Constants;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.OAuthGrantPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ProfileTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserModel user = appRealm.getUser("test-user@localhost");
user.setFirstName("First");
user.setLastName("Last");
user.setAttribute("key1", "value1");
user.setAttribute("key2", "value2");
ApplicationModel accountApp = appRealm.getApplicationNameMap().get(org.keycloak.models.Constants.ACCOUNT_APPLICATION);
ApplicationModel app = appRealm.getApplicationNameMap().get("test-app");
accountApp.addScopeMapping(app.getApplicationUser(), org.keycloak.models.Constants.ACCOUNT_PROFILE_ROLE);
app.getApplicationUser().addWebOrigin("http://localtest.me:8081");
UserModel thirdParty = appRealm.getUser("third-party");
accountApp.addScopeMapping(thirdParty, org.keycloak.models.Constants.ACCOUNT_PROFILE_ROLE);
}
});
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
@WebResource
protected OAuthClient oauth;
@WebResource
protected AccountUpdateProfilePage profilePage;
@WebResource
protected LoginPage loginPage;
@WebResource
protected OAuthGrantPage grantPage;
private List<String> defaultRoles;
@Test
public void getProfile() throws Exception {
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get("code");
String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
HttpResponse response = doGetProfile(token, null);
assertEquals(200, response.getStatusLine().getStatusCode());
JSONObject profile = new JSONObject(IOUtils.toString(response.getEntity().getContent()));
assertEquals("test-user@localhost", profile.getString("username"));
assertEquals("test-user@localhost", profile.getString("email"));
assertEquals("First", profile.getString("firstName"));
assertEquals("Last", profile.getString("lastName"));
JSONObject attributes = profile.getJSONObject("attributes");
assertEquals(2, attributes.length());
assertEquals("value1", attributes.getString("key1"));
assertEquals("value2", attributes.getString("key2"));
}
@Test
public void getProfileCors() throws Exception {
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get("code");
String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
driver.navigate().to("http://localtest.me:8081/app");
String[] response = doGetProfileJs(token);
assertEquals("200", response[0]);
}
@Test
public void getProfileCorsInvalidOrigin() throws Exception {
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get("code");
String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
driver.navigate().to("http://invalid.localtest.me:8081");
try {
doGetProfileJs(token);
fail("Expected failure");
} catch (Throwable t) {
}
}
@Test
public void getProfileCookieAuth() throws Exception {
profilePage.open();
loginPage.login("test-user@localhost", "password");
String[] response = doGetProfileJs(null);
assertEquals("200", response[0]);
JSONObject profile = new JSONObject(response[1]);
assertEquals("test-user@localhost", profile.getString("username"));
}
@Test
public void getProfileNoAuth() throws Exception {
HttpResponse response = doGetProfile(null, null);
assertEquals(403, response.getStatusLine().getStatusCode());
}
@Test
public void getProfileNoAccess() throws Exception {
try {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ApplicationModel app = appRealm.getApplicationNameMap().get(org.keycloak.models.Constants.ACCOUNT_APPLICATION);
defaultRoles = app.getDefaultRoles();
app.updateDefaultRoles(new String[0]);
}
});
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get("code");
String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
HttpResponse response = doGetProfile(token, null);
assertEquals(403, response.getStatusLine().getStatusCode());
} finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.getApplicationNameMap().get(org.keycloak.models.Constants.ACCOUNT_APPLICATION).updateDefaultRoles((String[]) defaultRoles.toArray(new String[0]));
}
});
}
}
@Test
public void getProfileOAuthClient() throws Exception {
oauth.addScope(org.keycloak.models.Constants.ACCOUNT_APPLICATION, org.keycloak.models.Constants.ACCOUNT_PROFILE_ROLE);
oauth.clientId("third-party");
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.accept();
String token = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get("code"), "password").getAccessToken();
HttpResponse response = doGetProfile(token, null);
assertEquals(200, response.getStatusLine().getStatusCode());
JSONObject profile = new JSONObject(IOUtils.toString(response.getEntity().getContent()));
assertEquals("test-user@localhost", profile.getString("username"));
}
@Test
public void getProfileOAuthClientNoScope() throws Exception {
oauth.addScope(org.keycloak.models.Constants.ACCOUNT_APPLICATION);
oauth.clientId("third-party");
oauth.doLoginGrant("test-user@localhost", "password");
String token = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get("code"), "password").getAccessToken();
HttpResponse response = doGetProfile(token, null);
assertEquals(403, response.getStatusLine().getStatusCode());
}
private URI getAccountURI() {
return UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT + "/rest/realms/" + oauth.getRealm() + "/account").build();
}
private HttpResponse doGetProfile(String token, String origin) throws IOException {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(UriBuilder.fromUri(getAccountURI()).build());
if (token != null) {
get.setHeader(HttpHeaders.AUTHORIZATION, "bearer " + token);
}
if (origin != null) {
get.setHeader("Origin", origin);
}
get.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);
return client.execute(get);
}
private String[] doGetProfileJs(String token) {
StringBuilder sb = new StringBuilder();
sb.append("var req = new XMLHttpRequest();\n");
sb.append("req.open('GET', '" + getAccountURI().toString() + "', false);\n");
if (token != null) {
sb.append("req.setRequestHeader('Authorization', 'Bearer " + token + "');\n");
}
sb.append("req.setRequestHeader('Accept', 'application/json');\n");
sb.append("req.send(null);\n");
sb.append("return req.status + '///' + req.responseText;\n");
JavascriptExecutor js = (JavascriptExecutor) driver;
String response = (String) js.executeScript(sb.toString());
return response.split("///");
}
}

View file

@ -76,7 +76,7 @@ public class RequiredActionMultipleActionsTest {
protected LoginUpdateProfilePage updateProfilePage; protected LoginUpdateProfilePage updateProfilePage;
@Test @Test
public void updateProfileAndPassword() { public void updateProfileAndPassword() throws Exception {
loginPage.open(); loginPage.open();
loginPage.login("test-user@localhost", "password"); loginPage.login("test-user@localhost", "password");

View file

@ -81,7 +81,7 @@ public class RequiredActionResetPasswordTest {
protected LoginPasswordUpdatePage changePasswordPage; protected LoginPasswordUpdatePage changePasswordPage;
@Test @Test
public void tempPassword() { public void tempPassword() throws Exception {
loginPage.open(); loginPage.open();
loginPage.login("test-user@localhost", "password"); loginPage.login("test-user@localhost", "password");

View file

@ -21,29 +21,26 @@
*/ */
package org.keycloak.testsuite.forms; package org.keycloak.testsuite.forms;
import org.junit.After; import org.apache.http.HttpResponse;
import org.junit.Assert; import org.junit.*;
import org.junit.ClassRule; import org.keycloak.models.*;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.TimeBasedOTP; import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AccountPasswordPage; import org.keycloak.testsuite.pages.*;
import org.keycloak.testsuite.pages.AccountTotpPage;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup; import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule; import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import java.util.List;
import static org.junit.Assert.assertEquals;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -77,8 +74,13 @@ public class AccountTest {
@WebResource @WebResource
protected AccountTotpPage totpPage; protected AccountTotpPage totpPage;
@WebResource
protected ErrorPage errorPage;
private TimeBasedOTP totp = new TimeBasedOTP(); private TimeBasedOTP totp = new TimeBasedOTP();
private List<String> defaultRoles;
@After @After
public void after() { public void after() {
keycloakRule.configure(new KeycloakSetup() { keycloakRule.configure(new KeycloakSetup() {
@ -185,4 +187,31 @@ public class AccountTest {
Assert.assertTrue(driver.getPageSource().contains("Remove Google")); Assert.assertTrue(driver.getPageSource().contains("Remove Google"));
} }
@Test
public void changeProfileNoAccess() throws Exception {
try {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ApplicationModel app = appRealm.getApplicationNameMap().get(Constants.ACCOUNT_APPLICATION);
defaultRoles = app.getDefaultRoles();
app.updateDefaultRoles(new String[0]);
}
});
profilePage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(errorPage.isCurrent());
Assert.assertEquals("No access", errorPage.getError());
} finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.getApplicationNameMap().get(org.keycloak.models.Constants.ACCOUNT_APPLICATION).updateDefaultRoles((String[]) defaultRoles.toArray(new String[0]));
}
});
}
}
} }

View file

@ -93,7 +93,7 @@ public class LoginTotpTest {
} }
@Test @Test
public void loginWithTotpFailure() { public void loginWithTotpFailure() throws Exception {
loginPage.open(); loginPage.open();
loginPage.login("test-user@localhost", "password"); loginPage.login("test-user@localhost", "password");
@ -106,7 +106,7 @@ public class LoginTotpTest {
} }
@Test @Test
public void loginWithTotpSuccess() { public void loginWithTotpSuccess() throws Exception {
loginPage.open(); loginPage.open();
loginPage.login("test-user@localhost", "password"); loginPage.login("test-user@localhost", "password");

View file

@ -21,13 +21,16 @@
*/ */
package org.keycloak.testsuite.oauth; package org.keycloak.testsuite.oauth;
import java.io.IOException;
import java.util.Map;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthGrantServlet;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.OAuthGrantPage; import org.keycloak.testsuite.pages.OAuthGrantPage;
import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.KeycloakRule;
@ -35,8 +38,6 @@ import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule; import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import java.io.IOException;
/** /**
* @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a> * @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
*/ */
@ -45,11 +46,6 @@ public class OAuthGrantTest {
@ClassRule @ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(); public static KeycloakRule keycloakRule = new KeycloakRule();
@BeforeClass
public static void before() {
keycloakRule.deployServlet("grant", "/grant", OAuthGrantServlet.class);
}
@Rule @Rule
public WebRule webRule = new WebRule(this); public WebRule webRule = new WebRule(this);
@ -65,28 +61,13 @@ public class OAuthGrantTest {
@WebResource @WebResource
protected OAuthGrantPage grantPage; protected OAuthGrantPage grantPage;
private static String GRANT_APP_URL = "http://localhost:8081/grant/";
private static String ACCESS_GRANTED = "Access rights granted.";
private static String ACCESS_NOT_GRANTED = "Access rights not granted.";
private static String ROLE_USER = "Have User privileges"; private static String ROLE_USER = "Have User privileges";
private static String ROLE_CUSTOMER = "Have Customer User privileges"; private static String ROLE_CUSTOMER = "Have Customer User privileges";
private static String GRANT_ROLE = "user";
private static String GRANT_APP = "test-app";
private static String GRANT_APP_ROLE = "customer-user";
@Test @Test
public void oauthGrantAcceptTest() throws IOException { public void oauthGrantAcceptTest() throws IOException {
oauth.clientId("third-party");
driver.navigate().to(GRANT_APP_URL); oauth.doLoginGrant("test-user@localhost", "password");
Assert.assertFalse(driver.getPageSource().contains(ACCESS_GRANTED));
Assert.assertFalse(driver.getPageSource().contains(ACCESS_NOT_GRANTED));
loginPage.isCurrent();
loginPage.login("test-user@localhost", "password");
grantPage.assertCurrent(); grantPage.assertCurrent();
Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); Assert.assertTrue(driver.getPageSource().contains(ROLE_USER));
@ -94,23 +75,50 @@ public class OAuthGrantTest {
grantPage.accept(); grantPage.accept();
Assert.assertTrue(driver.getPageSource().contains(ACCESS_GRANTED)); Assert.assertTrue(oauth.getCurrentQuery().containsKey("code"));
Assert.assertFalse(driver.getPageSource().contains(ACCESS_NOT_GRANTED)); OAuthClient.AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get("code"), "password");
Assert.assertTrue(driver.getPageSource().contains("Role:"+ GRANT_ROLE +".")); SkeletonKeyToken token = oauth.verifyToken(accessToken.getAccessToken());
Assert.assertTrue(driver.getPageSource().contains("App:"+ GRANT_APP +";"+ GRANT_APP_ROLE +"."));
SkeletonKeyToken.Access realmAccess = token.getRealmAccess();
Assert.assertEquals(1, realmAccess.getRoles().size());
Assert.assertTrue(realmAccess.isUserInRole("user"));
Map<String,SkeletonKeyToken.Access> resourceAccess = token.getResourceAccess();
Assert.assertEquals(1, resourceAccess.size());
Assert.assertEquals(1, resourceAccess.get("test-app").getRoles().size());
Assert.assertTrue(resourceAccess.get("test-app").isUserInRole("customer-user"));
}
@Test
public void oauthGrantAcceptTestWithScope() throws IOException {
oauth.addScope("test-app", "customer-user");
oauth.clientId("third-party");
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER));
grantPage.accept();
Assert.assertTrue(oauth.getCurrentQuery().containsKey("code"));
OAuthClient.AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get("code"), "password");
SkeletonKeyToken token = oauth.verifyToken(accessToken.getAccessToken());
SkeletonKeyToken.Access realmAccess = token.getRealmAccess();
Assert.assertNull(realmAccess);
Map<String,SkeletonKeyToken.Access> resourceAccess = token.getResourceAccess();
Assert.assertEquals(1, resourceAccess.size());
Assert.assertEquals(1, resourceAccess.get("test-app").getRoles().size());
Assert.assertTrue(resourceAccess.get("test-app").isUserInRole("customer-user"));
} }
@Test @Test
public void oauthGrantCancelTest() throws IOException { public void oauthGrantCancelTest() throws IOException {
oauth.clientId("third-party");
driver.navigate().to(GRANT_APP_URL); oauth.doLoginGrant("test-user@localhost", "password");
Assert.assertFalse(driver.getPageSource().contains(ACCESS_GRANTED));
Assert.assertFalse(driver.getPageSource().contains(ACCESS_NOT_GRANTED));
loginPage.isCurrent();
loginPage.login("test-user@localhost", "password");
grantPage.assertCurrent(); grantPage.assertCurrent();
Assert.assertTrue(driver.getPageSource().contains(ROLE_USER)); Assert.assertTrue(driver.getPageSource().contains(ROLE_USER));
@ -118,7 +126,7 @@ public class OAuthGrantTest {
grantPage.cancel(); grantPage.cancel();
Assert.assertFalse(driver.getPageSource().contains(ACCESS_GRANTED)); Assert.assertTrue(oauth.getCurrentQuery().containsKey("error"));
Assert.assertTrue(driver.getPageSource().contains(ACCESS_NOT_GRANTED)); Assert.assertEquals("access_denied", oauth.getCurrentQuery().get("error"));
} }
} }

View file

@ -41,6 +41,6 @@ public abstract class AbstractPage {
abstract boolean isCurrent(); abstract boolean isCurrent();
abstract void open(); abstract void open() throws Exception;
} }

View file

@ -54,6 +54,7 @@ public class WebRule extends ExternalResource {
if (browser.equals("htmlunit")) { if (browser.equals("htmlunit")) {
HtmlUnitDriver d = new HtmlUnitDriver(); HtmlUnitDriver d = new HtmlUnitDriver();
d.getWebClient().getOptions().setJavaScriptEnabled(true);
d.getWebClient().getOptions().setCssEnabled(false); d.getWebClient().getOptions().setCssEnabled(false);
driver = d; driver = d;
} else if (browser.equals("chrome")) { } else if (browser.equals("chrome")) {

View file

@ -78,13 +78,6 @@ public class CreateUsersWorker implements Worker {
user.setEmail(username + "@email.com"); user.setEmail(username + "@email.com");
} }
// Adding default roles of realm to user
if (addDefaultRoles) {
for (RoleModel role : realm.getDefaultRoles()) {
realm.grantRole(user, role);
}
}
// Creating password (will be same as username) // Creating password (will be same as username)
if (addPassword) { if (addPassword) {
UserCredentialModel password = new UserCredentialModel(); UserCredentialModel password = new UserCredentialModel();