keycloak-aplcache

Changes

Details

diff --git a/core/src/main/java/org/keycloak/representations/info/ClientInstallationRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ClientInstallationRepresentation.java
new file mode 100755
index 0000000..051229e
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/info/ClientInstallationRepresentation.java
@@ -0,0 +1,71 @@
+package org.keycloak.representations.info;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClientInstallationRepresentation {
+    protected String id;
+    protected String protocol;
+    protected boolean downloadOnly;
+    protected String displayType;
+    protected String helpText;
+    protected String filename;
+    protected String mediaType;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public boolean isDownloadOnly() {
+        return downloadOnly;
+    }
+
+    public void setDownloadOnly(boolean downloadOnly) {
+        this.downloadOnly = downloadOnly;
+    }
+
+    public String getDisplayType() {
+        return displayType;
+    }
+
+    public void setDisplayType(String displayType) {
+        this.displayType = displayType;
+    }
+
+    public String getHelpText() {
+        return helpText;
+    }
+
+    public void setHelpText(String helpText) {
+        this.helpText = helpText;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public void setFilename(String filename) {
+        this.filename = filename;
+    }
+
+    public String getMediaType() {
+        return mediaType;
+    }
+
+    public void setMediaType(String mediaType) {
+        this.mediaType = mediaType;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java
old mode 100644
new mode 100755
index 0154790..640f3f6
--- a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java
@@ -24,6 +24,7 @@ public class ServerInfoRepresentation {
 
     private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
     private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
+    private Map<String, List<ClientInstallationRepresentation>> clientInstallations;
 
     private Map<String, List<String>> enums;
 
@@ -105,4 +106,12 @@ public class ServerInfoRepresentation {
     public void setEnums(Map<String, List<String>> enums) {
         this.enums = enums;
     }
+
+    public Map<String, List<ClientInstallationRepresentation>> getClientInstallations() {
+        return clientInstallations;
+    }
+
+    public void setClientInstallations(Map<String, List<ClientInstallationRepresentation>> clientInstallations) {
+        this.clientInstallations = clientInstallations;
+    }
 }
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 6569388..be30adf 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1109,6 +1109,9 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 client : function(ClientLoader) {
                     return ClientLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
                 }
             },
             controller : 'ClientInstallationCtrl'
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index b2d77ff..eb58ca0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -685,7 +685,7 @@ module.controller('ClientListCtrl', function($scope, realm, clients, Client, ser
     };
 });
 
-module.controller('ClientInstallationCtrl', function($scope, realm, client, ClientInstallation,ClientInstallationJBoss, $http, $routeParams) {
+module.controller('ClientInstallationCtrl', function($scope, realm, client, serverInfo, ClientInstallation,$http, $routeParams) {
     $scope.realm = realm;
     $scope.client = client;
     $scope.installation = null;
@@ -693,36 +693,24 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, Clie
     $scope.configFormat = null;
     $scope.filename = null;
 
-    $scope.configFormats = [
-        "Keycloak JSON",
-        "Wildfly/EAP Subsystem XML"
-    ];
+    var protocol = client.protocol;
+    if (!protocol) protocol = 'openid-connect';
+    $scope.configFormats = serverInfo.clientInstallations[protocol];
+    console.log('configFormats.length: ' + $scope.configFormats.length);
 
     $scope.changeFormat = function() {
-        if ($scope.configFormat == "Keycloak JSON") {
-            $scope.filename = 'keycloak.json';
-
-            var url = ClientInstallation.url({ realm: $routeParams.realm, client: $routeParams.client });
-            $http.get(url).success(function(data) {
-                var tmp = angular.fromJson(data);
-                $scope.installation = angular.toJson(tmp, true);
-                $scope.type = 'application/json';
-            })
-        } else if ($scope.configFormat == "Wildfly/EAP Subsystem XML") {
-            $scope.filename = 'keycloak.xml';
-
-            var url = ClientInstallationJBoss.url({ realm: $routeParams.realm, client: $routeParams.client });
-            $http.get(url).success(function(data) {
-                $scope.installation = data;
-                $scope.type = 'text/xml';
-            })
-        }
-
-        console.debug($scope.filename);
+        var url = ClientInstallation.url({ realm: $routeParams.realm, client: $routeParams.client, provider: $scope.configFormat.id });
+        $http.get(url).success(function(data) {
+            var installation = data;
+            if ($scope.configFormat.mediaType == 'application/json') {
+                installation = angular.fromJson(data);
+                installation = angular.toJson(installation, true);
+            }
+            $scope.installation = installation;
+        })
     };
-
     $scope.download = function() {
-        saveAs(new Blob([$scope.installation], { type: $scope.type }), $scope.filename);
+        saveAs(new Blob([$scope.installation], { type: $scope.configFormat.mediaType }), $scope.configFormat.filename);
     }
 });
 
@@ -1065,7 +1053,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
 module.controller('CreateClientCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
     $scope.protocols = ['openid-connect',
         'saml'];//Object.keys(serverInfo.providers['login-protocol'].providers).sort();
-
+    $scope.create = true;
     $scope.templates = [ {name:'NONE'}];
     for (var i = 0; i < templates.length; i++) {
         var template = templates[i];
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index 94854f3..e949a9d 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -284,15 +284,6 @@ module.factory('ClientClaimsLoader', function(Loader, ClientClaims, $route, $q) 
     });
 });
 
-module.factory('ClientInstallationLoader', function(Loader, ClientInstallation, $route, $q) {
-    return Loader.get(ClientInstallation, function() {
-        return {
-            realm : $route.current.params.realm,
-            client : $route.current.params.client
-        }
-    });
-});
-
 module.factory('ClientRoleListLoader', function(Loader, ClientRole, $route, $q) {
     return Loader.query(ClientRole, function() {
         return {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 728ed42..5e81744 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -1044,16 +1044,28 @@ module.factory('ClientDescriptionConverter', function($resource) {
     });
 });
 
+/*
+module.factory('ClientInstallation', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients/:client/installation/providers/:provider', {
+        realm : '@realm',
+        client : '@client',
+        provider : '@provider'
+    });
+});
+*/
+
+
 
 module.factory('ClientInstallation', function($resource) {
-    var url = authUrl + '/admin/realms/:realm/clients/:client/installation/json';
+    var url = authUrl + '/admin/realms/:realm/clients/:client/installation/providers/:provider';
     return {
         url : function(parameters)
         {
-            return url.replace(':realm', parameters.realm).replace(':client', parameters.client);
+            return url.replace(':realm', parameters.realm).replace(':client', parameters.client).replace(':provider', parameters.provider);
         }
     }
 });
+
 module.factory('ClientInstallationJBoss', function($resource) {
     var url = authUrl + '/admin/realms/:realm/clients/:client/installation/jboss';
     return {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html
index 2e3c578..1530ba8 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html
@@ -14,7 +14,7 @@
                 <div class="col-md-6">
                     <div class="input-group">
                         <div>
-                            <select class="form-control" id="configFormats" name="configFormats" ng-change="changeFormat()" ng-model="configFormat" ng-options="a for a in configFormats">
+                            <select class="form-control" id="configFormats" name="configFormats" ng-change="changeFormat()" ng-model="configFormat" ng-options="a.displayType for a in configFormats">
                                 <option value="" selected> {{:: 'select-a-format' | translate}} </option>
                             </select>
                         </div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
index 6f56467..58388ff 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
@@ -33,7 +33,7 @@
 
         <li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'clustering' | translate}}</a></li>
 
-        <li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'">
+        <li ng-class="{active: path[4] == 'installation'}">
             <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">{{:: 'installation' | translate}}</a>
             <kc-tooltip>{{:: 'installation.tooltip' | translate}}</kc-tooltip>
         </li>
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 3b2cfdc..f40f962 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1,5 +1,6 @@
 package org.keycloak.models.utils;
 
+import org.keycloak.Config;
 import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.Constants;
 import org.keycloak.common.util.Base64;
@@ -593,9 +594,16 @@ public class RepresentationToModel {
         }
     }
 
+    public static void renameRealm(RealmModel realm, String name) {
+        if (name.equals(realm.getName())) return;
+        ClientModel masterApp = realm.getMasterAdminClient();
+        masterApp.setClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(name));
+        realm.setName(name);
+    }
+
     public static void updateRealm(RealmRepresentation rep, RealmModel realm) {
         if (rep.getRealm() != null) {
-            realm.setName(rep.getRealm());
+            renameRealm(realm, rep.getRealm());
         }
         if (rep.getDisplayName() != null) realm.setDisplayName(rep.getDisplayName());
         if (rep.getDisplayNameHtml() != null) realm.setDisplayNameHtml(rep.getDisplayNameHtml());
diff --git a/model/sessions-infinispan/pom.xml b/model/sessions-infinispan/pom.xml
index c4f57b0..553895c 100755
--- a/model/sessions-infinispan/pom.xml
+++ b/model/sessions-infinispan/pom.xml
@@ -36,4 +36,16 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index 5de2072..e0564e9 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -98,12 +98,26 @@ public class DeploymentBuilder {
 
                 }
                 if (key.isEncryption()) {
-                    KeyStore keyStore = loadKeystore(resourceLoader, key);
-                    try {
-                        PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
-                        deployment.setDecryptionKey(privateKey);
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
+                    if (key.getKeystore() != null) {
+
+                        KeyStore keyStore = loadKeystore(resourceLoader, key);
+                        try {
+                            PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
+                            deployment.setDecryptionKey(privateKey);
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+                    } else {
+                        if (key.getPrivateKeyPem() == null) {
+                            throw new RuntimeException("SP signing key must have a PrivateKey defined");
+                        }
+                        try {
+                            PrivateKey privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim());
+                            deployment.setDecryptionKey(privateKey);
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+
                     }
                 }
             }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
index 20f7810..61ecbdc 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
@@ -116,7 +116,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
                 attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
             } else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) {
                 attributes.put(SamlConfigAttributes.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
-                attributes.put(SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem);
+                attributes.put(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem);
             }
         }
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java
new file mode 100755
index 0000000..d3abddf
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java
@@ -0,0 +1,161 @@
+package org.keycloak.protocol.saml.installation;
+
+import org.keycloak.Config;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.ClientInstallationProvider;
+import org.keycloak.protocol.saml.SamlClient;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.services.resources.RealmsResource;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakSamlClientInstallation implements ClientInstallationProvider {
+
+    @Override
+    public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
+        SamlClient samlClient = new SamlClient(client);
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("<keycloak-saml-adapter>\n");
+        buffer.append("    <SP entityID=\"").append(client.getClientId()).append("\"\n");
+        buffer.append("        sslPolicy=\"").append(realm.getSslRequired().name()).append("\"\n");
+        buffer.append("        logoutPage=\"SPECIFY YOUR LOGOUT PAGE!\">\n");
+        if (samlClient.requiresClientSignature() || samlClient.requiresEncryption()) {
+            buffer.append("        <Keys>\n");
+            if (samlClient.requiresClientSignature()) {
+                buffer.append("            <Key signing=\"true\">\n");
+                buffer.append("                <PrivateKeyPem>\n");
+                if (samlClient.getClientSigningPrivateKey() == null) {
+                    buffer.append("                    PRIVATE KEY NOT SET UP OR KNOWN\n");
+                } else {
+                    buffer.append("                    ").append(samlClient.getClientSigningPrivateKey()).append("\n");
+                }
+                buffer.append("                </PrivateKeyPem>\n");
+                buffer.append("                <CertificatePem>\n");
+                if (samlClient.getClientSigningCertificate() == null) {
+                    buffer.append("                    YOU MUST CONFIGURE YOUR CLIENT's SIGNING CERTIFICATE\n");
+                } else {
+                    buffer.append("                    ").append(samlClient.getClientSigningCertificate()).append("\n");
+                }
+                buffer.append("                </CertificatePem>\n");
+                buffer.append("            </Key>\n");
+            }
+            if (samlClient.requiresEncryption()) {
+                buffer.append("            <Key encryption=\"true\">\n");
+                buffer.append("                <PrivateKeyPem>\n");
+                if (samlClient.getClientEncryptingPrivateKey() == null) {
+                    buffer.append("                    PRIVATE KEY NOT SET UP OR KNOWN\n");
+                } else {
+                    buffer.append("                    ").append(samlClient.getClientEncryptingPrivateKey()).append("\n");
+                }
+                buffer.append("                </PrivateKeyPem>\n");
+                buffer.append("            </Key>\n");
+
+            }
+            buffer.append("        </Keys>\n");
+        }
+        buffer.append("        <IDP entityID=\"idp\"");
+        if (samlClient.requiresClientSignature()) {
+            buffer.append("\n             signatureAlgorithm=\"").append(samlClient.getSignatureAlgorithm()).append("\"");
+            if (samlClient.getCanonicalizationMethod() != null) {
+                buffer.append("\n             signatureCanonicalizationMethod=\"").append(samlClient.getCanonicalizationMethod()).append("\"");
+            }
+        }
+        buffer.append(">\n");
+        buffer.append("            <SingleSignOnService signRequest=\"").append(Boolean.toString(samlClient.requiresClientSignature())).append("\"\n");
+        buffer.append("                                 validateResponseSignature=\"").append(Boolean.toString(samlClient.requiresRealmSignature())).append("\"\n");
+        buffer.append("                                 requestBinding=\"POST\"\n");
+        UriBuilder bindingUrlBuilder = UriBuilder.fromUri(baseUri);
+        String bindingUrl = RealmsResource.protocolUrl(bindingUrlBuilder)
+                .build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString();
+        buffer.append("                                 bindingUrl=\"").append(bindingUrl).append("\"/>\n");
+
+        buffer.append("            <SingleLogoutService signRequest=\"").append(Boolean.toString(samlClient.requiresClientSignature())).append("\"\n");
+        buffer.append("                                 signResponse=\"").append(Boolean.toString(samlClient.requiresClientSignature())).append("\"\n");
+        buffer.append("                                 validateRequestSignature=\"").append(Boolean.toString(samlClient.requiresRealmSignature())).append("\"\n");
+        buffer.append("                                 validateResponseSignature=\"").append(Boolean.toString(samlClient.requiresRealmSignature())).append("\"\n");
+        buffer.append("                                 requestBinding=\"POST\"\n");
+        buffer.append("                                 responseBinding=\"POST\"\n");
+        buffer.append("                                 postBindingUrl=\"").append(bindingUrl).append("\"\n");
+        buffer.append("                                 redirectBindingUrl=\"").append(bindingUrl).append("\"");
+        buffer.append("/>\n");
+        if (samlClient.requiresRealmSignature()) {
+            buffer.append("            <Keys>\n");
+            buffer.append("                <Key signing=\"true\">\n");
+            buffer.append("                    <CertificatePem>\n");
+            buffer.append("                       ").append(realm.getCertificatePem()).append("\n");
+            buffer.append("                    </CertificatePem>\n");
+            buffer.append("                </Key>\n");
+            buffer.append("            </Keys>\n");
+        }
+        buffer.append("        </IDP>\n");
+        buffer.append("    </SP>\n");
+        buffer.append("</keycloak-saml-adapter>\n");
+        return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
+    }
+
+    @Override
+    public String getProtocol() {
+        return SamlProtocol.LOGIN_PROTOCOL;
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Keycloak SAML Adapter keycloak-saml.xml";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Keycloak SAML adapter configuration file.  Put this in WEB-INF directory if your WAR.";
+    }
+
+    @Override
+    public String getFilename() {
+        return "keycloak-saml.xml";
+    }
+
+    @Override
+    public String getMediaType() {
+        return MediaType.APPLICATION_XML;
+    }
+
+    @Override
+    public boolean isDownloadOnly() {
+        return false;
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public ClientInstallationProvider create(KeycloakSession session) {
+        return this;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public String getId() {
+        return "keycloak-saml";
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
index d935f83..3ac9892 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
@@ -121,4 +121,21 @@ public class SamlClient extends ClientConfigResolver {
 
     }
 
+    public String getClientEncryptingCertificate() {
+        return client.getAttribute(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
+    }
+
+    public void setClientEncryptingCertificate(String val) {
+        client.setAttribute(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, val);
+
+    }
+    public String getClientEncryptingPrivateKey() {
+        return client.getAttribute(SamlConfigAttributes.SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE);
+    }
+
+    public void setClientEncryptingPrivateKey(String val) {
+        client.setAttribute(SamlConfigAttributes.SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE, val);
+
+    }
+
 }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
index eea258a..c6bc60a 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
@@ -19,4 +19,6 @@ public interface SamlConfigAttributes {
     String SAML_ENCRYPT = "saml.encrypt";
     String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
     String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
+    String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
+    String SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.PRIVATE_KEY;
 }
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 e7bd3eb..c76c853 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
@@ -70,7 +70,6 @@ public class SamlProtocol implements LoginProtocol {
 
     public static final String ATTRIBUTE_TRUE_VALUE = "true";
     public static final String ATTRIBUTE_FALSE_VALUE = "false";
-    public static final String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
     public static final String SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE = "saml_assertion_consumer_url_post";
     public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect";
     public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post";
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
index 5742f7d..3e03ed4 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
@@ -49,7 +49,7 @@ public class SamlProtocolUtils {
     }
 
     public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
-        return getPublicKey(client, SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
+        return getPublicKey(client, SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
     }
 
     public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException {
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ClientInstallationProvider b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ClientInstallationProvider
new file mode 100755
index 0000000..f8e9df5
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.ClientInstallationProvider
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.installation.KeycloakSamlClientInstallation
diff --git a/services/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java b/services/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java
new file mode 100755
index 0000000..25e8f14
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java
@@ -0,0 +1,27 @@
+package org.keycloak.protocol;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * Provides a template/sample client config adapter file.  For example keycloak.json for our OIDC adapter.  keycloak-saml.xml for our SAML client adapter
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ClientInstallationProvider extends Provider, ProviderFactory<ClientInstallationProvider> {
+    Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri);
+    String getProtocol();
+    String getDisplayType();
+    String getHelpText();
+    String getFilename();
+    String getMediaType();
+    boolean isDownloadOnly();
+}
diff --git a/services/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java b/services/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java
new file mode 100755
index 0000000..fa1b54e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java
@@ -0,0 +1,32 @@
+package org.keycloak.protocol;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientInstallationSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "client-installation";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return ClientInstallationProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return ClientInstallationProvider.class;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
new file mode 100755
index 0000000..bd87517
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
@@ -0,0 +1,129 @@
+package org.keycloak.protocol.oidc.installation;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.ClientAuthenticator;
+import org.keycloak.authentication.ClientAuthenticatorFactory;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.ClientInstallationProvider;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakOIDCClientInstallation implements ClientInstallationProvider {
+
+    @Override
+    public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
+        ClientManager.InstallationAdapterConfig rep = new ClientManager.InstallationAdapterConfig();
+        rep.setAuthServerUrl(baseUri.toString());
+        rep.setRealm(realm.getName());
+        rep.setRealmKey(realm.getPublicKeyPem());
+        rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
+
+        if (client.isPublicClient() && !client.isBearerOnly()) rep.setPublicClient(true);
+        if (client.isBearerOnly()) rep.setBearerOnly(true);
+        if (client.getRoles().size() > 0) rep.setUseResourceRoleMappings(true);
+
+        rep.setResource(client.getClientId());
+
+        if (showClientCredentialsAdapterConfig(client)) {
+            Map<String, Object> adapterConfig = getClientCredentialsAdapterConfig(session, client);
+            rep.setCredentials(adapterConfig);
+        }
+        String json = null;
+        try {
+            json = JsonSerialization.writeValueAsPrettyString(rep);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return Response.ok(json, MediaType.TEXT_PLAIN_TYPE).build();
+    }
+
+    public static Map<String, Object> getClientCredentialsAdapterConfig(KeycloakSession session, ClientModel client) {
+        String clientAuthenticator = client.getClientAuthenticatorType();
+        ClientAuthenticatorFactory authenticator = (ClientAuthenticatorFactory) session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, clientAuthenticator);
+        return authenticator.getAdapterConfiguration(client);
+    }
+
+
+    public static boolean showClientCredentialsAdapterConfig(ClientModel client) {
+        if (client.isPublicClient()) {
+            return false;
+        }
+
+        if (client.isBearerOnly() && client.getNodeReRegistrationTimeout() <= 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public String getProtocol() {
+        return OIDCLoginProtocol.LOGIN_PROTOCOL;
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Keycloak OIDC keycloak.json";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "keycloak.json file used by the Keycloak OIDC client adapter to configure clients.  This must be saved to a keycloak.json file and put in your WEB-INF directory of your WAR file.  You may also want to tweak this file after you download it.";
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public ClientInstallationProvider create(KeycloakSession session) {
+        return this;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public String getId() {
+        return "keycloak-oidc-keycloak-json";
+    }
+
+    @Override
+    public boolean isDownloadOnly() {
+        return false;
+    }
+
+    @Override
+    public String getFilename() {
+        return "keycloak.json";
+    }
+
+    @Override
+    public String getMediaType() {
+        return MediaType.APPLICATION_JSON;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java
new file mode 100755
index 0000000..8751441
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java
@@ -0,0 +1,117 @@
+package org.keycloak.protocol.oidc.installation;
+
+import org.keycloak.Config;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.ClientInstallationProvider;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakOIDCJbossSubsystemClientInstallation implements ClientInstallationProvider {
+    @Override
+    public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("<secure-deployment name=\"WAR MODULE NAME.war\">\n");
+        buffer.append("    <realm>").append(realm.getName()).append("</realm>\n");
+        buffer.append("    <realm-public-key>").append(realm.getPublicKeyPem()).append("</realm-public-key>\n");
+        buffer.append("    <auth-server-url>").append(baseUri.toString()).append("</auth-server-url>\n");
+        if (client.isBearerOnly()){
+            buffer.append("    <bearer-only>true</bearer-only>\n");
+
+        } else if (client.isPublicClient()) {
+            buffer.append("    <public-client>true</public-client>\n");
+        }
+        buffer.append("    <ssl-required>").append(realm.getSslRequired().name()).append("</ssl-required>\n");
+        buffer.append("    <resource>").append(client.getClientId()).append("</resource>\n");
+        String cred = client.getSecret();
+        if (KeycloakOIDCClientInstallation.showClientCredentialsAdapterConfig(client)) {
+            Map<String, Object> adapterConfig = KeycloakOIDCClientInstallation.getClientCredentialsAdapterConfig(session, client);
+            for (Map.Entry<String, Object> entry : adapterConfig.entrySet()) {
+                buffer.append("    <credential name=\"" + entry.getKey() + "\">");
+
+                Object value = entry.getValue();
+                if (value instanceof Map) {
+                    buffer.append("\n");
+                    Map<String, Object> asMap = (Map<String, Object>) value;
+                    for (Map.Entry<String, Object> credEntry : asMap.entrySet()) {
+                        buffer.append("        <" + credEntry.getKey() + ">" + credEntry.getValue().toString() + "</" + credEntry.getKey() + ">\n");
+                    }
+                    buffer.append("    </credential>\n");
+                } else {
+                    buffer.append(value.toString()).append("</credential>\n");
+                }
+            }
+        }
+        if (client.getRoles().size() > 0) {
+            buffer.append("    <use-resource-role-mappings>true</use-resource-role-mappings>\n");
+        }
+        buffer.append("</secure-deployment>\n");
+        return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
+    }
+
+    @Override
+    public String getProtocol() {
+        return OIDCLoginProtocol.LOGIN_PROTOCOL;
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Keycloak OIDC JBoss Subsystem XML";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "XML snippet you must edit and add to the Keycloak OIDC subsystem on your client app server.  This type of configuration is useful when you can't or don't want to crack open your WAR file.";
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public ClientInstallationProvider create(KeycloakSession session) {
+        return this;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public String getId() {
+        return "keycloak-oidc-jboss-subsystem";
+    }
+
+    @Override
+    public boolean isDownloadOnly() {
+        return false;
+    }
+
+    @Override
+    public String getFilename() {
+        return "keycloak-oidc-subsystem.xml";
+    }
+
+    @Override
+    public String getMediaType() {
+        return MediaType.APPLICATION_XML;
+    }
+}
+
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index 73ebe44..7c19f61 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -17,6 +17,7 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.ClientInstallationProvider;
 import org.keycloak.representations.adapters.action.GlobalRequestResult;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
@@ -143,15 +144,27 @@ public class ClientResource {
         return new ClientAttributeCertificateResource(realm, auth, client, session, attributePrefix, adminEvent);
     }
 
+    @GET
+    @NoCache
+    @Path("installation/providers/{providerId}")
+    public Response getInstallationProvider(@PathParam("providerId") String providerId) {
+        ClientInstallationProvider provider = session.getProvider(ClientInstallationProvider.class, providerId);
+        if (provider == null) throw new NotFoundException("Unknown Provider");
+        return provider.generateInstallation(session, realm, client, keycloak.getBaseUri(uriInfo));
+    }
+
 
     /**
      * Get keycloak.json file
      *
+     * this method is deprecated, see getInstallationProvider
+     *
      * Returns a keycloak.json file to be used to configure the adapter of the specified client.
      *
      * @return
      * @throws IOException
      */
+    @Deprecated
     @GET
     @NoCache
     @Path("installation/json")
@@ -169,11 +182,14 @@ public class ClientResource {
     /**
      * Get adapter configuration XML for JBoss / Wildfly Keycloak subsystem
      *
+     * this method is deprecated, see getInstallationProvider
+     *
      * Returns XML that can be included in the JBoss / Wildfly Keycloak subsystem to configure the adapter of that client.
      *
      * @return
      * @throws IOException
      */
+    @Deprecated
     @GET
     @NoCache
     @Path("installation/jboss")
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
index 672745c..dfefb10 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
@@ -21,6 +21,7 @@ import org.keycloak.freemarker.ThemeProvider;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.protocol.ClientInstallationProvider;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.LoginProtocolFactory;
 import org.keycloak.protocol.ProtocolMapper;
@@ -61,6 +62,7 @@ public class ServerInfoAdminResource {
         setProviders(info);
         setProtocolMapperTypes(info);
         setBuiltinProtocolMappers(info);
+        setClientInstallations(info);
         info.setEnums(ENUMS);
         return info;
     }
@@ -144,6 +146,27 @@ public class ServerInfoAdminResource {
         }
     }
 
+    private void setClientInstallations(ServerInfoRepresentation info) {
+        info.setClientInstallations(new HashMap<String, List<ClientInstallationRepresentation>>());
+        for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ClientInstallationProvider.class)) {
+            ClientInstallationProvider provider = (ClientInstallationProvider)p;
+            List<ClientInstallationRepresentation> types = info.getClientInstallations().get(provider.getProtocol());
+            if (types == null) {
+                types = new LinkedList<>();
+                info.getClientInstallations().put(provider.getProtocol(), types);
+            }
+            ClientInstallationRepresentation rep = new ClientInstallationRepresentation();
+            rep.setId(p.getId());
+            rep.setHelpText(provider.getHelpText());
+            rep.setDisplayType( provider.getDisplayType());
+            rep.setProtocol( provider.getProtocol());
+            rep.setDownloadOnly( provider.isDownloadOnly());
+            rep.setFilename(provider.getFilename());
+            rep.setMediaType(provider.getMediaType());
+            types.add(rep);
+        }
+    }
+
     private void setProtocolMapperTypes(ServerInfoRepresentation info) {
         info.setProtocolMapperTypes(new HashMap<String, List<ProtocolMapperTypeRepresentation>>());
         for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ProtocolMapper.class)) {
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index a1a3acc..e0ea200 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -57,6 +57,10 @@ public class RealmsResource {
         return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
     }
 
+    public static UriBuilder protocolUrl(UriBuilder builder) {
+        return builder.path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
+    }
+
     public static UriBuilder clientRegistrationUrl(UriInfo uriInfo) {
         return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getClientsService");
     }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ClientInstallationProvider b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ClientInstallationProvider
new file mode 100755
index 0000000..c3da086
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ClientInstallationProvider
@@ -0,0 +1,2 @@
+org.keycloak.protocol.oidc.installation.KeycloakOIDCClientInstallation
+org.keycloak.protocol.oidc.installation.KeycloakOIDCJbossSubsystemClientInstallation
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 6189178..a657c78 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -9,3 +9,4 @@ org.keycloak.authentication.RequiredActionSpi
 org.keycloak.authentication.FormAuthenticatorSpi
 org.keycloak.authentication.FormActionSpi
 org.keycloak.services.clientregistration.ClientRegistrationSpi
+org.keycloak.protocol.ClientInstallationSpi
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
old mode 100644
new mode 100755
index b2fb326..a6f8a44
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -101,6 +101,34 @@ public class RealmTest extends AbstractClientTest {
         serverInfoResource.getInfo();
     }
 
+    /**
+     * KEYCLOAK-1990 1991
+     * @throws Exception
+     */
+    @Test
+    public void renameRealmTest() throws Exception {
+        Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
+        RealmRepresentation realm1 = new RealmRepresentation();
+        realm1.setRealm("test-immutable");
+        keycloak.realms().create(realm1);
+        realm1 = keycloak.realms().realm("test-immutable").toRepresentation();
+        realm1.setRealm("test-immutable-old");
+        keycloak.realms().realm("test-immutable").update(realm1);
+        realm1 = keycloak.realms().realm("test-immutable-old").toRepresentation();
+
+        RealmRepresentation realm2 = new RealmRepresentation();
+        realm2.setRealm("test-immutable");
+        keycloak.realms().create(realm2);
+        realm2 = keycloak.realms().realm("test-immutable").toRepresentation();
+
+        keycloak.realms().realm("test-immutable-old").remove();
+        keycloak.realms().realm("test-immutable").remove();
+
+
+
+
+    }
+
     @Test
     public void updateRealm() {
         // first change