keycloak-uncached

Details

diff --git a/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
index 1b10128..7bddd17 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java
@@ -33,6 +33,7 @@ public class BrowserSecurityHeaders {
         Map<String, String> headerMap = new HashMap<>();
         headerMap.put("xFrameOptions", "X-Frame-Options");
         headerMap.put("contentSecurityPolicy", "Content-Security-Policy");
+        headerMap.put("contentSecurityPolicyReportOnly", "Content-Security-Policy-Report-Only");
         headerMap.put("xContentTypeOptions", "X-Content-Type-Options");
         headerMap.put("xRobotsTag", "X-Robots-Tag");
         headerMap.put("xXSSProtection", "X-XSS-Protection");
@@ -41,6 +42,7 @@ public class BrowserSecurityHeaders {
         Map<String, String> dh = new HashMap<>();
         dh.put("xFrameOptions", "SAMEORIGIN");
         dh.put("contentSecurityPolicy", "frame-src 'self'; frame-ancestors 'self'; object-src 'none';");
+        dh.put("contentSecurityPolicyReportOnly", "");
         dh.put("xContentTypeOptions", "nosniff");
         dh.put("xRobotsTag", "none");
         dh.put("xXSSProtection", "1; mode=block");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index 5320783..bdf19c7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -120,13 +120,41 @@ public class LoginTest extends AbstractTestRealmKeycloakTest {
         for (Map.Entry<String, String> entry : BrowserSecurityHeaders.defaultHeaders.entrySet()) {
             String headerName = BrowserSecurityHeaders.headerAttributeMap.get(entry.getKey());
             String headerValue = response.getHeaderString(headerName);
-            Assert.assertNotNull(headerValue);
-            Assert.assertThat(headerValue, is(equalTo(entry.getValue())));
+            if (entry.getValue().isEmpty()) {
+                Assert.assertNull(headerValue);
+            } else {
+                Assert.assertNotNull(headerValue);
+                Assert.assertThat(headerValue, is(equalTo(entry.getValue())));
+            }
         }
         response.close();
         client.close();
     }
 
+    @Test
+    public void testContentSecurityPolicyReportOnlyBrowserSecurityHeader() {
+        final String expectedCspReportOnlyValue = "default-src 'none'";
+        final String cspReportOnlyAttr = "contentSecurityPolicyReportOnly";
+        final String cspReportOnlyHeader = "Content-Security-Policy-Report-Only";
+
+        RealmRepresentation realmRep = adminClient.realm("test").toRepresentation();
+        final String defaultContentSecurityPolicyReportOnly = realmRep.getBrowserSecurityHeaders().get(cspReportOnlyAttr);
+        realmRep.getBrowserSecurityHeaders().put(cspReportOnlyAttr, expectedCspReportOnlyValue);
+        adminClient.realm("test").update(realmRep);
+
+        try {
+            Client client = ClientBuilder.newClient();
+            Response response = client.target(oauth.getLoginFormUrl()).request().get();
+            String headerValue = response.getHeaderString(cspReportOnlyHeader);
+            Assert.assertThat(headerValue, is(equalTo(expectedCspReportOnlyValue)));
+            response.close();
+            client.close();
+        } finally {
+            realmRep.getBrowserSecurityHeaders().put(cspReportOnlyAttr, defaultContentSecurityPolicyReportOnly);
+            adminClient.realm("test").update(realmRep);
+        }
+    }
+
     //KEYCLOAK-5556
     @Test
     public void testPOSTAuthenticationRequest() {
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 8eb6ead..28f098e 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
@@ -146,6 +146,8 @@ x-frame-options=X-Frame-Options
 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-sec-policy-report-only=Content-Security-Policy-Report-Only
+content-sec-policy-report-only-tooltip=For testing Content Security Policies
 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)
 robots-tag=X-Robots-Tag
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 0a8ffbd..51acdf1 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
@@ -23,6 +23,13 @@
                 <kc-tooltip>{{:: 'content-sec-policy-tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
+                <label class="col-md-2 control-label" for="contentSecurityPolicyReportOnly"><a href="http://www.w3.org/TR/CSP/" target="_blank">{{:: 'content-sec-policy-report-only' | translate}}</a></label>
+                <div class="col-sm-6">
+                    <input class="form-control" id="contentSecurityPolicyReportOnly" type="text" ng-model="realm.browserSecurityHeaders.contentSecurityPolicyReportOnly">
+                </div>
+                <kc-tooltip>{{:: 'content-sec-policy-report-only-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" target="_blank">{{:: 'content-type-options' | translate}}</a></label>
                 <div class="col-sm-6">
                     <input class="form-control" id="xContentTypeOptions" type="text" ng-model="realm.browserSecurityHeaders.xContentTypeOptions">
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties
index c1d777a..3731b9a 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties
@@ -100,6 +100,8 @@ x-frame-options=Alternativer for X-Frame
 x-frame-options-tooltip=Standardverdi hindrer sider fra \u00E5 bli inkludert via non-origin iframes. (Klikk p\u00E5 etikett for mer informasjon)
 content-sec-policy=Sikkerhetspolicy for innhold
 content-sec-policy-tooltip=Standardverdi hindrer sider fra \u00E5 bli inkludert via non-origin iframes. (Klikk p\u00E5 etikett for mer informasjon)
+content-sec-policy-report-only=Rapporterende sikkerhetspolicy for innhold
+content-sec-policy-report-only-tooltip=Verdi for Content-Security-Policy-Report-Only header. Til bruk for testing av ny sikkerhetspolicy.
 content-type-options=Alternativer for X-innholdstyper
 content-type-options-tooltip=Standardverdi som forhindrer Internet Explorer og Google Chrome fra \u00E5 MIME-sniffe et svar vekk fra den deklarerte innholdstypen (content-type) (Klikk p\u00E5 etikett for mer informasjon)
 max-login-failures=Maksimum antall innloggingsfeil