diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js index e6fc2d7388..bfb76f7e70 100755 --- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js +++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js @@ -668,12 +668,13 @@ module.directive('onoffswitch', function() { name: '@', id: '@', ngModel: '=', + ngDisabled: '=', kcOnText: '@onText', kcOffText: '@offText' }, // TODO - The same code acts differently when put into the templateURL. Find why and move the code there. //templateUrl: "templates/kc-switch.html", - template: "
", + template: "
", compile: function(element, attrs) { /* We don't want to propagate basic attributes to the root element of directive. Id should be passed to the @@ -834,20 +835,38 @@ module.directive('kcDropdown', function ($compile, Notifications) { }); module.directive('kcReadOnly', function() { + var disabled = {}; + var d = { replace : false, link : function(scope, element, attrs) { - scope.$watch(attrs.kcReadOnly, function(readOnly, oldValue) { + var disable = function(i, e) { + if (!e.disabled) { + disabled[e.tagName + i] = true; + e.disabled = true; + } + } + + var enable = function(i, e) { + if (disabled[e.tagName + i]) { + e.disabled = false; + delete disabled[i]; + } + } + + scope.$watch(attrs.kcReadOnly, function(readOnly) { if (readOnly) { - element.find('input').attr('disabled', 'disabled'); - element.find('button').attr('disabled', 'disabled'); - element.find('select').attr('disabled', 'disabled'); - element.find('textarea').attr('disabled', 'disabled'); + console.debug('readonly'); + element.find('input').each(disable); + element.find('button').each(disable); + element.find('select').each(disable); + element.find('textarea').each(disable); } else { - element.find('input').removeAttr('disabled'); - element.find('button').removeAttr('disabled'); - element.find('select').removeAttr('disabled'); - element.find('textarea').removeAttr('disabled'); + element.find('input').each(enable); + element.find('input').each(enable); + element.find('button').each(enable); + element.find('select').each(enable); + element.find('textarea').each(enable); } }); } diff --git a/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css b/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css index f84516d035..e4f64c8716 100644 --- a/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css +++ b/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css @@ -47,7 +47,6 @@ #kc-login { float: right; margin-left: 10px; - margin-bottom: 10px; } #kc-feedback-wrapper { @@ -210,19 +209,19 @@ ol#kc-totp-settings li:first-of-type { height: 37px; - margin: 20px; + margin: 15px; } #kc-header { - padding-left: 40px; - padding-right: 40px; + padding-left: 15px; + padding-right: 15px; white-space: normal; float: none; } #kc-feedback { - padding-left: 40px; - padding-right: 40px; + padding-left: 15px; + padding-right: 15px; float: none; } @@ -232,21 +231,26 @@ ol#kc-totp-settings li:first-of-type { } #kc-form { - padding-left: 40px; - padding-right: 40px; + padding-left: 15px; + padding-right: 15px; float: none; } #kc-info-wrapper { border-top: 1px solid rgba(255, 255, 255, 0.1); - margin-top: 20px; - padding-top: 20px; - padding-left: 20px; - padding-right: 40px; + margin-top: 15px; + padding-top: 15px; + padding-left: 0px; + padding-right: 15px; } #kc-social-providers li { display: inline-block; margin-right: 5px; } + + .login-pf .container { + padding-top: 15px; + padding-bottom: 15px; + } } \ No newline at end of file diff --git a/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties b/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties index aba1e9bf25..720d4eab88 100644 --- a/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties +++ b/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties @@ -19,7 +19,7 @@ kcLabelClass=control-label kcLabelWrapperClass=col-xs-12 col-sm-12 col-md-4 col-lg-3 kcInputClass=form-control kcInputWrapperClass=col-xs-12 col-sm-12 col-md-8 col-lg-9 -kcFormOptionsClass=col-xs-5 col-sm-5 col-md-offset-4 col-md-4 col-lg-offset-3 col-lg-5 -kcFormButtonsClass=col-xs-7 col-sm-7 col-md-4 col-lg-4 submit +kcFormOptionsClass=col-xs-4 col-sm-5 col-md-offset-4 col-md-4 col-lg-offset-3 col-lg-5 +kcFormButtonsClass=col-xs-8 col-sm-7 col-md-4 col-lg-4 submit kcInfoAreaClass=col-xs-12 col-sm-4 col-md-4 col-lg-6 details \ No newline at end of file diff --git a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js index dd49966b8d..336974bfce 100755 --- a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js +++ b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js @@ -38,9 +38,9 @@ var Keycloak = function (options) { delete sessionStorage.oauthToken; processCallback(successCallback, errorCallback); } else if (options.token) { - setToken(options.token, successCallback); + kc.setToken(options.token, successCallback); } else if (sessionStorage.oauthToken) { - setToken(sessionStorage.oauthToken, successCallback); + kc.setToken(sessionStorage.oauthToken, successCallback); } else if (options.onload) { switch (options.onload) { case 'login-required' : @@ -58,7 +58,7 @@ var Keycloak = function (options) { } kc.logout = function () { - setToken(undefined); + kc.setToken(undefined); window.location.href = kc.createLogoutUrl(); } @@ -164,8 +164,10 @@ var Keycloak = function (options) { var url = kc.getRealmUrl() + '/tokens/access/codes'; var req = new XMLHttpRequest(); - req.open('POST', url, true, options.clientId, options.clientSecret); + req.open('POST', url, true); req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + req.setRequestHeader('Authorization', 'Basic ' + btoa(options.clientId + ':' + options.clientSecret)); + req.withCredentials = true; req.onreadystatechange = function () { if (req.readyState == 4) { @@ -197,12 +199,12 @@ var Keycloak = function (options) { kc.tokenParsed = JSON.parse(atob(token.split('.')[1])); kc.authenticated = true; - kc.username = kc.tokenParsed.sub; + kc.subject = kc.tokenParsed.sub; kc.realmAccess = kc.tokenParsed.realm_access; kc.resourceAccess = kc.tokenParsed.resource_access; setTimeout(function() { - successCallback && successCallback({ authenticated: kc.authenticated, username: kc.username }); + successCallback && successCallback({ authenticated: kc.authenticated, subject: kc.subject }); }, 0); } else { delete sessionStorage.oauthToken; diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index 16db7d0310..6784de8c99 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -38,6 +38,7 @@ import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.NotAcceptableException; import javax.ws.rs.NotAuthorizedException; +import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; @@ -343,6 +344,13 @@ public class TokenService { return processLogin(clientId, scopeParam, state, redirect, formData); } + @Path("access/codes") + @OPTIONS + @Produces("application/json") + public Response accessCodeToTokenPreflight() { + return Cors.add(request, Response.ok()).auth().preflight().build(); + } + @Path("access/codes") @POST @Produces("application/json") @@ -418,7 +426,7 @@ public class TokenService { .generateIDToken() .generateRefreshToken().build(); - return Cors.add(request, Response.ok(res)).allowedOrigins(client).allowedMethods("POST").build(); + return Cors.add(request, Response.ok(res)).auth().allowedOrigins(client).allowedMethods("POST").build(); } protected ClientModel authorizeClient(String authorizationHeader) {