keycloak-aplcache

Merge pull request #795 from patriot1burke/master force

10/22/2014 5:19:02 PM

Details

diff --git a/docbook/reference/en/en-US/master.xml b/docbook/reference/en/en-US/master.xml
index 3487d3c..751436c 100755
--- a/docbook/reference/en/en-US/master.xml
+++ b/docbook/reference/en/en-US/master.xml
@@ -13,6 +13,7 @@
                 <!ENTITY JavascriptAdapter SYSTEM "modules/javascript-adapter.xml">
                 <!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
                 <!ENTITY Logout SYSTEM "modules/logout.xml">
+                <!ENTITY SAML SYSTEM "modules/saml.xml">
                 <!ENTITY SocialConfig SYSTEM "modules/social-config.xml">
                 <!ENTITY SocialFacebook SYSTEM "modules/social-facebook.xml">
                 <!ENTITY SocialGitHub SYSTEM "modules/social-github.xml">
@@ -121,6 +122,7 @@ This one is short
     &UserFederation;
     &ExportImport;
     &ServerCache;
+    &SAML;
     &SecurityVulnerabilities;
     &Clustering;
     &Migration;
diff --git a/docbook/reference/en/en-US/modules/Overview.xml b/docbook/reference/en/en-US/modules/Overview.xml
index d14169e..3361e0d 100755
--- a/docbook/reference/en/en-US/modules/Overview.xml
+++ b/docbook/reference/en/en-US/modules/Overview.xml
@@ -74,6 +74,9 @@
                 OpenID Connect Support.
             </listitem>
             <listitem>
+                SAML Support.
+            </listitem>
+            <listitem>
                 CORS Support
             </listitem>
             <listitem>
@@ -89,13 +92,13 @@
                 Account Management console that allows users to manage their own account, view their open sessions, reset passwords, etc.
             </listitem>
             <listitem>
-                Deployable as a WAR, appliance, or on Openshift.
+                Deployable as a WAR, appliance, or on Openshift.  Completely clusterable.
             </listitem>
             <listitem>
                 Multitenancy support.  You can host and manage multiple realms for multiple organizations.
             </listitem>
             <listitem>
-                Supports JBoss AS7, EAP 6.x, Wildfly and JavaScript applications.  Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments
+                Supports JBoss AS7, EAP 6.x, Wildfly and Pure JavaScript applications.  Plans to support Node.js, RAILS, GRAILS, and other non-Java deployments
             </listitem>
         </itemizedlist>
     </para>
diff --git a/docbook/reference/en/en-US/modules/saml.xml b/docbook/reference/en/en-US/modules/saml.xml
new file mode 100755
index 0000000..1f6a1a3
--- /dev/null
+++ b/docbook/reference/en/en-US/modules/saml.xml
@@ -0,0 +1,97 @@
+<chapter id="saml">
+    <title>SAML SSO</title>
+    <para>
+        Keycloak supports SAML 2.0 for registered applications.  Both POST and Redirect bindings are supported.  You can choose
+        to require client signature validation and can have the server sign and/or encrypt responses as well.  We do not
+        yet support logout via redirects.  All logouts happen via a background POST binding request to the application
+        that will be logged out.  We do not support SAML 1.1 either.  If you want support for either of those, please
+        log a JIRA request and we'll schedule it.
+    </para>
+    <para>
+        When you create an application in the admin console, you can choose which protocol the application will log in with.
+        In the application create screen, choose <literal>saml</literal> from the protocol list.  After that there
+        are a bunch of configuration options.  Here is a description of each item:
+    </para>
+    <para>
+        <variablelist>
+            <varlistentry>
+                <term>Include AuthnStatement</term>
+                <listitem>
+                    <para>
+                        SAML login responses may specify the authenticaiton method used (password, etc.) as well as
+                        a timestamp of the login.  Setting this to on will include that statement in the response document.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Multi-valued Roles</term>
+                <listitem>
+                    <para>
+                        If this switch is off, any user role mapings will have a corresponding attribute created for it.
+                        If this switch is turn on, only one role attribute will be created, but it will have
+                        multiple values within in.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Sign Documents</term>
+                <listitem>
+                    <para>
+                        When turned on, Keycloak will sign the document using the realm's private key.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Sign Assertions</term>
+                <listitem>
+                    <para>
+                        With the <literal>Sign Documents</literal> switch signs the whole document.  With this setting
+                        you just assign the assertions of the document.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Signature Algorithm</term>
+                <listitem>
+                    <para>
+                        Choose between a variety of algorithms for signing SAML documents.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Encrypt Assertions</term>
+                <listitem>
+                    <para>
+                        Encrypt assertions in SAML documents with the realm's private key.  The AES algorithm is used
+                        with a key size of 128 bits.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Client Signature Required</term>
+                <listitem>
+                    <para>
+                        Expect that documents coming from a client are signed.  Keycloak will validate this signature
+                        using the client keys set up in the <literal>Application Keys</literal> submenu item.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>Force POST Binding</term>
+                <listitem>
+                    <para>
+                        By default, Keycloak will respond using the initial SAML binding of the original request.  By turning
+                        on this switch, you will force Keycloak to always respond using the SAML POST Binding even if the
+                        original request was a the Redirect binding.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </para>
+    <para>
+        One thing to note is that roles are not treated as a hierarchy.  So, any role mappings will just be added
+        to the role attributes in the SAML document using their basic name.  So, if you have multiple applicaiton roles
+        you might have name collisions.  You can use the Scope Mapping menu item to control which role mappings are set
+        in the response.
+    </para>
+</chapter>
diff --git a/docbook/reference/en/en-US/modules/security-vulnerabilities.xml b/docbook/reference/en/en-US/modules/security-vulnerabilities.xml
index 246cfd0..49a2988 100755
--- a/docbook/reference/en/en-US/modules/security-vulnerabilities.xml
+++ b/docbook/reference/en/en-US/modules/security-vulnerabilities.xml
@@ -152,4 +152,13 @@
             At this point in time, there is no knowledge of any SQL injection vulnerabilities in Keycloak
         </para>
     </section>
+    <section>
+        <title>Limiting Scope</title>
+        <para>
+            Using the <literal>Scope</literal> menu in the admin console for oauth clients or applications, you can control
+            exactly which role mappings will be included within the token sent back to the client or application.  This
+            allows you to limit the scope of permissions given to the application or client which is great if the client isn't
+            very trusted and is known to not being very careful with its tokens.
+        </para>
+    </section>
 </chapter>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index c96722e..e1efcba 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -363,6 +363,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
     $scope.samlAssertionSignature = false;
     $scope.samlClientSignature = false;
     $scope.samlEncrypt = false;
+    $scope.samlForcePostBinding = false;
     if (!$scope.create) {
         if (!application.attributes) {
             application.attributes = {};
@@ -442,6 +443,13 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
             $scope.samlMultiValuedRoles = false;
         }
     }
+    if ($scope.application.attributes["saml.force.post.binding"]) {
+        if ($scope.application.attributes["saml.force.post.binding"] == "true") {
+            $scope.samlForcePostBinding = true;
+        } else {
+            $scope.samlForcePostBinding = false;
+        }
+    }
 
     $scope.switchChange = function() {
         $scope.changed = true;
@@ -535,6 +543,12 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
             $scope.application.attributes["saml.multivalued.roles"] = "false";
 
         }
+        if ($scope.samlForcePostBinding == true) {
+            $scope.application.attributes["saml.force.post.binding"] = "true";
+        } else {
+            $scope.application.attributes["saml.force.post.binding"] = "false";
+
+        }
 
         $scope.application.protocol = $scope.protocol;
         $scope.application.attributes['saml.signature.algorithm'] = $scope.signatureAlgorithm;
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
index 9af7c91..165e57d 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
@@ -111,6 +111,13 @@
                     </div>
                     <span tooltip-placement="right" tooltip="Will the client sign their saml requests and responses?  And should they be validated?" class="fa fa-info-circle"></span>
                 </div>
+                <div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
+                    <label class="col-sm-2 control-label" for="samlForcePostBinding">Force POST Binding</label>
+                    <div class="col-sm-6">
+                        <input ng-model="samlForcePostBinding" ng-click="switchChange()" name="samlForcePostBinding" id="samlForcePostBinding" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Always use POST binding for responses." class="fa fa-info-circle"></span>
+                </div>
 
                 <div class="form-group" data-ng-show="!application.bearerOnly">
                     <label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 44c88e3..99b7861 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -39,6 +39,14 @@ public class SamlProtocol implements LoginProtocol {
     public static final String SAML_BINDING = "saml_binding";
     public static final String SAML_POST_BINDING = "post";
     public static final String SAML_GET_BINDING = "get";
+    public static final String SAML_SERVER_SIGNATURE = "saml.server.signature";
+    public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
+    public static final String SAML_AUTHNSTATEMENT = "saml.authnstatement";
+    public static final String SAML_MULTIVALUED_ROLES = "saml.multivalued.roles";
+    public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
+    public static final String SAML_ENCRYPT = "saml.encrypt";
+    public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
+    public static final String REQUEST_ID = "REQUEST_ID";
 
     protected KeycloakSession session;
 
@@ -98,14 +106,15 @@ public class SamlProtocol implements LoginProtocol {
     }
 
     protected boolean isPostBinding(ClientSessionModel clientSession) {
-        return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING));
+        ClientModel client = clientSession.getClient();
+        return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || "true".equals(client.getAttribute(SAML_FORCE_POST_BINDING));
     }
 
     @Override
     public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
         ClientSessionModel clientSession = accessCode.getClientSession();
         ClientModel client = clientSession.getClient();
-        String requestID = clientSession.getNote("REQUEST_ID");
+        String requestID = clientSession.getNote(REQUEST_ID);
         String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
         String redirectUri = clientSession.getRedirectUri();
         String responseIssuer = getResponseIssuer(realm);
@@ -166,23 +175,23 @@ public class SamlProtocol implements LoginProtocol {
     }
 
     private boolean requiresRealmSignature(ClientModel client) {
-        return "true".equals(client.getAttribute("saml.server.signature"));
+        return "true".equals(client.getAttribute(SAML_SERVER_SIGNATURE));
     }
 
     private boolean requiresAssertionSignature(ClientModel client) {
-        return "true".equals(client.getAttribute("saml.assertion.signature"));
+        return "true".equals(client.getAttribute(SAML_ASSERTION_SIGNATURE));
     }
 
     private boolean includeAuthnStatement(ClientModel client) {
-        return "true".equals(client.getAttribute("saml.authnstatement"));
+        return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT));
     }
 
     private boolean multivaluedRoles(ClientModel client) {
-        return "true".equals(client.getAttribute("saml.multivalued.roles"));
+        return "true".equals(client.getAttribute(SAML_MULTIVALUED_ROLES));
     }
 
     public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) {
-        String alg = client.getAttribute("saml.signature.algorithm");
+        String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
         if (alg != null) {
             SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
             if (algorithm != null) return algorithm;
@@ -191,7 +200,7 @@ public class SamlProtocol implements LoginProtocol {
     }
 
     private boolean requiresEncryption(ClientModel client) {
-        return "true".equals(client.getAttribute("saml.encrypt"));
+        return "true".equals(client.getAttribute(SAML_ENCRYPT));
     }
 
     public void initClaims(SALM2LoginResponseBuilder builder, ClientModel model, UserModel user) {
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 2b42828..0fc67a8 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -198,7 +198,7 @@ public class SamlService {
             clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
             clientSession.setNote(SamlProtocol.SAML_BINDING, getBindingType());
             clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
-            clientSession.setNote("REQUEST_ID", requestAbstractType.getID());
+            clientSession.setNote(SamlProtocol.REQUEST_ID, requestAbstractType.getID());
 
             Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
             if (response != null) return response;