keycloak-aplcache

KEYCLOAK-2593 Character set missing from responses and no

3/11/2016 11:25:21 AM

Details

diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java
index ec8137e..6196a29 100755
--- a/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java
+++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java
@@ -26,7 +26,7 @@ public interface MigrationModel {
     /**
      * Must have the form of major.minor.micro as the version is parsed and numbers are compared
      */
-    String LATEST_VERSION = "1.9.0";
+    String LATEST_VERSION = "1.9.2";
 
     String getStoredVersion();
     void setStoredVersion(String version);
diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java
index 9b1442c..7a29804 100755
--- a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java
+++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java
@@ -25,6 +25,7 @@ import org.keycloak.migration.migrators.MigrateTo1_6_0;
 import org.keycloak.migration.migrators.MigrateTo1_7_0;
 import org.keycloak.migration.migrators.MigrateTo1_8_0;
 import org.keycloak.migration.migrators.MigrateTo1_9_0;
+import org.keycloak.migration.migrators.MigrateTo1_9_2;
 import org.keycloak.migration.migrators.MigrationTo1_2_0_CR1;
 import org.keycloak.models.KeycloakSession;
 
@@ -92,6 +93,12 @@ public class MigrationModelManager {
             }
             new MigrateTo1_9_0().migrate(session);
         }
+        if (stored == null || stored.lessThan(MigrateTo1_9_2.VERSION)) {
+            if (stored != null) {
+                logger.debug("Migrating older model to 1.9.2 updates");
+            }
+            new MigrateTo1_9_2().migrate(session);
+        }
 
         model.setStoredVersion(MigrationModel.LATEST_VERSION);
     }
diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java
new file mode 100644
index 0000000..7c1f097
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.migration.migrators;
+
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.BrowserSecurityHeaders;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+
+public class MigrateTo1_9_2 {
+
+    public static final ModelVersion VERSION = new ModelVersion("1.9.2");
+
+    public void migrate(KeycloakSession session) {
+        for (RealmModel realm : session.realms().getRealms()) {
+            if (realm.getBrowserSecurityHeaders() != null) {
+                realm.getBrowserSecurityHeaders().put("xFrameOptions", "nosniff");
+            }
+        }
+    }
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java b/server-spi/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
index 4abc494..1056027 100755
--- a/server-spi/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
+++ b/server-spi/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
@@ -30,13 +30,15 @@ public class BrowserSecurityHeaders {
     public static final Map<String, String> defaultHeaders;
 
     static {
-        Map<String, String> headerMap = new HashMap<String, String>();
+        Map<String, String> headerMap = new HashMap<>();
         headerMap.put("xFrameOptions", "X-Frame-Options");
         headerMap.put("contentSecurityPolicy", "Content-Security-Policy");
+        headerMap.put("xContentTypeOptions", "X-Content-Type-Options");
 
-        Map<String, String> dh = new HashMap<String, String>();
+        Map<String, String> dh = new HashMap<>();
         dh.put("xFrameOptions", "SAMEORIGIN");
         dh.put("contentSecurityPolicy", "frame-src 'self'");
+        dh.put("xContentTypeOptions", "nosniff");
 
         defaultHeaders = Collections.unmodifiableMap(dh);
         headerAttributeMap = Collections.unmodifiableMap(headerMap);
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
index fd70124..893e816 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
@@ -27,7 +27,6 @@ import java.util.Map;
 import java.util.Properties;
 
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -64,6 +63,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.FormMessage;
+import org.keycloak.utils.MediaType;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -209,7 +209,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
         try {
             String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme);
-            Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result);
+            Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML_UTF_8_TYPE).language(locale).entity(result);
             BrowserSecurityHeaderSetup.headers(builder, realm);
             return builder.build();
         } catch (FreeMarkerException e) {
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
index bb2e518..1a81870 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -61,8 +61,8 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.services.Urls;
 import org.keycloak.services.messages.Messages;
+import org.keycloak.utils.MediaType;
 
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -312,7 +312,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
 
         try {
             String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme);
-            Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result);
+            Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML_UTF_8).entity(result);
             BrowserSecurityHeaderSetup.headers(builder, realm);
             for (Map.Entry<String, String> entry : httpResponseHeaders.entrySet()) {
                 builder.header(entry.getKey(), entry.getValue());
@@ -413,7 +413,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
         }
         try {
             String result = freeMarker.processTemplate(attributes, form, theme);
-            Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result);
+            Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML_UTF_8_TYPE).language(locale).entity(result);
             BrowserSecurityHeaderSetup.headers(builder, realm);
             for (Map.Entry<String, String> entry : httpResponseHeaders.entrySet()) {
                 builder.header(entry.getKey(), entry.getValue());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
index ba7c83a..9ed474b 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
@@ -42,14 +42,13 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.resources.KeycloakApplication;
 import org.keycloak.services.Urls;
+import org.keycloak.utils.MediaType;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Providers;
@@ -296,7 +295,7 @@ public class AdminConsole {
 
             FreeMarkerUtil freeMarkerUtil = new FreeMarkerUtil();
             String result = freeMarkerUtil.processTemplate(map, "index.ftl", theme);
-            Response.ResponseBuilder builder = Response.status(Response.Status.OK).type(MediaType.TEXT_HTML).entity(result);
+            Response.ResponseBuilder builder = Response.status(Response.Status.OK).type(MediaType.TEXT_HTML_UTF_8).language(Locale.ENGLISH).entity(result);
             BrowserSecurityHeaderSetup.headers(builder, realm);
             return builder.build();
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
index e2ee402..23e9baa 100755
--- a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
@@ -17,17 +17,27 @@
 package org.keycloak.services.resources;
 
 import org.keycloak.Config;
-import org.keycloak.theme.FreeMarkerUtil;
-import org.keycloak.theme.Theme;
-import org.keycloak.theme.ThemeProvider;
-import org.keycloak.models.KeycloakSession;
 import org.keycloak.common.util.MimeTypeUtil;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.managers.ApplianceBootstrap;
 import org.keycloak.services.util.CacheControlUtil;
-
-import javax.ws.rs.*;
-import javax.ws.rs.core.*;
+import org.keycloak.theme.FreeMarkerUtil;
+import org.keycloak.theme.Theme;
+import org.keycloak.theme.ThemeProvider;
+import org.keycloak.utils.MediaType;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.InetAddress;
@@ -64,7 +74,7 @@ public class WelcomeResource {
      * @throws URISyntaxException
      */
     @GET
-    @Produces("text/html")
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     public Response getWelcomePage() throws URISyntaxException {
         checkBootstrap();
 
@@ -127,7 +137,7 @@ public class WelcomeResource {
      */
     @GET
     @Path("/welcome-content/{path}")
-    @Produces("text/html")
+    @Produces(MediaType.TEXT_HTML_UTF_8)
     public Response getResource(@PathParam("path") String path) {
         try {
             InputStream resource = getTheme().getResourceAsStream(path);
diff --git a/services/src/main/java/org/keycloak/theme/BrowserSecurityHeaderSetup.java b/services/src/main/java/org/keycloak/theme/BrowserSecurityHeaderSetup.java
index 41fc584..dfcbf50 100755
--- a/services/src/main/java/org/keycloak/theme/BrowserSecurityHeaderSetup.java
+++ b/services/src/main/java/org/keycloak/theme/BrowserSecurityHeaderSetup.java
@@ -32,8 +32,9 @@ public class BrowserSecurityHeaderSetup {
     public static Response.ResponseBuilder headers(Response.ResponseBuilder builder, RealmModel realm) {
         for (Map.Entry<String, String> entry : realm.getBrowserSecurityHeaders().entrySet()) {
             String headerName = BrowserSecurityHeaders.headerAttributeMap.get(entry.getKey());
-            if (headerName == null) continue;
-            builder.header(headerName, entry.getValue());
+            if (headerName != null && entry.getValue() != null && entry.getValue().length() > 0) {
+                builder.header(headerName, entry.getValue());
+            }
         }
         return builder;
     }
diff --git a/services/src/main/java/org/keycloak/utils/MediaType.java b/services/src/main/java/org/keycloak/utils/MediaType.java
new file mode 100644
index 0000000..31ab972
--- /dev/null
+++ b/services/src/main/java/org/keycloak/utils/MediaType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.utils;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MediaType {
+
+    public static final String TEXT_HTML_UTF_8 = "text/html; charset=utf-8";
+    public static final javax.ws.rs.core.MediaType TEXT_HTML_UTF_8_TYPE = new javax.ws.rs.core.MediaType("text", "html", "utf-8");
+
+    public static final String APPLICATION_JSON = javax.ws.rs.core.MediaType.APPLICATION_JSON;
+    public static final javax.ws.rs.core.MediaType APPLICATION_JSON_TYPE = javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+
+    public static final String APPLICATION_FORM_URLENCODED = javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
+    public static final javax.ws.rs.core.MediaType APPLICATION_FORM_URLENCODED_TYPE = javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE;
+
+}
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 0a0cfc5..a86792a 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -95,8 +95,11 @@ login-action-timeout.tooltip=Max time a user has to complete login related actio
 headers=Headers
 brute-force-detection=Brute Force Detection
 x-frame-options=X-Frame-Options
-click-label-for-info=Click on label link for more information. The default value prevents pages from being included via non-origin iframes.
+x-frame-options-tooltip=Default value prevents pages from being included via non-origin iframes (click label for more information)
 content-sec-policy=Content-Security-Policy
+content-sec-policy-tooltip=Default value prevents pages from being included via non-origin iframes (click label for more information)
+content-type-options=X-Content-Type-Options
+content-type-options-tooltip=Default value prevents Internet Explorer and Google Chrome from MIME-sniffing a response away from the declared content-type (click label for more information)
 max-login-failures=Max Login Failures
 max-login-failures.tooltip=How many failures before wait is triggered.
 wait-increment=Wait Increment
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html b/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html
index aa1dc4e..1dc08a1 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html
@@ -9,18 +9,25 @@
     <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
         <fieldset class="border-top">
             <div class="form-group">
-                <label class="col-md-2 control-label" for="xFrameOptions"><a href="http://tools.ietf.org/html/rfc7034">{{:: 'x-frame-options' | translate}}</a></label>
+                <label class="col-md-2 control-label" for="xFrameOptions"><a href="http://tools.ietf.org/html/rfc7034" target="_blank">{{:: 'x-frame-options' | translate}}</a></label>
                 <div class="col-sm-6">
                     <input class="form-control" id="xFrameOptions" type="text" ng-model="realm.browserSecurityHeaders.xFrameOptions">
                 </div>
-                <kc-tooltip>{{:: 'click-label-for-info' | translate}}</kc-tooltip>
+                <kc-tooltip>{{:: 'x-frame-options-tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
-                <label class="col-md-2 control-label" for="contentSecurityPolicy"><a href="http://www.w3.org/TR/CSP/">{{:: 'content-sec-policy' | translate}}</a></label>
+                <label class="col-md-2 control-label" for="contentSecurityPolicy"><a href="http://www.w3.org/TR/CSP/" target="_blank">{{:: 'content-sec-policy' | translate}}</a></label>
                 <div class="col-sm-6">
                     <input class="form-control" id="contentSecurityPolicy" type="text" ng-model="realm.browserSecurityHeaders.contentSecurityPolicy">
                 </div>
-                <kc-tooltip>{{:: 'click-label-for-info' | translate}}</kc-tooltip>
+                <kc-tooltip>{{:: 'content-sec-policy-tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="xContentTypeOptions"><a href="https://www.owasp.org/index.php/List_of_useful_HTTP_headers">{{:: 'content-type-options' | translate}}</a></label>
+                <div class="col-sm-6">
+                    <input class="form-control" id="xContentTypeOptions" type="text" ng-model="realm.browserSecurityHeaders.xContentTypeOptions">
+                </div>
+                <kc-tooltip>{{:: 'content-type-options-tooltip' | translate}}</kc-tooltip>
             </div>
         </fieldset>
         <div class="form-group" data-ng-show="access.manageRealm">