keycloak-uncached
Changes
connections/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java 3(+3 -0)
connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_Beta1.java 3(+3 -0)
dependencies/server-all/pom.xml 5(+5 -0)
distribution/modules/build.xml 4(+4 -0)
distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml 1(+1 -0)
distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml 1(+1 -0)
distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-linkedin/main/module.xml 22(+22 -0)
docbook/reference/en/en-US/modules/themes.xml 174(+138 -36)
examples/themes/README.md 10(+2 -8)
examples/themes/src/main/resources/theme/logo-example/account/resources/img/red-hat-logo.png 0(+0 -0)
forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/AdvancedMessageFormatterMethod.java 36(+36 -0)
forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java 2(+1 -1)
forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManagerFactory.java 6(+2 -4)
forms/common-themes/src/main/resources/theme/account/base/messages/messages_en.properties 57(+0 -57)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-linkedin.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/account/messages/messages_de.properties 57(+50 -7)
forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties 99(+99 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/applications.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/oauth-clients.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-clustering.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-clustering-node.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-credentials.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-identity-provider.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-import.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-installation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-keys.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-list.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-mappers.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-mappers-add.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-revocation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-role-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-role-list.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-saml-key-export.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-saml-key-import.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-saml-keys.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-scope-mappings.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/application-sessions.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/brute-force.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/defense-headers.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-generic.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-kerberos.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-claims.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-credentials.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-identity-provider.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-installation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-list.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-mappers.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-mappers-add.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-protocol-mapper-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-revocation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/oauth-client-scope-mappings.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-cache-settings.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-credentials.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-default-roles.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-events-config.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-export.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-facebook.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-github.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-google.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-twitter.html 1(+1 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-login-settings.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-theme-settings.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-tokens.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/session-realm.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/session-revocation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-attribute-entry.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-credentials.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federated-identity.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-dropdown.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-navigation.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-navigation-application.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-navigation-oauth-client.html 0(+0 -0)
forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties 154(+154 -0)
forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties 152(+152 -0)
forms/common-themes/src/main/resources/theme/email/keycloak/messages/messages_de.properties 12(+0 -12)
forms/common-themes/src/main/resources/theme/email/keycloak/messages/messages_en.properties 12(+0 -12)
forms/common-themes/src/main/resources/theme/keycloak/account/resources/img/header-bkgrnd.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/admin-console.css 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/img/sprite-table-nav.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/img/header-bkgrnd.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/img/icon-collapse-expand.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/img/icon-sidebar-active.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/img/sprite-arrow-down.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/admin/resources/img/sprites-white.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-cookies.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-loader.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-mocks.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-resource.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-route.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-sanitize.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/angular-scenario.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/jstd-scenario-adapter.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/jstd-scenario-adapter-config.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/select2.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/ui-bootstrap-tpls-0.11.0.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/version.json 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/angular/version.txt 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/font-awesome/fonts/FontAwesome.otf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/font-awesome/fonts/fontawesome-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/font-awesome/fonts/fontawesome-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/font-awesome/fonts/fontawesome-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/components/font-awesome/fonts/fontawesome-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/filesaver/FileSaver.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/angular-file-upload.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/angular-file-upload.min.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/angular-file-upload-html5-shim.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/angular-file-upload-html5-shim.min.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/angular-file-upload-shim.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/angular-file-upload-shim.min.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/fileupload/FileAPI.min.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/jquery/jquery-1.10.2.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/css/patternfly.css 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/css/patternfly.min.css 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-BoldItalic-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-BoldItalic-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-BoldItalic-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-BoldItalic-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Bold-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Bold-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Bold-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Bold-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBoldItalic-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBoldItalic-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBoldItalic-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBoldItalic-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBold-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBold-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBold-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-ExtraBold-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Italic-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Italic-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Italic-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Italic-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-LightItalic-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-LightItalic-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-LightItalic-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-LightItalic-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Light-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Light-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Light-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Light-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Regular-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Regular-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Regular-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Regular-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-SemiboldItalic-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-SemiboldItalic-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-SemiboldItalic-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-SemiboldItalic-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Semibold-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Semibold-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Semibold-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/OpenSans-Semibold-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/PatternFlyIcons-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/PatternFlyIcons-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/PatternFlyIcons-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/fonts/PatternFlyIcons-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/img/bg-login.jpg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/img/brand.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/img/brand.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/img/brand-lg.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/img/logo.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/img/logo.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/js/patternfly.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/patternfly/js/patternfly.min.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/component.json 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/LICENSE 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/README.md 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/release.sh 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2.css 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2.jquery.json 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2.min.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ar.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ca.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_cs.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_da.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_de.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_el.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_en.js.template 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_es.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_et.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_eu.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_fi.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_fr.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_gl.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_he.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_hr.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_hu.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_id.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_is.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_it.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ja.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ko.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_lt.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_lv.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_mk.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_nl.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_no.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_pl.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_pt-BR.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_pt-PT.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ro.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ru.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_sk.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_sv.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_tr.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_ua.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_vi.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_zh-CN.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2_locale_zh-TW.js 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2-spinner.gif 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/select2-3.4.1/select2x2.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/zocial/zocial.css 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/zocial/zocial-regular-webfont.eot 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/zocial/zocial-regular-webfont.svg 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/zocial/zocial-regular-webfont.ttf 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/common/resources/lib/zocial/zocial-regular-webfont.woff 0(+0 -0)
forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties 12(+12 -0)
forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties 12(+12 -0)
forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/jboss_community.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/login/base/messages/messages_de.properties 147(+0 -147)
forms/common-themes/src/main/resources/theme/login/base/messages/messages_en.properties 145(+0 -145)
forms/common-themes/src/main/resources/theme/login/patternfly/resources/img/keycloak-logo.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/account/resources/img/icon-sidebar-active.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/account/resources/img/keycloak-logo.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/feedback-error-arrow-down.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/feedback-error-sign.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/feedback-success-arrow-down.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/feedback-success-sign.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/feedback-warning-arrow-down.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/feedback-warning-sign.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/keycloak-logo.png 0(+0 -0)
forms/common-themes/src/main/resources/theme/welcome/keycloak/resources/jboss_community.png 0(+0 -0)
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java 2(+2 -0)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java 2(+1 -1)
picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/KeycloakEventBridge.java 7(+5 -2)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/account/aerogear/resources/img/logo.png 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/admin/aerogear/resources/css/styles.css 45(+0 -45)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/admin/aerogear/resources/img/logo.png 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/admin/aerogear/theme.properties 4(+0 -4)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/account/resources/css/account.css 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/account/resources/img/logo.png 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/account/theme.properties 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/css/styles.css 45(+45 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/img/logo.png 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/theme.properties 4(+4 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/login/resources/css/login.css 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/login/resources/img/logo.png 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/login/theme.properties 0(+0 -0)
project-integrations/aerogear-ups/auth-server/src/main/resources/theme/login/aerogear/resources/img/logo.png 0(+0 -0)
README.md 2(+1 -1)
social/linkedin/.gitignore 1(+1 -0)
social/linkedin/pom.xml 40(+40 -0)
social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java 109(+109 -0)
social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProviderFactory.java 47(+47 -0)
social/linkedin/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory 1(+1 -0)
social/pom.xml 1(+1 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java 2(+2 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java 20(+16 -4)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java 21(+21 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java 5(+5 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java 5(+5 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java 5(+5 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java 4(+2 -2)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java 4(+2 -2)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java 15(+15 -0)
Details
diff --git a/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
index 6f1d632..58962dc 100755
--- a/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
+++ b/connections/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
@@ -87,10 +87,13 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
boolean clustered = config.getBoolean("clustered", false);
boolean async = config.getBoolean("async", true);
+ boolean allowDuplicateJMXDomains = config.getBoolean("allowDuplicateJMXDomains", true);
if (clustered) {
gcb.transport().defaultTransport();
}
+ gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains);
+
cacheManager = new DefaultCacheManager(gcb.build());
containerManaged = false;
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
index 27eb99f..a97473d 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="psilva@redhat.com" id="1.2.0.Beta1">
+ <delete tableName="CLIENT_SESSION_ROLE"/>
+ <delete tableName="CLIENT_SESSION_NOTE"/>
+ <delete tableName="CLIENT_SESSION"/>
+ <delete tableName="USER_SESSION"/>
+
<createTable tableName="PROTOCOL_MAPPER">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
@@ -14,8 +19,12 @@
<column name="PROTOCOL_MAPPER_NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
- <column name="APPLIED_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false"/>
- <column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="APPLIED_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
<column name="CONSENT_TEXT" type="VARCHAR(255)"/>
<column name="CLIENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
@@ -46,12 +55,20 @@
<column name="INTERNAL_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
- <column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
<column name="PROVIDER_ALIAS" type="VARCHAR(255)"/>
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
- <column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN" defaultValueBoolean="false"/>
- <column name="STORE_TOKEN" type="BOOLEAN" defaultValueBoolean="false"/>
- <column name="AUTHENTICATE_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ <column name="STORE_TOKEN" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ <column name="AUTHENTICATE_BY_DEFAULT" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
<column name="REALM_ID" type="VARCHAR(36)"/>
</createTable>
<createTable tableName="IDENTITY_PROVIDER_CONFIG">
@@ -63,14 +80,16 @@
<constraints nullable="false"/>
</column>
</createTable>
- <createTable tableName="CLIENT_IDENTITY_PROVIDER_MAPPING">
+ <createTable tableName="CLIENT_IDENTITY_PROV_MAPPING">
<column name="CLIENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="IDENTITY_PROVIDER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
- <column name="RETRIEVE_TOKEN" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="RETRIEVE_TOKEN" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
</createTable>
<createTable tableName="REALM_SUPPORTED_LOCALES">
<column name="REALM_ID" type="VARCHAR(36)">
@@ -78,30 +97,61 @@
</column>
<column name="VALUE" type="VARCHAR(255)"/>
</createTable>
+ <createTable tableName="USER_SESSION_NOTE">
+ <column name="USER_SESSION" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(2048)"/>
+ </createTable>
<addColumn tableName="CLIENT">
- <column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ </addColumn>
+ <addColumn tableName="USER_SESSION">
+ <column name="USER_SESSION_STATE" type="INT" />
</addColumn>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_PCM" tableName="PROTOCOL_MAPPER"/>
<addPrimaryKey columnNames="INTERNAL_ID" constraintName="CONSTRAINT_2B" tableName="IDENTITY_PROVIDER"/>
<addPrimaryKey columnNames="IDENTITY_PROVIDER, USER_ID" constraintName="CONSTRAINT_40" tableName="FEDERATED_IDENTITY"/>
<addPrimaryKey columnNames="IDENTITY_PROVIDER_ID, NAME" constraintName="CONSTRAINT_D" tableName="IDENTITY_PROVIDER_CONFIG"/>
<addPrimaryKey columnNames="PROTOCOL_MAPPER_ID, NAME" constraintName="CONSTRAINT_PMConfig" tableName="PROTOCOL_MAPPER_CONFIG"/>
- <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
- <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="PROTOCOL_MAPPER" constraintName="FK_PCM_REALM" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
- <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
- <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
- <addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
- <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
- <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addPrimaryKey columnNames="USER_SESSION, NAME" constraintName="CONSTRAINT_USN_PK" tableName="USER_SESSION_NOTE"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="PROTOCOL_MAPPER" constraintName="FK_PCM_REALM" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
+ <addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
+ <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="CLIENT_IDENTITY_PROV_MAPPING" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROV_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SUPPORTED_LOCALES" constraintName="FK_SUPPORTED_LOCALES_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="USER_SESSION" baseTableName="USER_SESSION_NOTE" constraintName="FK5EDFB00FF51D3472" referencedColumnNames="ID" referencedTableName="USER_SESSION"/>
<addUniqueConstraint columnNames="PROVIDER_ALIAS, REALM_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
- <addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROVIDER_MAPPING"/>
-
+ <addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROV_MAPPING"/>
<addColumn tableName="REALM">
<column name="LOGIN_LIFESPAN" type="INT"/>
- <column name="INTERNATIONALIZATION_ENABLED" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="INTERNATIONALIZATION_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
<column name="DEFAULT_LOCALE" type="VARCHAR(255)" />
- <column name="REGISTRATION_EMAIL_AS_USERNAME" type="BOOLEAN" defaultValueBoolean="false"/>
+ <column name="REG_EMAIL_AS_USERNAME" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
</addColumn>
+
+ <!-- KEYCLOAK-1106 APPLICATION_ID and REALM_ID switched in REALM_APPLICATION table -->
+ <dropForeignKeyConstraint baseTableName="REALM_APPLICATION" constraintName="FK_71S3P0DIUXAWWQQSA528UBY2Q" />
+ <dropForeignKeyConstraint baseTableName="REALM_APPLICATION" constraintName="FK_L5QGA3RFME47335JY8JXYXH3I" />
+ <dropUniqueConstraint tableName="REALM_APPLICATION" constraintName="UK_L5QGA3RFME47335JY8JXYXH3I" />
+ <renameColumn tableName="REALM_APPLICATION" oldColumnName="APPLICATION_ID" newColumnName="APPLICATION_ID_TMP" columnDataType="VARCHAR(36)"/>
+ <renameColumn tableName="REALM_APPLICATION" oldColumnName="REALM_ID" newColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
+ <renameColumn tableName="REALM_APPLICATION" oldColumnName="APPLICATION_ID_TMP" newColumnName="REALM_ID" columnDataType="VARCHAR(36)"/>
+ <addUniqueConstraint columnNames="APPLICATION_ID" constraintName="UK_M6QGA3RFME47335JY8JXYXH3I" tableName="REALM_APPLICATION"/>
+ <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="REALM_APPLICATION" constraintName="FK_82S3P0DIUXAWWQQSA528UBY2Q" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_APPLICATION" constraintName="FK_M6QGA3RFME47335JY8JXYXH3I" referencedColumnNames="ID" referencedTableName="REALM"/>
+
</changeSet>
</databaseChangeLog>
diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_Beta1.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_Beta1.java
index 13a79a3..5dcb1e7 100644
--- a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_Beta1.java
+++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_2_0_Beta1.java
@@ -33,6 +33,9 @@ public class Update1_2_0_Beta1 extends Update {
@Override
public void update(KeycloakSession session) {
+ deleteEntries("clientSessions");
+ deleteEntries("sessions");
+
convertSocialToIdFedRealms();
convertSocialToIdFedUsers();
addAccessCodeLoginTimeout();
dependencies/server-all/pom.xml 5(+5 -0)
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index fe6d5cc..a7bd522 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -122,6 +122,11 @@
<artifactId>keycloak-social-facebook</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-linkedin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- ldap federation api -->
<dependency>
distribution/modules/build.xml 4(+4 -0)
diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml
index 9199165..ad0f9e7 100755
--- a/distribution/modules/build.xml
+++ b/distribution/modules/build.xml
@@ -235,6 +235,10 @@
<module-def name="org.keycloak.keycloak-social-facebook">
<maven-resource group="org.keycloak" artifact="keycloak-social-facebook"/>
</module-def>
+
+ <module-def name="org.keycloak.keycloak-social-linkedin">
+ <maven-resource group="org.keycloak" artifact="keycloak-social-linkedin"/>
+ </module-def>
<module-def name="org.keycloak.keycloak-kerberos-federation">
<maven-resource group="org.keycloak" artifact="keycloak-kerberos-federation"/>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml
index c757599..f8a251d 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-server/main/module.xml
@@ -57,6 +57,7 @@
<module name="org.keycloak.keycloak-social-github" services="import"/>
<module name="org.keycloak.keycloak-social-google" services="import"/>
<module name="org.keycloak.keycloak-social-twitter" services="import"/>
+ <module name="org.keycloak.keycloak-social-linkedin" services="import"/>
<module name="org.keycloak.keycloak-subsystem" services="import"/>
<module name="org.keycloak.keycloak-timer-api" services="import"/>
<module name="org.keycloak.keycloak-timer-basic" services="import"/>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
index bafc53f..6166b9b 100755
--- a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
@@ -60,6 +60,7 @@
<module name="org.keycloak.keycloak-social-github" services="import"/>
<module name="org.keycloak.keycloak-social-google" services="import"/>
<module name="org.keycloak.keycloak-social-twitter" services="import"/>
+ <module name="org.keycloak.keycloak-social-linkedin" services="import"/>
<module name="org.keycloak.keycloak-timer-api" services="import"/>
<module name="org.keycloak.keycloak-timer-basic" services="import"/>
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-linkedin/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-linkedin/main/module.xml
new file mode 100755
index 0000000..00853bf
--- /dev/null
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-social-linkedin/main/module.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-linkedin">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="org.keycloak.keycloak-social-core"/>
+ <module name="org.keycloak.keycloak-broker-core"/>
+ <module name="org.keycloak.keycloak-broker-oidc"/>
+ <module name="org.keycloak.keycloak-model-api"/>
+ <module name="org.jboss.logging"/>
+ <module name="javax.api"/>
+ <module name="org.codehaus.jackson.jackson-core-asl"/>
+ <module name="org.codehaus.jackson.jackson-mapper-asl"/>
+ <module name="org.codehaus.jackson.jackson-xc"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
index 29de32e..6a97ca5 100755
--- a/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
+++ b/distribution/subsystem-war/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -50,6 +50,7 @@
<module name="org.keycloak.keycloak-social-github" services="import"/>
<module name="org.keycloak.keycloak-social-google" services="import"/>
<module name="org.keycloak.keycloak-social-twitter" services="import"/>
+ <module name="org.keycloak.keycloak-social-linkedin" services="import"/>
<module name="org.keycloak.keycloak-timer-api" services="import"/>
<module name="org.keycloak.keycloak-timer-basic" services="import"/>
<module name="org.hibernate" services="import"/>
diff --git a/docbook/reference/en/en-US/modules/identity-broker.xml b/docbook/reference/en/en-US/modules/identity-broker.xml
index 0ab53f0..3a74b73 100755
--- a/docbook/reference/en/en-US/modules/identity-broker.xml
+++ b/docbook/reference/en/en-US/modules/identity-broker.xml
@@ -715,6 +715,99 @@
</tgroup>
</table>
</section>
+ <section>
+ <title>LinkedIn</title>
+ <para>
+ To enable login with LinkedIn you first have to create an application in
+ <ulink url="https://www.linkedin.com/secure/developer">LinkedIn Developer Network</ulink>. Then you need to copy
+ the client id and secret into the Keycloak Admin Console.
+ </para>
+ <para>
+ Let's see first how to create an application with LinkedIn.
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>
+ Log in to <ulink url="https://www.linkedin.com/secure/developer">LinkedIn Developer Network</ulink>. Click the
+ <literal>Add New Application</literal> link. Use any value for <literal>Application Name</literal>,
+ <literal>Website URL</literal>, <literal>Description</literal>, <literal>Developer Contact Email</literal> and <literal>Phone</literal> you want.
+ Select <literal>r_basicprofile</literal> and <literal>r_emailaddress</literal> in the <literal>Default Scope</literal> section.
+ Click the <literal>Add Application</literal> button.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Copy <literal>Consumer Key / API Key</literal> and <literal>Consumer Secret / Secret Key</literal> from the shown page.
+ </para>
+ </listitem>
+ </orderedlist>
+ <para>
+ Now that you have the client id and secret, you can proceed with the creation of a LinkedIn Identity Provider in Keycloak. As follows:
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>
+ Select the <literal>LinkedIn</literal> identity provider from the drop-down box on the top right corner of the identity providers table in Keycloak's Admin Console. You should be presented with a specific page to configure the selected provided.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Copy the client id and secret to their corresponding fields in the Keycloak Admin Console. Click <literal>Save</literal>.
+ </para>
+ </listitem>
+ </orderedlist>
+ <para>
+ Once you create the identity provider in Keycloak, you must update your LinkedIn application with the redirect url that was
+ generated to your identity provider.
+ </para>
+ <orderedlist>
+ <listitem>
+ <para>
+ Open the LinkedIn Developer Network and select your application. In <literal>OAuth 2.0 Redirect URLs</literal>
+ insert the redirect uri created by Keycloak. The redirect uri
+ usually have the following format: <literal>http://{host}:{port}/auth/realms/{realm}/broker/{provider_alias}/endpoint</literal>.
+ </para>
+ </listitem>
+ </orderedlist>
+ <note>
+ <para>
+ You can always get the redirect url for a specific identity provider from the table presented when you
+ click on the 'Identity Provider' tab in <emphasis>Realm > Settings</emphasis>.
+ </para>
+ </note>
+ <para>
+ That is it! This pretty much what you need to do in order to setup this identity provider.
+ </para>
+ <para>
+ The table below lists some additional configuration options you may use when configuring this provider.
+ </para>
+ <table>
+ <title>Configuration Options</title>
+ <tgroup align="left" cols="2">
+ <thead>
+ <row>
+ <entry>
+ Configuration
+ </entry>
+ <entry>
+ Description
+ </entry>
+ </row>
+ </thead>
+ <tbody valign="top">
+ <row>
+ <entry>
+ <literal>Default Scopes</literal>
+ </entry>
+ <entry>
+ Allows you to manually specify the scopes that users must authorize when authenticating with this provider.
+ For a complete list of scopes, please take a look at application configuration in <ulink url="https://www.linkedin.com/secure/developer">LinkedIn Developer Network</ulink>. By default, Keycloak uses the following scopes: <literal>r_basicprofile r_emailaddress</literal>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
</section>
<section>
docbook/reference/en/en-US/modules/themes.xml 174(+138 -36)
diff --git a/docbook/reference/en/en-US/modules/themes.xml b/docbook/reference/en/en-US/modules/themes.xml
index 3996b3a..db3d659 100755
--- a/docbook/reference/en/en-US/modules/themes.xml
+++ b/docbook/reference/en/en-US/modules/themes.xml
@@ -2,18 +2,18 @@
<title>Themes</title>
<para>
- Keycloak provides theme support for login forms and account management. This allows customizing the look
- and feel of end-user facing pages so they can be integrated with your brand and applications.
+ Keycloak provides theme support for web pages and emails. This allows customizing the look
+ and feel of end-user facing pages so they can be integrated with your applications.
</para>
<section>
<title>Theme types</title>
<para>
- There are several types of themes in Keycloak:
+ A theme can support several types to customize different aspects of Keycloak. The types currently available
+ are:
<itemizedlist>
<listitem>Account - Account management</listitem>
<listitem>Admin - Admin console</listitem>
- <listitem>Common - Shared resources for themes</listitem>
<listitem>Email - Emails</listitem>
<listitem>Login - Login forms</listitem>
<listitem>Welcome - Welcome pages</listitem>
@@ -29,6 +29,11 @@
from the drop-down box in the top left corner. Under <literal>Settings</literal> click on <literal>Theme</literal>.
</para>
<para>
+ To set the theme for the <literal>master</literal> Keycloak admin console set the admin console theme for
+ the <literal>master</literal> realm. To set the theme for per realm admin access control set the admin console
+ theme for the corresponding realm.
+ </para>
+ <para>
To change the welcome theme you need to edit <literal>standalone/configuration/keycloak-server.json</literal>
and add <literal>welcomeTheme</literal> to the theme element, for example:
<programlisting>
@@ -43,9 +48,8 @@
<section>
<title>Default themes</title>
<para>
- Keycloak comes bundled with default themes in <literal>standalone/configuration/themes</literal>. It is
- not recommended to edit these themes directly. Instead you should create a new theme to extend a default
- theme. A good reference is to copy the keycloak themes as these extend the base theme to add styling.
+ Keycloak comes bundled with default themes in <literal>standalone/configuration/themes</literal>. You should
+ not edit the bundled themes directly. Instead create a new theme that extends a bundled theme.
</para>
</section>
@@ -65,24 +69,63 @@
<para>
A theme can extend another theme. When extending a theme you can override individual files (templates, stylesheets, etc.).
The recommended way to create a theme is to extend the base theme. The base theme provides templates
- and a default message bundle. It should be possible to achieve the customization required by styling these
- templates.
+ and a default message bundle. If you decide to override templates bear in mind that you may need to update
+ your templates when upgrading to a new release to include any changes made to the original template.
+ </para>
+ <para>
+ Before creating a theme it's a good idea to disable caching as this makes it possible to edit theme resources
+ without restarting the server. To do this open <literal>../standalone/configuration/keycloak-server.json</literal>
+ for <literal>theme</literal> set <literal>staticMaxAge</literal> to <literal>-1</literal> and
+ <literal>cacheTemplates</literal> and <literal>cacheThemes</literal> to <literal>false</literal>. For example:
+<programlisting>[<![CDATA[
+"theme": {
+ "default": "keycloak",
+ "staticMaxAge": 01,
+ "cacheTemplates": false,
+ "cacheThemes": false,
+ "folder": {
+ "dir": "${jboss.server.config.dir}/themes"
+ }
+},
+]]></programlisting>
+ Remember to re-enable caching in production as it will significantly impact performance.
</para>
<para>
- To create a new theme, create a folder in <literal>.../standalone/configuration/themes/<theme type></literal>.
- The name of the folder is the name of the theme. Then create a file <literal>theme.properties</literal> inside the theme folder.
- The contents of the file should be:
+ To create a new theme create a directory for the theme in <literal>.../standalone/configuration/themes</literal>.
+ The name of the directory should be the name of the theme. For example to create a theme called <literal>example-theme</literal>
+ create the directory <literal>.../standalone/configuration/themes/example-theme</literal>. Inside the theme
+ directory you then need to create a directory for each of the types your theme is going to provide. For example
+ to add the login type to the <literal>example-theme</literal> theme create the directory
+ <literal>.../standalone/configuration/themes/example-theme/login</literal>.
</para>
- <programlisting>parent=base</programlisting>
<para>
- You have now created your theme. Check that it works by configuring it for a realm. It should look the same
- as the base theme as you've not added anything to it yet. The next sections will describe how to modify
- the theme.</para>
+ For each type create a file <literal>theme.properties</literal> which allows setting some configuration for
+ the theme, for example what theme it overrides and if it should import any themes. For the above example we
+ want to override the base theme and import common resources from the Keycloak theme. To do this create the
+ file <literal>.../standalone/configuration/themes/example-theme/login/theme.properties</literal> with the
+ following contents:
+<programlisting>[<![CDATA[
+parent=base
+import=common/keycloak
+]]></programlisting>
+ </para>
+ <para>
+ You have now created a theme with support for the login type. To check that it works open the admin console.
+ Select your realm and click on <literal>Themes</literal>. For <literal>Login Theme</literal> select
+ <literal>example-theme</literal> and click <literal>Save</literal>. Then open the login page for the realm.
+ You can do this either by login through your application or by opening <literal>http://localhost:8080/realms/<realm name>/account</literal>.
+ </para>
+ <para>
+ To see the effect of changing the parent theme, set <literal>parent=keycloak</literal> in <literal>theme.properties</literal>
+ and refresh the login page. To follow the rest of the documentation set it back to <literal>parent=base</literal>
+ before continuing.
+ </para>
<section>
<title>Stylesheets</title>
<para>
- A theme can have one or more stylesheets, to add a stylesheet create a file inside <literal>resources/css</literal> (for example <literal>resources/css/styles.css</literal>)
- inside your theme folder. Then registering it in <literal>theme.properties</literal> by adding:
+ A theme can have one or more stylesheets, to add a stylesheet create a file inside <literal>resources/css</literal>
+ (for example <literal>resources/css/styles.css</literal>) inside your theme folder. Then registering it
+ in <literal>theme.properties</literal> by adding:
</para>
<programlisting>styles=css/styles.css</programlisting>
<para>
@@ -90,6 +133,17 @@
as you want. For example:
</para>
<programlisting>styles=css/styles.css css/more-styles.css</programlisting>
+ For the example-theme above add <literal>example-theme/login/resources/css/styles.css</literal> with the
+ following content:
+<programlisting>[<![CDATA[
+#kc-form {
+ background-color: #000;
+ color: #fff;
+ padding: 20px;
+}]]></programlisting>
+ Then edit <literal>example-theme/login/theme.properties</literal> and add <programlisting>styles=css/styles.css</programlisting>.
+ Refresh the login page to see your changes. It's not pretty, but you can see how easily you can modify the
+ styles for your theme.
</section>
<section>
<title>Scripts</title>
@@ -121,9 +175,8 @@
<section>
<title>Messages</title>
<para>
- Text in the templates are loaded from message bundles. Currently internationalization isn't supported,
- but that will be added in a later release. A theme that extends another theme will inherit all messages
- from the parents message bundle, but can override individual messages. For example to replace
+ Text in the templates are loaded from message bundles. A theme that extends another theme will inherit
+ all messages from the parents message bundle, but can override individual messages. For example to replace
<literal>Username</literal> on the login form with <literal>Your Username</literal> create the file
<literal>messages/messages.properties</literal> inside your theme folder and add the following content:
</para>
@@ -134,31 +187,80 @@
<para>
Keycloak uses <ulink url="http://freemarker.org">Freemarker Templates</ulink> in order to generate HTML.
These templates are defined in <literal>.ftl</literal> files and can be overriden from the base theme.
- Check out the Freemarker website on how to form a template file.
+ Check out the Freemarker website on how to form a template file. To override the login template for the
+ <literal>example-theme</literal> copy <literal>../standalone/configuration/themes/base/login/login.ftl</literal>
+ to <literal>../standalone/configuration/themes/example-theme/login</literal> and open it in an editor. After
+ the first line (<#import ...>) add <literal><h1>HELLO WORLD!</h1></literal> then refresh
+ the page.
</para>
</section>
</section>
<section>
+ <title>Deploying themes</title>
+ <para>
+ Themes can be deployed to Keycloak by copying the theme directory to <literal>../standalone/configuration/themes</literal>
+ or it can be deployed as a module. For a single server or during development just copying the theme is fine, but
+ in a cluster or domain it's recommended to deploy as a module.
+ </para>
+ <para>
+ To deploy a theme as a module you need to create an jar (it's basically just a zip with jar extension) with
+ the theme resources and a file <literal>META/keycloak-server.json</literal> that describes the themes contained
+ in the archive. For example <literal>example-theme.jar</literal> with the contents:
+ <itemizedlist>
+ <listitem>META-INF/keycloak-themes.json</listitem>
+ <listitem>theme/example-theme/login/theme.properties</listitem>
+ <listitem>theme/example-theme/login/login.ftl</listitem>
+ <listitem>theme/example-theme/login/resources/css/styles.css</listitem>
+ </itemizedlist>
+ The contents of META-INF/keycloak-server.json in this case would be:
+<programlisting>[<![CDATA[
+{
+ "themes": [{
+ "name" : "example-theme",
+ "types": [ "login" ]
+ }]
+}
+]]></programlisting>
+ As you can see a single jar can contain multiple themes and each theme can support one or more types.
+ </para>
+ <para>
+ The deploy the jar as a module to Keycloak you can either manually create the module or use <literal>jboss-cli</literal>.
+ It's simplest to use <literal>jboss-cli</literal> as it creates the required directories and module descriptor
+ for you. To deploy the above jar <literal>jboss-cli</literal> run:
+<programlisting>[<![CDATA[
+ KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.example.exampletheme --resources=example-theme.jar"
+]]></programlisting>
+ If you're on windows run <programlisting>KEYCLOAK_HOME/bin/jboss-cli.bat</programlisting>.
+ </para>
+ <para>
+ This command creates <literal>modules/org/example/exampletheme/main</literal> containing <literal>example-theme.jar</literal>
+ and <literal>module.xml</literal>.
+ </para>
+ <para>
+ Once you've created the module you need to register it with Keycloak do this by editing
+ <literal>../standalone/configuration/keycloak-server.json</literal> and adding the module to <literal>theme/module/modules</literal>. For example:
+<programlisting>[<![CDATA[
+"theme": {
+ ...
+ "module": {
+ "modules": [ "org.example.exampletheme" ]
+ }
+}
+]]></programlisting>
+ </para>
+ <para>
+ If a theme is deployed to <literal>../standalone/configuration/themes</literal> and as a module the first
+ is used.
+ </para>
+ </section>
+
+ <section>
<title>SPIs</title>
<para>
For full control of login forms and account management Keycloak provides a number of SPIs.
</para>
<section>
- <title>Theme SPI</title>
- <para>
- The Theme SPI allows creating different mechanisms to load themes for the default FreeMarker based
- implementations of login forms and account management. To create a theme provider you will need to implement
- <literal>org.keycloak.freemarker.ThemeProviderFactory</literal> and <literal>org.keycloak.freemarker.ThemeProvider</literal>.
- </para>
- <para>
- Keycloak comes with two theme providers, one that loads themes from the classpath (used by default themes)
- and another that loads themes from a folder (used by custom themes). Looking at these
- would be a good place to start to create your own theme provider. You can find them inside
- <literal>forms/common-themes</literal> on GitHub or the source download.
- </para>
- </section>
- <section>
<title>Account SPI</title>
<para>
The Account SPI allows implementing the account management pages using whatever web framework or templating
diff --git a/examples/cors/angular-product-app/.gitignore b/examples/cors/angular-product-app/.gitignore
new file mode 100644
index 0000000..9500957
--- /dev/null
+++ b/examples/cors/angular-product-app/.gitignore
@@ -0,0 +1 @@
+/.externalToolBuilders/*
examples/themes/README.md 10(+2 -8)
diff --git a/examples/themes/README.md b/examples/themes/README.md
index 75ec61a..ad60677 100644
--- a/examples/themes/README.md
+++ b/examples/themes/README.md
@@ -21,7 +21,7 @@ Then open $KEYCLOAK_HOME/standalone/configuration/keycloak-server.json and regis
}
}
-Alternatively you can copy `src/main/resources/theme/login` to `standalone/configuration/themes/login/`.
+Alternatively you can copy `src/main/resources/theme/sunrise` to `standalone/configuration/themes/`.
Once you've added the theme open the admin console, select your realm, click on `Theme`. In the dropdown for `Login Theme` select `sunrise`. Click `Save` and login to the realm to see the new theme in action.
@@ -46,13 +46,7 @@ Then open $KEYCLOAK_HOME/standalone/configuration/keycloak-server.json and regis
}
}
-Alternatively you can copy:
-
-* `account/logo-example` to `standalone/configuration/themes/account/`
-* `login/logo-example` to `standalone/configuration/themes/login/`
-* `admin/logo-example` to `standalone/configuration/themes/admin/`
-* `welcome/logo-example` to `standalone/configuration/themes/welcome/`
-
+Alternatively you can copy `src/main/resources/theme/logo-example` to `standalone/configuration/themes/`.
Once you've added the theme open the admin console, select your realm, click on `Theme`. In the dropdowns for `Login Theme`, `Account Theme` and `Admin Console Theme` select `logo-example`. Click `Save` and login to the realm to see the new theme in action.
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/AdvancedMessageFormatterMethod.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/AdvancedMessageFormatterMethod.java
new file mode 100644
index 0000000..007d445
--- /dev/null
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/AdvancedMessageFormatterMethod.java
@@ -0,0 +1,36 @@
+package org.keycloak.freemarker.beans;
+
+import freemarker.template.TemplateMethodModelEx;
+import freemarker.template.TemplateModelException;
+
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
+/**
+ * @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
+ */
+public class AdvancedMessageFormatterMethod implements TemplateMethodModelEx {
+ private final Properties messages;
+ private final Locale locale;
+
+ public AdvancedMessageFormatterMethod(Locale locale, Properties messages) {
+ this.locale = locale;
+ this.messages = messages;
+ }
+
+ @Override
+ public Object exec(List list) throws TemplateModelException {
+ if (list.size() >= 1) {
+ String key = list.get(0).toString();
+ if (key.startsWith("${") && key.endsWith("}")) {
+ key = key.substring(2, key.length() - 1);
+ return new MessageFormat(messages.getProperty(key, key), locale).format(list.subList(1, list.size()).toArray());
+ } else {
+ return key;
+ }
+ }
+ return null;
+ }
+}
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java
index 37bfc56..db57676 100644
--- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java
@@ -22,8 +22,8 @@ public class MessageFormatterMethod implements TemplateMethodModelEx {
@Override
public Object exec(List list) throws TemplateModelException {
- String key = list.get(0).toString();
if (list.size() >= 1) {
+ String key = list.get(0).toString();
return new MessageFormat(messages.getProperty(key,key),locale).format(list.subList(1, list.size()).toArray());
} else {
return null;
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManagerFactory.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManagerFactory.java
index 2fdaf05..42fa50e 100755
--- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManagerFactory.java
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManagerFactory.java
@@ -11,9 +11,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class ExtendingThemeManagerFactory implements ThemeProviderFactory {
- private ConcurrentHashMap<ThemeKey, Theme> themeCache = new ConcurrentHashMap<ThemeKey, Theme>();
-
- private ExtendingThemeManager themeManager;
+ private ConcurrentHashMap<ThemeKey, Theme> themeCache;
@Override
public ThemeProvider create(KeycloakSession session) {
@@ -23,7 +21,7 @@ public class ExtendingThemeManagerFactory implements ThemeProviderFactory {
@Override
public void init(Config.Scope config) {
if(Config.scope("theme").getBoolean("cacheThemes", true)) {
- themeCache = new ConcurrentHashMap<ThemeKey, Theme>();
+ themeCache = new ConcurrentHashMap<>();
}
}
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java
index 8f2dc0e..e41d671 100755
--- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java
@@ -20,7 +20,7 @@ public class FreeMarkerUtil {
public FreeMarkerUtil() {
if (Config.scope("theme").getBoolean("cacheTemplates", true)) {
- cache = new ConcurrentHashMap<String, Template>();
+ cache = new ConcurrentHashMap<>();
}
}
diff --git a/forms/common-themes/src/main/java/org/keycloak/theme/ClassLoaderTheme.java b/forms/common-themes/src/main/java/org/keycloak/theme/ClassLoaderTheme.java
index c95edd2..3e92be8 100755
--- a/forms/common-themes/src/main/java/org/keycloak/theme/ClassLoaderTheme.java
+++ b/forms/common-themes/src/main/java/org/keycloak/theme/ClassLoaderTheme.java
@@ -2,13 +2,11 @@ package org.keycloak.theme;
import org.keycloak.freemarker.Theme;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Locale;
import java.util.Properties;
-import java.util.ResourceBundle;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -42,7 +40,7 @@ public class ClassLoaderTheme implements Theme {
this.type = type;
this.classLoader = classLoader;
- String themeRoot = "theme/" + type.toString().toLowerCase() + "/" + name + "/";
+ String themeRoot = "theme/" + name + "/" + type.toString().toLowerCase() + "/";
this.templateRoot = themeRoot;
this.resourceRoot = themeRoot + "resources/";
@@ -60,10 +58,6 @@ public class ClassLoaderTheme implements Theme {
}
}
- public ClassLoaderTheme() {
-
- }
-
@Override
public String getName() {
return name;
diff --git a/forms/common-themes/src/main/java/org/keycloak/theme/FolderTheme.java b/forms/common-themes/src/main/java/org/keycloak/theme/FolderTheme.java
index 0edc92e..7d9ce2a 100644
--- a/forms/common-themes/src/main/java/org/keycloak/theme/FolderTheme.java
+++ b/forms/common-themes/src/main/java/org/keycloak/theme/FolderTheme.java
@@ -18,11 +18,13 @@ public class FolderTheme implements Theme {
private String parentName;
private String importName;
private File themeDir;
+ private String name;
private Type type;
private final Properties properties;
- public FolderTheme(File themeDir, Type type) throws IOException {
+ public FolderTheme(File themeDir, String name, Type type) throws IOException {
this.themeDir = themeDir;
+ this.name = name;
this.type = type;
this.properties = new Properties();
@@ -36,7 +38,7 @@ public class FolderTheme implements Theme {
@Override
public String getName() {
- return themeDir.getName();
+ return name;
}
@Override
diff --git a/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java b/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java
index b8c7c89..90d8ba6 100755
--- a/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java
+++ b/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java
@@ -15,10 +15,10 @@ import java.util.Set;
*/
public class FolderThemeProvider implements ThemeProvider {
- private File rootDir;
+ private File themesDir;
- public FolderThemeProvider(File rootDir) {
- this.rootDir = rootDir;
+ public FolderThemeProvider(File themesDir) {
+ this.themesDir = themesDir;
}
@Override
@@ -28,51 +28,41 @@ public class FolderThemeProvider implements ThemeProvider {
@Override
public Theme getTheme(String name, Theme.Type type) throws IOException {
- if (hasTheme(name, type)) {
- return new FolderTheme(new File(getTypeDir(type), name), type);
- }
- return null;
+ File themeDir = getThemeDir(name, type);
+ return themeDir.isDirectory() ? new FolderTheme(themeDir, name, type) : null;
}
@Override
public Set<String> nameSet(Theme.Type type) {
- File typeDir = getTypeDir(type);
- if (typeDir != null) {
- File[] themes = typeDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File pathname) {
- return pathname.isDirectory();
- }
- });
-
+ final String typeName = type.name().toLowerCase();
+ File[] themeDirs = themesDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.isDirectory() && new File(pathname, typeName).isDirectory();
+ }
+ });
+ if (themeDirs != null) {
Set<String> names = new HashSet<String>();
- for (File t : themes) {
- names.add(t.getName());
+ for (File themeDir : themeDirs) {
+ names.add(themeDir.getName());
}
return names;
+ } else {
+ return Collections.emptySet();
}
-
- return Collections.emptySet();
- }
-
- private File getTypeDir(Theme.Type type) {
- if (rootDir != null && rootDir.isDirectory()) {
- File typeDir = new File(rootDir, type.name().toLowerCase());
- if (typeDir.isDirectory()) {
- return typeDir;
- }
- }
- return null;
}
@Override
public boolean hasTheme(String name, Theme.Type type) {
- File typeDir = getTypeDir(type);
- return typeDir != null && new File(typeDir, name).isDirectory();
+ return getThemeDir(name, type).isDirectory();
}
@Override
public void close() {
}
+ private File getThemeDir(String name, Theme.Type type) {
+ return new File(themesDir, name + File.separator + type.name().toLowerCase());
+ }
+
}
diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
new file mode 100755
index 0000000..0d995cd
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -0,0 +1,99 @@
+doSave=Save
+doCancel=Cancel
+doLogOutAllSessions=Log out all sessions
+doRemove=Remove
+doAdd=Add
+doSignOut=Sign Out
+
+editAccountHtmlTtile=Edit Account
+federatedIdentitiesHtmlTitle=Federated Identities
+accountLogHtmlTitle=Account Log
+changePasswordHtmlTitle=Change Password
+sessionsHtmlTitle=Sessions
+accountManagementTitle=Keycloak Account Management
+authenticatorTitle=Authenticator
+
+authenticatorCode=One-time code
+email=Email
+firstName=First name
+lastName=Last name
+password=Password
+passwordConfirm=Confirmation
+passwordNew=New Password
+username=Username
+street=Street
+locality=City or Locality
+region=State, Province, or Region
+postal_code=Zip or Postal code
+country=Country
+
+requiredFields=Required fields
+allFieldsRequired=All fields required
+
+backToApplication=« Back to application
+backTo=Back to {0}
+
+date=Date
+event=Event
+ip=IP
+client=Client
+clients=Clients
+details=Details
+started=Started
+lastAccess=Last Access
+expires=Expires
+applications=Applications
+
+account=Account
+federatedIdentity=Federated Identity
+authenticator=Authenticator
+sessions=Sessions
+log=Log
+
+configureAuthenticators=Configured Authenticators
+mobile=Mobile
+totpStep1=Install <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> on your mobile.
+totpStep2=Open the application and scan the barcode or enter the key.
+totpStep3=Enter the one-time code provided by the application and click Save to finish the setup.
+
+missingFirstNameMessage=Please specify first name.
+invalidEmailMessage=Invalid email address.
+missingLastNameMessage=Please specify last name.
+missingEmailMessage=Please specify email.
+missingPasswordMessage=Please specify password.
+notMatchPasswordMessage=Passwords don''t match.
+
+missingTotpMessage=Please specify authenticator code.
+invalidPasswordExistingMessage=Invalid existing password.
+invalidPasswordConfirmMessage=Password confirmation doesn''t match.
+invalidTotpMessage=Invalid authenticator code.
+
+readOnlyUserMessage=You can''t update your account as it is read only.
+readOnlyPasswordMessage=You can''t update your password as your account is read only.
+
+successTotpMessage=Mobile authenticator configured.
+successTotpRemovedMessage=Mobile authenticator removed.
+
+accountUpdatedMessage=Your account has been updated.
+accountPasswordUpdatedMessage=Your password has been updated.
+
+missingIdentityProviderMessage=Identity provider not specified.
+invalidFederatedIdentityActionMessage=Invalid or missing action.
+identityProviderNotFoundMessage=Specified identity provider not found.
+federatedIdentityLinkNotActiveMessage=This identity is not active anymore.
+federatedIdentityRemovingLastProviderMessage=You can''t remove last federated identity as you don''t have password.
+identityProviderRedirectErrorMessage=Failed to redirect to identity provider.
+identityProviderRemovedMessage=Identity provider removed successfully.
+
+accountDisabledMessage=Account is disabled, contact admin.
+
+accountTemporarilyDisabledMessage=Account is temporarily disabled, contact admin or try again later.
+invalidPasswordMinLengthMessage=Invalid password: minimum length {0}.
+invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least {0} lower case characters.
+invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} numerical digits.
+invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
+invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
+invalidPasswordNotUsernameMessage=Invalid password\: must not be equal to the username.
+
+locale_de=German
+locale_en=English
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-twitter.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-twitter.html
new file mode 100755
index 0000000..a4630ac
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-twitter.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="resourceUrl + '/partials/realm-identity-provider-social.html'"></div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login.ftl b/forms/common-themes/src/main/resources/theme/base/login/login.ftl
new file mode 100755
index 0000000..505e185
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login.ftl
@@ -0,0 +1,84 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout displayInfo=social.displayInfo; section>
+ <#if section = "title">
+ <#if client.application>
+ ${msg("loginTitle",(realm.name!''))}
+ <#elseif client.oauthClient>
+ ${msg("loginOauthTitle",(realm.name!''))}
+ </#if>
+ <#elseif section = "header">
+ <#if client.application>
+ ${msg("loginTitleHtml",(realm.name!''))}
+ <#elseif client.oauthClient>
+ ${msg("loginOauthTitleHtml",(realm.name!''), (client.clientId!''))}
+ </#if>
+ <#elseif section = "form">
+ <#if realm.password>
+ <form id="kc-form-login" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="username" class="${properties.kcLabelClass!}"><#if !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')?html}" type="text" autofocus />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input id="password" class="${properties.kcInputClass!}" name="password" type="password" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <#if realm.rememberMe>
+ <div class="checkbox">
+ <label>
+ <#if login.rememberMe??>
+ <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3" checked> ${msg("rememberMe")}
+ <#else>
+ <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3"> ${msg("rememberMe")}
+ </#if>
+ </label>
+ </div>
+ </#if>
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ <#if realm.resetPasswordAllowed>
+ <span><a href="${url.loginPasswordResetUrl}">${msg("doForgotPassword")}</a></span>
+ </#if>
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <div class="${properties.kcFormButtonsWrapperClass!}">
+ <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
+ <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
+ </div>
+ </div>
+ </div>
+ </form>
+ </#if>
+ <#elseif section = "info" >
+ <#if realm.password && realm.registrationAllowed>
+ <div id="kc-registration">
+ <span>${msg("noAccount")} <a href="${url.registrationUrl}">${msg("doRegister")}</a></span>
+ </div>
+ </#if>
+
+ <#if realm.password && social.providers??>
+ <div id="kc-social-providers">
+ <ul>
+ <#list social.providers as p>
+ <li><a href="${p.loginUrl}" id="zocial-${p.alias}" class="zocial ${p.providerId}"> <span class="text">${p.alias}</span></a></li>
+ </#list>
+ </ul>
+ </div>
+ </#if>
+ </#if>
+</@layout.registrationLayout>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl
new file mode 100755
index 0000000..94f264a
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-config-totp.ftl
@@ -0,0 +1,45 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout displayInfo=true; section>
+ <#if section = "title">
+ ${msg("loginTotpTitle")}
+ <#elseif section = "header">
+ ${msg("loginTotpTitle")}
+ <#elseif section = "form">
+ <form action="${url.loginUpdateTotpUrl}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="otp" class="${properties.kcLabelClass!}">${msg("loginTotpOneTime")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="totp" name="totp" class="${properties.kcInputClass!}" />
+ </div>
+ <input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}"/>
+ </div>
+ </div>
+ </form>
+ <#elseif section = "info" >
+ <ol id="kc-totp-settings">
+ <li>
+ <p>${msg("loginTotpStep1")}</p>
+ </li>
+ <li>
+ <p>${msg("loginTotpStep2")}</p>
+ <img src="${totp.totpSecretQrCodeUrl}" alt="Figure: Barcode"><br/>
+ <span class="code">${totp.totpSecretEncoded}</span>
+ </li>
+ <li>
+ <p>${msg("loginTotpStep3")}</p>
+ </li>
+ </ol>
+ </#if>
+</@layout.registrationLayout>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-oauth-grant.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-oauth-grant.ftl
new file mode 100755
index 0000000..9b0637b
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-oauth-grant.ftl
@@ -0,0 +1,66 @@
+<#-- TODO: Only a placeholder, implementation needed -->
+<#import "template.ftl" as layout>
+<@layout.registrationLayout bodyClass="oauth"; section>
+ <#if section = "title">
+ ${msg("oauthGrantTitle")}
+ <#elseif section = "header">
+ ${msg("oauthGrantTitleHtml",(realm.name!''), (client.clientId!''))}
+ <#elseif section = "form">
+ <div id="kc-oauth" class="content-area">
+ <h3>${msg("oauthGrantRequest")}</h3>
+ <ul>
+ <#if oauth.claimsRequested??>
+ <li>
+ <span>
+ ${msg("personalInfo")}
+ <#list oauth.claimsRequested as claim>
+ ${advancedMsg(claim)}<#if claim_has_next>, </#if>
+ </#list>
+ </span>
+ </li>
+ </#if>
+ <#if oauth.accessRequestMessage??>
+ <li>
+ <span>
+ ${oauth.accessRequestMessage}
+ </span>
+ </li>
+ </#if>
+ <#if oauth.realmRolesRequested??>
+ <#list oauth.realmRolesRequested as role>
+ <li>
+ <span><#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if></span>
+ </li>
+ </#list>
+ </#if>
+ <#if oauth.resourceRolesRequested??>
+ <#list oauth.resourceRolesRequested?keys as resource>
+ <#list oauth.resourceRolesRequested[resource] as role>
+ <li>
+ <span class="kc-role"><#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if></span>
+ <span class="kc-resource">${msg("inResource", resource)}</span>
+ </li>
+ </#list>
+ </#list>
+ </#if>
+ </ul>
+
+ <form class="form-actions" action="${url.oauthAction}" method="POST">
+ <input type="hidden" name="code" value="${oauth.code}">
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <div class="${properties.kcFormButtonsWrapperClass!}">
+ <input class="btn btn-primary btn-lg" name="accept" id="kc-login" type="submit" value="${msg("doYes")}"/>
+ <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${msg("doNo")}"/>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-reset-password.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-reset-password.ftl
new file mode 100755
index 0000000..ec327c2
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-reset-password.ftl
@@ -0,0 +1,33 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout displayInfo=true; section>
+ <#if section = "title">
+ ${msg("emailForgotTitle")}
+ <#elseif section = "header">
+ ${msg("emailForgotTitle")}
+ <#elseif section = "form">
+ <form id="kc-reset-password-form" class="${properties.kcFormClass!}" action="${url.loginPasswordResetUrl}" method="post">
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="username" class="${properties.kcLabelClass!}">${msg("usernameOrEmail")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="username" name="username" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ <span><a href="${url.loginUrl}">${msg("backToLogin")}</a></span>
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}"/>
+ </div>
+ </div>
+ </form>
+ <#elseif section = "info" >
+ ${msg("emailInstruction")}
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-totp.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-totp.ftl
new file mode 100755
index 0000000..ec07935
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-totp.ftl
@@ -0,0 +1,37 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout; section>
+ <#if section = "title">
+ ${msg("loginTitle",realm.name)}
+ <#elseif section = "header">
+ ${msg("loginTitleHtml",realm.name)}
+ <#elseif section = "form">
+ <form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
+ <input id="username" name="username" value="${login.username!''}" type="hidden" />
+ <input id="password-token" name="password-token" value="${login.passwordToken!''}" type="hidden" />
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="totp" class="${properties.kcLabelClass!}">${msg("doLogIn")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input id="totp" name="totp" type="text" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <div class="${properties.kcFormButtonsWrapperClass!}">
+ <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
+ <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
+ </div>
+ </div>
+ </div>
+ </form>
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl
new file mode 100755
index 0000000..d201616
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-update-password.ftl
@@ -0,0 +1,39 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout displayInfo=true; section>
+ <#if section = "title">
+ ${msg("updatePasswordTitle")}
+ <#elseif section = "header">
+ ${msg("updatePasswordTitle")}
+ <#elseif section = "form">
+ <form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginUpdatePasswordUrl}" method="post">
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}" autofocus />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="password" id="password-confirm" name="password-confirm" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}"/>
+ </div>
+ </div>
+ </form>
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
new file mode 100755
index 0000000..56b5cbe
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
@@ -0,0 +1,48 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout; section>
+ <#if section = "title">
+ ${msg("loginProfileTitle")}
+ <#elseif section = "header">
+ ${msg("loginProfileTitle")}
+ <#elseif section = "form">
+ <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginUpdateProfileUrl}" method="post">
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="email" name="email" value="${(user.email!'')?html}" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="firstName" name="firstName" value="${(user.firstName!'')?html}" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="lastName" name="lastName" value="${(user.lastName!'')?html}" class="${properties.kcInputClass!}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}" />
+ </div>
+ </div>
+ </form>
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
new file mode 100644
index 0000000..dc5acea
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
@@ -0,0 +1,154 @@
+doLogIn=Anmelden
+doRegister=Registrieren
+doCancel=Abbrechen
+doSubmit=Absenden
+doYes=Ja
+doNo=Nein
+doForgotPassword=Passwort vergessen?
+doClickHere=hier klicken
+
+registerWithTitle=Registrierung bei {0}
+registerWithTitleHtml=Registrierung bei <strong>{0}</strong>
+loginTitle=Anmeldung bei {0}
+loginTitleHtml=Anmeldung bei <strong>{0}</strong>
+loginOauthTitle=Tempor�rer zugriff auf {0}
+loginOauthTitleHtml=Tempor�rer zugriff auf <strong>{0}</strong> angefordert von <strong>{1}</strong>.
+loginTotpTitle=Mobile Authentifizierung Einrichten
+loginProfileTitle=Benutzerkonto Informationen aktualisieren
+oauthGrantTitle=OAuth gew�hren
+oauthGrantTitleHtml=Tempor�rer zugriff auf <strong>{0}</strong> angefordert von <strong>{1}</strong>.
+errorTitle=Es tut uns leid...
+errorTitleHtml=Es tut uns leid...
+emailVerifyTitle=E-Mail verifizieren
+emailForgotTitle=Passwort vergessen?
+updatePasswordTitle=Passwort aktualisieren
+codeSuccessTitle=Erfolgreicher code
+codeErrorTitle=Fehler code\: {0}
+
+noAccount=Neuer Benutzer?
+username=Benutzername
+usernameOrEmail=Benutzername oder E-Mail
+firstName=Vorname
+fullName=Name
+lastName=Nachname
+email=E-Mail
+password=Passwort
+passwordConfirm=Passwort best�tigen
+passwordNew=Neues Passwort
+passwordNewConfirm=Neues Passwort best�tigen
+rememberMe=Angemeldet bleiben
+authenticatorCode=One-time Code
+street=Strasse
+region=Staat, Provinz, Region
+postal_code=PLZ
+locality=Stadt oder Ortschaft
+country=Land
+
+loginTotpStep1=Installieren Sie <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> oder <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> auf Ihrem Smartphone.
+loginTotpStep2=�ffnen Sie die Applikation und scannen Sie den Barcode oder geben sie den Code ein.
+loginTotpStep3=Geben Sie den One-time Code welcher die Applikation generiert hat ein und klicken Sie auf Absenden.
+loginTotpOneTime=One-time Code
+
+oauthGrantRequest=Wollen Sie diese Zugriffsreche gew�hren?
+inResource=in <strong>{0}</strong>
+
+emailVerifyInstruction1=Ein E-Mail mit weitern Anweisungen wurde an Sie versendet.
+emailVerifyInstruction2=Falls Sie kein E-Mail erhalten haben, dann k�nnen Sie
+emailVerifyInstruction3=um ein neues E-Mail zu verschicken.
+
+backToLogin=« Zur�ck zur Anmeldung
+backToApplication=« Zur�ck zur Applikation
+
+emailInstruction=Geben Sie ihren Benutzernamen oder E-Mail Adresse ein und klicken Sie auf Absenden. Danach werden wir ihnen ein E-Mail mit weiteren Instruktionen zusenden.
+
+copyCodeInstruction=Bitte kopieren sie den folgenden Code und f�gen ihn in die Applikation ein\:
+
+personalInfo=Pers�nliche Informationen:
+
+invalidUserMessage=Ung�ltiger Benutzername oder Passwort.
+invalidEmailMessage=Ung�ltige E-Mail Adresse.
+accountDisabledMessage=Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.
+accountTemporarilyDisabledMessage=Benutzerkonto ist tempor�r gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es sp�ter noch einmal.
+expiredCodeMessage=Zeit�berschreitung bei der Anmeldung. Bitter melden Sie sich erneut an.
+
+missingFirstNameMessage=Bitte geben Sie einen Vornamen ein.
+missingLastNameMessage=Bitte geben Sie einen Nachnamen ein.
+missingEmailMessage=Bitte geben Sie eine E-Mail Adresse ein.
+missingUsernameMessage=Bitte geben Sie einen Benutzernamen ein.
+missingPasswordMessage=Bitte geben Sie ein Passwort ein.
+missingTotpMessage=Bitte geben Sie den One-time Code ein.
+notMatchPasswordMessage=Passw�rter sind nicht identisch.
+
+invalidPasswordExistingMessage=Das aktuelle Passwort is ung�ltig.
+invalidPasswordConfirmMessage=Die Passwortbest�tigung ist nicht identisch.
+invalidTotpMessage=Ung�ltiger One-time Code.
+
+usernameExistsMessage=Benutzername existiert bereits.
+emailExistsMessage=E-Mail existiert bereits.
+
+federatedIdentityEmailExistsMessage=Es existiert bereits ein Benutzer mit dieser E-Mail Adresse. Bitte melden Sie sich bei der Benutzerverwaltung an um das Benutzerkonto zu verkn�pfen.
+federatedIdentityUsernameExistsMessage=Es existiert bereits ein Benutzer mit diesem Benutzernamen. Bitte melden Sie sich bei der Benutzerverwaltung an um das Benutzerkonto zu verkn�pfen.
+
+configureTotpMessage=Sie m�ssen eine Mobile Authentifizierung einrichten um das Benutzerkonto zu aktivieren.
+updateProfileMessage=Sie m�ssen ihr Benutzerkonto aktualisieren um das Benutzerkonto zu aktivieren.
+updatePasswordMessage=Sie m�ssen ihr Passwort �ndern um das Benutzerkonto zu aktivieren.
+verifyEmailMessage=Sie m�ssen ihre E-Mail Adresse verifizieren um das Benutzerkonto zu aktivieren.
+
+emailSentMessage=Sie sollten in k�rze ein E-Mail mit weiteren Instruktionen erhalten.
+emailSendErrorMessage=Das E-Mail konnte nicht versendet werden, bitte versuchen Sie es sp�ter nochmals.
+
+accountUpdatedMessage=Ihr Benutzerkonto wurde aktualisiert.
+accountPasswordUpdatedMessage=Ihr Passwort wurde aktualisiert.
+
+noAccessMessage=Kein Zugriff
+
+invalidPasswordMinLengthMessage=Ung�ltiges Passwort: minimum l�nge {0}.
+invalidPasswordMinDigitsMessage=Ung�ltiges Passwort: muss mindestens {0} Zahl(en) beinhalten.
+invalidPasswordMinLowerCaseCharsMessage=Ung�ltiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.
+invalidPasswordMinUpperCaseCharsMessage=Ung�ltiges Passwort: muss mindestens {0} Grossbuchstaben beinhalten.
+invalidPasswordMinSpecialCharsMessage=Ung�ltiges Passwort: muss mindestens {0} Spezialzeichen beinhalten.
+invalidPasswordNotUsernameMessage=Ung�ltiges Passwort\: darf nicht gleich sein wie Benutzername.
+
+failedToProcessResponseMessage=Konnte Response nicht verarbeiten.
+httpsRequiredMessage=HTTPS erforderlich.
+realmNotEnabledMessage=Realm nicht aktiviert.
+invalidRequestMessage=Ung�ltiger Request.
+unknownLoginRequesterMessage=Ung�ltiger login requester
+loginRequesterNotEnabledMessage=Login requester nicht aktiviert.
+bearerOnlyMessage=Bearer-only Applikationen k�nne sich nicht via Browser anmelden.
+directGrantsOnlyMessage=Direct-grants-only Clients k�nne sich nicht via Browser anmelden.
+invalidRedirectUriMessage=Ung�ltige redirect uri.
+unsupportedNameIdFormatMessage=Nicht unterst�tztes NameIDFormat.
+invlidRequesterMessage=Ung�ltiger requester.
+registrationNotAllowedMessage=Registrierung nicht erlaubt.
+
+permissionNotApprovedMessage=Berechtigung nicht best�tigt.
+noRelayStateInResponseMessage=Kein relay state in der Antwort von dem Identity Provider [{0}].
+identityProviderAlreadyLinkedMessage=Die Identit�t welche von dem Identity Provider [{0}] zur�ckgegeben wurde, ist bereits mit einem anderen Benutzer verkn�pft.
+insufficientPermissionMessage=Nicht gen�gend Rechte um die Identit�t zu verkn�pfen.
+couldNotProceedWithAuthenticationRequestMessage=Konnte den Authentifizierungs Request nicht weiter verarbeiten.
+couldNotObtainTokenMessage=Konnte kein token vom Identity Provider [{0}] entnehmen.
+unexpectedErrorRetrievingTokenMessage=Unerwarteter Fehler w�hrend dem Empfang des Token von dem Identity Provider [{0}].
+unexpectedErrorHandlingResponseMessage=Unerwarteter Fehler w�hrend der Bearbeitung des Respons vom Identity Provider [{0}].
+identityProviderAuthenticationFailedMessage=Authentifizierung Fehlgeschlagen. Konnte sich mit dem Identity Provider [{0}] nicht authentifizieren.
+couldNotSendAuthenticationRequestMessage=Konnte Authentifizierungs Request nicht an den Identity Provider [{0}] schicken.
+unexpectedErrorHandlingRequestMessage=Unerwarteter Fehler w�hrend der Bearbeitung des Requests zum Identity Provider [{0}].
+invalidAccessCodeMessage=Ung�ltiger Access-Code.
+sessionNotActiveMessage=Session nicht aktiv.
+unknownCodeMessage=Unbekannter Code, bitte melden Sie sich erneut �ber die Applikation an.
+invalidCodeMessage=Ung�ltiger Code, bitte melden Sie sich erneut �ber die Applikation an.
+identityProviderUnexpectedErrorMessage=Unerwarteter Fehler w�hrend der Authentifizierung mit dem Identity Provider.
+identityProviderNotFoundMessage=Konnte kein Identity Provider mit der Identit�t [{0}] finden.
+realmSupportsNoCredentialsMessage=Realm [{0}] unterst�tzt keine Credential Typen.
+identityProviderNotUniqueMessage=Realm [{0}] unterst�tz mehrere Identity Providers.
+
+invalidParameterMessage=Invalid parameter\: {0}
+missingParameterMessage=Missing parameter\: {0}
+clientNotFoundMessage=Client not found.
+
+emailVerifiedMessage=Ihr E-Mail Adresse wurde erfolgreich verifiziert.
+
+locale_de=Deutsch
+locale_en=Englisch
+
+poweredByKeycloak=Powered by Keycloak
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
new file mode 100755
index 0000000..5ed7cec
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -0,0 +1,152 @@
+doLogIn=Log in
+doRegister=Register
+doCancel=Cancel
+doSubmit=Submit
+doYes=Yes
+doNo=No
+doForgotPassword=Forgot Password?
+doClickHere=Click here
+
+registerWithTitle=Register with {0}
+registerWithTitleHtml=Register with <strong>{0}</strong>
+loginTitle=Log in to {0}
+loginTitleHtml=Log in to <strong>{0}</strong>
+loginOauthTitle=Temporary access for {0}
+loginOauthTitleHtml=Temporary access for <strong>{0}</strong> requested by <strong>{1}</strong>.
+loginTotpTitle=Mobile Authenticator Setup
+loginProfileTitle=Update Account Information
+oauthGrantTitle=OAuth Grant
+oauthGrantTitleHtml=Temporary access for <strong>{0}</strong> requested by <strong>{1}</strong>.
+errorTitle=We''re sorry...
+errorTitleHtml=We''re <strong>sorry</strong> ...
+emailVerifyTitle=Email verification
+emailForgotTitle=Forgot Your Password?
+updatePasswordTitle=Update password
+codeSuccessTitle=Success code
+codeErrorTitle=Error code\: {0}
+
+noAccount=New user?
+username=Username
+usernameOrEmail=Username or email
+firstName=First name
+fullName=Full name
+lastName=Last name
+email=Email
+password=Password
+passwordConfirm=Confirm password
+passwordNew=New Password
+passwordNewConfirm=New Password confirmation
+rememberMe=Remember me
+authenticatorCode=One-time code
+street=Street
+locality=City or Locality
+region=State, Province, or Region
+postal_code=Zip or Postal code
+country=Country
+
+loginTotpStep1=Install <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> on your mobile
+loginTotpStep2=Open the application and scan the barcode or enter the key
+loginTotpStep3=Enter the one-time code provided by the application and click Submit to finish the setup
+loginTotpOneTime=One-time code
+
+oauthGrantRequest=Do you grant these access privileges?
+inResource=in <strong>{0}</strong>
+
+emailVerifyInstruction1=An email with instructions to verify your email address has been sent to you.
+emailVerifyInstruction2=Haven''t received a verification code in your email?
+emailVerifyInstruction3=to re-send the email.
+
+backToLogin=« Back to Login
+
+emailInstruction=Enter your username or email address and we will send you instructions on how to create a new password.
+
+copyCodeInstruction=Please copy this code and paste it into your application:
+
+personalInfo=Personal Info:
+
+invalidUserMessage=Invalid username or password.
+invalidEmailMessage=Invalid email address.
+accountDisabledMessage=Account is disabled, contact admin.
+accountTemporarilyDisabledMessage=Account is temporarily disabled, contact admin or try again later.
+expiredCodeMessage=Login timeout. Please login again.
+
+missingFirstNameMessage=Please specify first name.
+missingLastNameMessage=Please specify last name.
+missingEmailMessage=Please specify email.
+missingUsernameMessage=Please specify username.
+missingPasswordMessage=Please specify password.
+missingTotpMessage=Please specify authenticator code.
+notMatchPasswordMessage=Passwords don''t match.
+
+invalidPasswordExistingMessage=Invalid existing password.
+invalidPasswordConfirmMessage=Password confirmation doesn''t match.
+invalidTotpMessage=Invalid authenticator code.
+
+usernameExistsMessage=Username already exists.
+emailExistsMessage=Email already exists.
+
+federatedIdentityEmailExistsMessage=User with email already exists. Please login to account management to link the account.
+federatedIdentityUsernameExistsMessage=User with username already exists. Please login to account management to link the account.
+
+configureTotpMessage=You need to set up Mobile Authenticator to activate your account.
+updateProfileMessage=You need to update your user profile to activate your account.
+updatePasswordMessage=You need to change your password to activate your account.
+verifyEmailMessage=You need to verify your email address to activate your account.
+
+emailSentMessage=You should receive an email shortly with further instructions.
+emailSendErrorMessage=Failed to send email, please try again later.
+
+accountUpdatedMessage=Your account has been updated.
+accountPasswordUpdatedMessage=Your password has been updated.
+
+noAccessMessage=No access
+
+invalidPasswordMinLengthMessage=Invalid password: minimum length {0}.
+invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} numerical digits.
+invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least {0} lower case characters.
+invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
+invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
+invalidPasswordNotUsernameMessage=Invalid password\: must not be equal to the username.
+
+failedToProcessResponseMessage=Failed to process response
+httpsRequiredMessage=HTTPS required
+realmNotEnabledMessage=Realm not enabled
+invalidRequestMessage=Invalid Request
+unknownLoginRequesterMessage=Unknown login requester
+loginRequesterNotEnabledMessage=Login requester not enabled
+bearerOnlyMessage=Bearer-only applications are not allowed to initiate browser login
+directGrantsOnlyMessage=Direct-grants-only clients are not allowed to initiate browser login
+invalidRedirectUriMessage=Invalid redirect uri
+unsupportedNameIdFormatMessage=Unsupported NameIDFormat
+invlidRequesterMessage=Invalid requester
+registrationNotAllowedMessage=Registration not allowed
+
+permissionNotApprovedMessage=Permission not approved.
+noRelayStateInResponseMessage=No relay state in response from identity provider [{0}].
+identityProviderAlreadyLinkedMessage=The identity returned by the identity provider [{0}] is already linked to another user.
+insufficientPermissionMessage=Insufficient permissions to link identities.
+couldNotProceedWithAuthenticationRequestMessage=Could not proceed with authentication request to identity provider.
+couldNotObtainTokenMessage=Could not obtain token from identity provider [{0}].
+unexpectedErrorRetrievingTokenMessage=Unexpected error when retrieving token from identity provider [{0}].
+unexpectedErrorHandlingResponseMessage=Unexpected error when handling response from identity provider [{0}].
+identityProviderAuthenticationFailedMessage=Authentication failed. Could not authenticate with identity provider [{0}].
+couldNotSendAuthenticationRequestMessage=Could not send authentication request to identity provider [{0}].
+unexpectedErrorHandlingRequestMessage=Unexpected error when handling authentication request to identity provider [{0}].
+invalidAccessCodeMessage=Invalid access code.
+sessionNotActiveMessage=Session not active.
+unknownCodeMessage=Unknown code, please login again through your application.
+invalidCodeMessage=Invalid code, please login again through your application.
+identityProviderUnexpectedErrorMessage=Unexpected error when authenticating with identity provider
+identityProviderNotFoundMessage=Could not find an identity provider with the identifier [{0}].
+realmSupportsNoCredentialsMessage=Realm [{0}] does not support any credential type.
+identityProviderNotUniqueMessage=Realm [{0}] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.
+emailVerifiedMessage=Your email address has been verified.
+
+locale_de=German
+locale_en=English
+
+poweredByKeycloak=Powered by Keycloak
+backToApplication=« Back to Application
+missingParameterMessage=Missing parameters\: {0}
+clientNotFoundMessage=Client not found.
+invalidParameterMessage=Invalid parameter\: {0}
diff --git a/forms/common-themes/src/main/resources/theme/base/login/register.ftl b/forms/common-themes/src/main/resources/theme/base/login/register.ftl
new file mode 100755
index 0000000..930a07d
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/register.ftl
@@ -0,0 +1,124 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout; section>
+ <#if section = "title">
+ ${msg("registerWithTitle",(realm.name!''))}
+ <#elseif section = "header">
+ ${msg("registerWithTitleHtml",(realm.name!''))}
+ <#elseif section = "form">
+ <form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post">
+ <#if !realm.registrationEmailAsUsername>
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="username" class="${properties.kcInputClass!}" name="username" value="${(register.formData.username!'')?html}" />
+ </div>
+ </div>
+ </#if>
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="firstName" class="${properties.kcInputClass!}" name="firstName" value="${(register.formData.firstName!'')?html}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="lastName" class="${properties.kcInputClass!}" name="lastName" value="${(register.formData.lastName!'')?html}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="email" class="${properties.kcInputClass!}" name="email" value="${(register.formData.email!'')?html}" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="password" id="password" class="${properties.kcInputClass!}" name="password" />
+ </div>
+ </div>
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="password" id="password-confirm" class="${properties.kcInputClass!}" name="password-confirm" />
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" class="${properties.kcInputClass!}" id="user.attributes.street" name="user.attributes.street"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" class="${properties.kcInputClass!}" id="user.attributes.locality" name="user.attributes.locality"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" class="${properties.kcInputClass!}" id="user.attributes.region" name="user.attributes.region"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" class="${properties.kcInputClass!}" id="user.attributes.postal_code" name="user.attributes.postal_code"/>
+ </div>
+ </div>
+ <div class="form-group">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
+ </div>
+
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" class="${properties.kcInputClass!}" id="user.attributes.country" name="user.attributes.country"/>
+ </div>
+ </div>
+
+
+ <div class="${properties.kcFormGroupClass!}">
+ <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+ <div class="${properties.kcFormOptionsWrapperClass!}">
+ <span><a href="${url.loginUrl}">${msg("backToLogin")}</a></span>
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+ <input class="btn btn-primary btn-lg" type="submit" value="${msg("doRegister")}"/>
+ </div>
+ </div>
+ </form>
+ </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/template.ftl b/forms/common-themes/src/main/resources/theme/base/login/template.ftl
new file mode 100644
index 0000000..907cfd5
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/login/template.ftl
@@ -0,0 +1,91 @@
+<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" class="${properties.kcHtmlClass!}">
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <#if properties.meta?has_content>
+ <#list properties.meta?split(' ') as meta>
+ <meta name="${meta?split('==')[0]}" content="${meta?split('==')[1]}"/>
+ </#list>
+ </#if>
+ <title><#nested "title"></title>
+ <link rel="icon" href="${url.resourcesPath}/img/favicon.ico" />
+ <#if properties.styles?has_content>
+ <#list properties.styles?split(' ') as style>
+ <link href="${url.resourcesPath}/${style}" rel="stylesheet" />
+ </#list>
+ </#if>
+ <#if properties.scripts?has_content>
+ <#list properties.scripts?split(' ') as script>
+ <script src="${url.resourcesPath}/${script}" type="text/javascript"></script>
+ </#list>
+ </#if>
+</head>
+
+<body class="${properties.kcBodyClass!}">
+ <div id="kc-logo"><div id="kc-logo-wrapper"></div></div>
+
+ <div id="kc-container" class="${properties.kcContainerClass!}">
+ <div id="kc-container-wrapper" class="${properties.kcContainerWrapperClass!}">
+
+ <div id="kc-header" class="${properties.kcHeaderClass!}">
+ <div id="kc-header-wrapper" class="${properties.kcHeaderWrapperClass!}"><#nested "header"></div>
+ </div>
+
+ <#if displayMessage && message?has_content>
+ <div id="kc-feedback" class="feedback-${message.type} ${properties.kcFeedBackClass!}">
+ <div id="kc-feedback-wrapper">
+ <span class="kc-feedback-text">${message.summary}</span>
+ </div>
+ </div>
+ <#else>
+ <div id="kc-feedback-placeholder" class="${properties.kcFeedBackPlaceholderClass!}">
+ <div id="kc-feedback-placeholder-wrapper"></div>
+ </div>
+ </#if>
+
+ <#if realm.internationalizationEnabled>
+ <div id="kc-locale" class="${properties.kcLocaleClass!}">
+ <div id="kc-locale-wrapper" class="${properties.kcLocaleWrapperClass!}">
+ <div class="kc-dropdown">
+ <a href="#">${locale.current}</a>
+ <ul>
+ <#list locale.supported as l>
+ <li class="kc-dropdown-item"><a href="${l.url}">${l.label}</a></li>
+ </#list>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </#if>
+
+ <div id="kc-content" class="${properties.kcContentClass!}">
+ <div id="kc-content-wrapper" class="${properties.kcContentWrapperClass!}">
+ <div id="kc-form" class="${properties.kcFormAreaClass!}">
+ <div id="kc-form-wrapper" class="${properties.kcFormAreaWrapperClass!}">
+ <#nested "form">
+ </div>
+ </div>
+
+ <#if displayInfo>
+ <div id="kc-info" class="${properties.kcInfoAreaClass!}">
+ <div id="kc-info-wrapper" class="${properties.kcInfoAreaWrapperClass!}">
+ <#nested "info">
+ </div>
+ </div>
+ </#if>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!--
+ <p class="powered">
+ <a href="http://www.keycloak.org">${msg("poweredByKeycloak")}</a>
+ </p>
+ </div>
+ -->
+</body>
+</html>
+</#macro>
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/email-verification.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/email-verification.ftl
new file mode 100644
index 0000000..5f2490b
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/email-verification.ftl
@@ -0,0 +1 @@
+${msg("emailVerificationBody",link, linkExpiration)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/event-login_error.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/event-login_error.ftl
new file mode 100644
index 0000000..7835c91
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/event-login_error.ftl
@@ -0,0 +1 @@
+${msg("eventLoginErrorBody",event.date,event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/event-remove_totp.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/event-remove_totp.ftl
new file mode 100644
index 0000000..8930b64
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/event-remove_totp.ftl
@@ -0,0 +1 @@
+${msg("eventRemoveTotpBody",event.date, event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/event-update_password.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/event-update_password.ftl
new file mode 100644
index 0000000..754daac
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/event-update_password.ftl
@@ -0,0 +1 @@
+${msg("eventUpdatePasswordBody",event.date, event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/event-update_totp.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/event-update_totp.ftl
new file mode 100644
index 0000000..3a7b0f7
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/event-update_totp.ftl
@@ -0,0 +1 @@
+${msg("eventUpdateTotpBody",event.date, event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties
new file mode 100644
index 0000000..60228c3
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_de.properties
@@ -0,0 +1,12 @@
+emailVerificationSubject=E-Mail verifizieren
+passwordResetSubject=Passwort zur�ckzusetzen
+passwordResetBody=Jemand hat angefordert Ihr Keycloak Passwort zur�ckzusetzen. Falls das Sie waren, dann klicken Sie auf den folgenden Link um das Passwort zur�ckzusetzen.\n\n{0}\n\nDieser Link wird in {1} Minuten ablaufen.\n\nFalls Sie das Passwort nicht zur�cksetzen m�chten, dann k�nnen Sie diese E-Mail ignorieren.
+emailVerificationBody=Jemand hat ein Keycloak Konto mit dieser E-Mail Adresse erstellt. Fall das Sie waren, dann klicken Sie auf den Link um die E-Mail Adresse zu verifizieren.\n\n{0}\n\nDieser Link wird in {1} Minuten ablaufen.\n\nFalls Sie dieses Konto nicht erstellt haben, dann k�nnen sie diese Nachricht ignorieren.
+eventLoginErrorSubject=Fehlgeschlagene Anmeldung
+eventLoginErrorBody=Jemand hat um {0} von {1} versucht sich mit ihrem Konto anzumelden. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.
+eventRemoveTotpSubject=TOTP Entfernt
+eventRemoveTotpBody=TOTP wurde von ihrem Konto am {0} von {1} entfernt. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.
+eventUpdatePasswordSubject=Passwort Aktualisiert
+eventUpdatePasswordBody=Ihr Passwort wurde am {0} von {1} ge�ndert. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.
+eventUpdateTotpSubject=TOTP Aktualisiert
+eventUpdateTotpBody=TOTP wurde am {0} von {1} ge�ndert. Falls das nicht Sie waren, dann kontaktieren Sie bitte Ihren Admin.
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties
new file mode 100755
index 0000000..c23daf6
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/messages/messages_en.properties
@@ -0,0 +1,12 @@
+emailVerificationSubject=Verify email
+emailVerificationBody=Someone has created a Keycloak account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn''t create this account, just ignore this message.
+passwordResetSubject=Reset password
+passwordResetBody=Someone just requested to change your Keycloak account''s password. If this was you, click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
+eventLoginErrorSubject=Login error
+eventLoginErrorBody=A failed login attempt was dettected to your account on {0} from {1}. If this was not you, please contact an admin.
+eventRemoveTotpSubject=Remove TOTP
+eventRemoveTotpBody=TOTP was removed from your account on {0} from {1}. If this was not you, please contact an admin.
+eventUpdatePasswordSubject=Update password
+eventUpdatePasswordBody=Your password was changed on {0} from {1}. If this was not you, please contact an admin.
+eventUpdateTotpSubject=Update TOTP
+eventUpdateTotpBody=TOTP was updated for your account on {0} from {1}. If this was not you, please contact an admin.
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/email/password-reset.ftl b/forms/common-themes/src/main/resources/theme/keycloak/email/password-reset.ftl
new file mode 100644
index 0000000..d7150d6
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/email/password-reset.ftl
@@ -0,0 +1 @@
+${msg("passwordResetBody",link, linkExpiration)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/index.html b/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/index.html
new file mode 100755
index 0000000..dd0b909
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/index.html
@@ -0,0 +1,50 @@
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright (c) 2011, Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags. See the copyright.txt file in the
+ ~ distribution for a full listing of individual contributors.
+ ~
+ ~ This is free software; you can redistribute it and/or modify it
+ ~ under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ This software is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public
+ ~ License along with this software; if not, write to the Free
+ ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ -->
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<html>
+<head>
+ <title>Welcome to Keycloak</title>
+ <link rel="shortcut icon" href="welcome-content/favicon.ico" type="image/x-icon">
+ <link rel="StyleSheet" href="welcome-content/keycloak.css" type="text/css">
+</head>
+
+<body>
+<div class="wrapper">
+ <div class="content">
+ <div class="logo">
+ <img src="welcome-content/keycloak_logo.png" alt="Keycloak" border="0" />
+ </div>
+ <h1>Welcome to Keycloak</h1>
+
+ <h3>Your Keycloak is running.</h3>
+
+ <p><a href="http://www.keycloak.org/docs">Documentation</a> | <a href="admin/">Administration Console</a> </p>
+
+ <p><a href="http://www.keycloak.org">Keycloak Project</a> |
+ <a href="https://lists.jboss.org/mailman/listinfo/keycloak-user">Mailing List</a> |
+ <a href="https://issues.jboss.org/browse/KEYCLOAK">Report an issue</a></p>
+ <p class="logos"><a href="http://www.jboss.org"><img src="welcome-content/jboss_community.png" alt="JBoss and JBoss Community" width="254" height="31" border="0"></a></p>
+ </div>
+</div>
+</body>
+</html>
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/jboss_community.png b/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/jboss_community.png
new file mode 100644
index 0000000..946e8f9
Binary files /dev/null and b/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/jboss_community.png differ
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/keycloak.css b/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/keycloak.css
new file mode 100644
index 0000000..5152862
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/keycloak/welcome/resources/keycloak.css
@@ -0,0 +1,65 @@
+/*
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright (c) 2011, Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags. See the copyright.txt file in the
+ ~ distribution for a full listing of individual contributors.
+ ~
+ ~ This is free software; you can redistribute it and/or modify it
+ ~ under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ This software is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public
+ ~ License along with this software; if not, write to the Free
+ ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+
+body {
+ /*background: url(bkg.gif) repeat-x;*/
+ color: #5B5B5B;
+ font-family: "Lucida Sans Unicode", "Lucida Grande", Geneva, Verdana, Arial, sans-serif;
+ margin-top: 100px;
+ text-align: center;
+}
+.wrapper {
+ margin-left: auto;
+ margin-right: auto;
+ width: 50em;
+ text-align: left;
+}
+a {
+ text-decoration: none;
+ color: #5e8a9a;
+}
+h1 {
+ padding-top: 20px;
+ color: #7b1e1e;
+}
+a:hover {
+ text-decoration: underline;
+ color: #8ec6d9;
+}
+.content {
+ margin: 0;
+ padding: 0;
+}
+.dualbrand {
+ padding-top: 20px;
+}
+.wildfly {
+ float:left;
+ margin-left: 10px;
+}
+.logo{
+ margin-bottom: 10px;
+}
+.note {
+ font-size: 8pt;
+ color: #aaaaaa;
+}
diff --git a/forms/common-themes/src/main/resources/theme/patternfly/account/resources/img/favicon.ico b/forms/common-themes/src/main/resources/theme/patternfly/account/resources/img/favicon.ico
new file mode 100644
index 0000000..b30dce5
Binary files /dev/null and b/forms/common-themes/src/main/resources/theme/patternfly/account/resources/img/favicon.ico differ
diff --git a/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css
new file mode 100644
index 0000000..345f594
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/css/login.css
@@ -0,0 +1,322 @@
+.kc-dropdown{
+ position: relative;
+}
+.kc-dropdown > a{
+ position: absolute;
+ right: 0px;
+ display:block;
+ padding: 11px 10px 12px;
+ line-height: 12px;
+ font-size: 12px;
+ color: #fff !important;
+ text-decoration: none;
+}
+.kc-dropdown > a::after{
+ content: "\2c5";
+ margin-left: 4px;
+}
+.kc-dropdown:hover > a{
+ background-color: rgba(0,0,0,0.2);
+}
+.kc-dropdown ul li a{
+ padding: 1px 11px;
+ font-size: 12px;
+ color: #000 !important;
+ border: 1px solid #fff;
+ text-decoration: none;
+ display:block;
+ line-height: 20px;
+}
+.kc-dropdown ul li a:hover{
+ color: #4d5258;
+ background-color: #d4edfa;
+ border-color: #b3d3e7;
+}
+.kc-dropdown ul{
+ position: absolute;
+ right: 0px;
+ top: 35px;
+ z-index: 2000;
+ list-style:none;
+ display:none;
+ padding: 5px 0px;
+ margin: 0px;
+ background-color: #fff !important;
+ border: 1px solid #b6b6b6;
+ border-radius: 1px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
+ min-width: 100px;
+}
+.kc-dropdown:hover ul{
+ display:block;
+}
+
+.login-pf .container {
+ padding-top: 40px;
+}
+
+.login-pf a:hover {
+ color: #0099d3;
+}
+
+#kc-logo {
+ width: 100%;
+}
+
+#kc-logo-wrapper {
+ background-image: url("../img/keycloak-logo.png");
+ background-repeat: no-repeat;
+ background-position: top right;
+
+ height: 37px;
+
+ margin: 50px;
+}
+
+#kc-header {
+ overflow: visible;
+ padding-left: 80px;
+ white-space: nowrap;
+}
+
+#kc-header-wrapper {
+ font-size: 26px;
+ text-transform: uppercase;
+ line-height: 1.2em;
+ margin-bottom: 15px;
+}
+
+#kc-container-wrapper {
+ bottom: 13%;
+ position: absolute;
+ width: 100%;
+}
+
+#kc-content {
+ position: relative;
+}
+
+#kc-content-wrapper {
+ overflow-y: hidden;
+}
+
+#kc-info {
+ padding-bottom: 200px;
+ margin-bottom: -200px;
+}
+
+#kc-info-wrapper {
+ font-size: 13px;
+}
+
+#kc-form-options span {
+ display: block;
+}
+
+#kc-login {
+ float: right;
+ margin-left: 10px;
+}
+
+#kc-feedback-wrapper {
+ display: inline-block;
+ width: auto;
+ background-position: left bottom;
+ background-repeat: no-repeat;
+ padding-bottom: 10px;
+}
+
+#kc-feedback span {
+ display: block;
+ padding: 0.90909090909091em 3.63636363636364em;
+ border-style: solid;
+ border-width: 1px 1px 0px 1px;
+ background-repeat: no-repeat;
+ background-position: 1.27272727272727em center;
+ font-weight: normal;
+ line-height: 1.4em;
+ border-radius: 2px;
+ color: #4d5258;
+ margin-bottom: 0;
+}
+
+.feedback-error #kc-feedback-wrapper {
+ background-image: url(../img/feedback-error-arrow-down.png);
+}
+.feedback-error span {
+ border-color: #b91415;
+ background-image: url(../img/feedback-error-sign.png);
+ background-color: #f8e7e7;
+}
+
+.feedback-success #kc-feedback-wrapper {
+ background-image: url(../img/feedback-success-arrow-down.png);
+}
+.feedback-success span {
+ border-color: #4b9e39;
+ background-image: url(../img/feedback-success-sign.png);
+ background-color: #e4f1e1;
+}
+
+.feedback-warning #kc-feedback-wrapper {
+ background-image: url(../img/feedback-warning-arrow-down.png);
+}
+.feedback-warning span {
+ border-color: #f17528;
+ background-image: url(../img/feedback-warning-sign.png);
+ background-color: #fef1e9;
+}
+
+#kc-registration {
+ margin-bottom: 15px;
+}
+
+/* TOTP */
+
+ol#kc-totp-settings {
+ margin: 0;
+ padding: 0;
+}
+
+ol#kc-totp-settings li {
+ margin-top: 1em;
+}
+
+ol#kc-totp-settings li:first-of-type {
+ margin-top: 0;
+}
+
+/* OAuth */
+
+#kc-oauth h3 {
+ margin-top: 0;
+}
+
+#kc-oauth ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+#kc-oauth ul li {
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ font-size: 12px;
+ padding: 10px 30px;
+}
+
+#kc-oauth ul li:first-of-type {
+ border-top: 0;
+}
+
+#kc-oauth .kc-role {
+ display: inline-block;
+ width: 50%;
+}
+
+/* Code */
+#kc-code textarea {
+ width: 100%;
+ height: 8em;
+}
+
+/* Social */
+
+#kc-social-providers ul {
+ padding: 0;
+ margin: 0;
+}
+
+#kc-social-providers li {
+ display: block;
+ margin-top: 5px;
+}
+
+#kc-social-providers li:first-of-type {
+ margin-top: 0;
+}
+
+.zocial {
+ width: 125px;
+}
+
+.zocial:hover {
+ color: #fff !important;
+}
+
+.zocial.facebook,
+.zocial.github,
+.zocial.google,
+.zocial.twitter {
+ background-image: none;
+ border-radius: 2px;
+
+ font-family: "Open Sans", sans-serif;
+ font-weight: normal;
+
+ box-shadow: none;
+ text-shadow: none;
+}
+
+.zocial.google {
+ background-color: #dd4b39 !important;
+}
+.zocial.google .text:after {
+ content: "+";
+}
+
+.zocial.facebook:hover,
+.zocial.github:hover,
+.zocial.google:hover,
+.zocial.twitter:hover {
+ background-image: linear-gradient(rgba(0, 0, 0, 0.2) 0%, rgba(0, 0, 0, 0.2) 100%) !important;
+}
+
+@media (max-width: 767px) {
+ #kc-logo-wrapper {
+ margin-top: 30px;
+ margin-right: 15px;
+ }
+
+ #kc-header {
+ padding-left: 15px;
+ padding-right: 15px;
+ float: none;
+ }
+
+ #kc-feedback {
+ padding-left: 15px;
+ padding-right: 15px;
+ float: none;
+ }
+
+ #kc-form {
+ float: none;
+ }
+
+ #kc-info-wrapper {
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ margin-top: 15px;
+ padding-top: 15px;
+ padding-left: 0px;
+ padding-right: 15px;
+ }
+
+ #kc-social-providers li {
+ display: inline-block;
+ margin-right: 5px;
+ }
+
+ .login-pf .container {
+ padding-top: 15px;
+ padding-bottom: 15px;
+ }
+}
+
+@media (max-height: 500px) {
+ #kc-container-wrapper {
+ position: inherit;
+ float: none;
+ }
+}
diff --git a/forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/favicon.ico b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/favicon.ico
new file mode 100644
index 0000000..b30dce5
Binary files /dev/null and b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/favicon.ico differ
diff --git a/forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/keycloak-logo.png b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/keycloak-logo.png
new file mode 100644
index 0000000..9555748
Binary files /dev/null and b/forms/common-themes/src/main/resources/theme/patternfly/login/resources/img/keycloak-logo.png differ
diff --git a/forms/common-themes/src/main/resources/theme/patternfly/login/theme.properties b/forms/common-themes/src/main/resources/theme/patternfly/login/theme.properties
new file mode 100644
index 0000000..25427a7
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/patternfly/login/theme.properties
@@ -0,0 +1,29 @@
+parent=base
+import=common/keycloak
+
+styles=lib/patternfly/css/patternfly.css lib/zocial/zocial.css css/login.css
+meta=viewport==width=device-width,initial-scale=1
+
+kcHtmlClass=login-pf
+
+kcContentClass=col-sm-12 col-md-12 col-lg-12 container
+kcContentWrapperClass=row
+
+kcHeaderClass=col-xs-12 col-sm-7 col-md-6 col-lg-5
+kcFeedBackClass=col-xs-12 col-sm-4 col-md-5 col-lg-6
+kcFeedBackPlaceholderClass=col-xs-12 col-sm-4 col-md-5 col-lg-6
+kcLocaleClass=col-xs-12 col-sm-1
+
+kcFormAreaClass=col-xs-12 col-sm-8 col-md-8 col-lg-6 login
+
+kcFormClass=form-horizontal
+kcFormGroupClass=form-group
+kcLabelClass=control-label
+kcLabelWrapperClass=col-xs-12 col-sm-12 col-md-4 col-lg-3
+kcInputClass=form-control
+kcInputWrapperClass=col-xs-12 col-sm-12 col-md-8 col-lg-9
+kcFormOptionsClass=col-xs-4 col-sm-5 col-md-offset-4 col-md-4 col-lg-offset-3 col-lg-5
+kcFormButtonsClass=col-xs-8 col-sm-7 col-md-4 col-lg-4 submit
+kcTextareaClass=form-control
+
+kcInfoAreaClass=col-xs-12 col-sm-4 col-md-4 col-lg-6 details
\ No newline at end of file
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 44c8bad..9aca77f 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -6,6 +6,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.freemarker.*;
+import org.keycloak.freemarker.beans.AdvancedMessageFormatterMethod;
import org.keycloak.freemarker.beans.MessageFormatterMethod;
import org.keycloak.login.LoginFormsPages;
import org.keycloak.login.LoginFormsProvider;
@@ -239,6 +240,7 @@ import java.util.concurrent.TimeUnit;
break;
case OAUTH_GRANT:
attributes.put("oauth", new OAuthGrantBean(accessCode, clientSession, client, realmRolesRequested, resourceRolesRequested, this.accessRequestMessage));
+ attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messages));
break;
case CODE:
attributes.put(OAuth2Constants.CODE, new CodeBean(accessCode, messageType == MessageType.ERROR ? message : null));
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java
index e760b3c..2fd87ae 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java
@@ -13,7 +13,7 @@ import java.io.Serializable;
/**
* @author pedroigor
*/
-@Table(name="CLIENT_IDENTITY_PROVIDER_MAPPING")
+@Table(name="CLIENT_IDENTITY_PROV_MAPPING")
@Entity
@IdClass(ClientIdentityProviderMappingEntity.Key.class)
public class ClientIdentityProviderMappingEntity {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
index d2e159a..00671fb 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
@@ -51,8 +51,8 @@ public class IdentityProviderEntity {
private boolean authenticateByDefault;
@ElementCollection
- @MapKeyColumn(name="name")
- @Column(name="value", columnDefinition = "TEXT")
+ @MapKeyColumn(name="NAME")
+ @Column(name="VALUE", columnDefinition = "TEXT")
@CollectionTable(name="IDENTITY_PROVIDER_CONFIG", joinColumns={ @JoinColumn(name="IDENTITY_PROVIDER_ID") })
private Map<String, String> config;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index 563791e..9fbf077 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -47,7 +47,7 @@ public class RealmEntity {
protected String sslRequired;
@Column(name="REGISTRATION_ALLOWED")
protected boolean registrationAllowed;
- @Column(name = "REGISTRATION_EMAIL_AS_USERNAME")
+ @Column(name = "REG_EMAIL_AS_USERNAME")
protected boolean registrationEmailAsUsername;
@Column(name="PASSWORD_CRED_GRANT_ALLOWED")
protected boolean passwordCredentialGrantAllowed;
@@ -104,7 +104,7 @@ public class RealmEntity {
List<UserFederationProviderEntity> userFederationProviders = new ArrayList<UserFederationProviderEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
- @JoinTable(name="REALM_APPLICATION", joinColumns={ @JoinColumn(name="APPLICATION_ID") }, inverseJoinColumns={ @JoinColumn(name="REALM_ID") })
+ @JoinTable(name="REALM_APPLICATION", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name="APPLICATION_ID") })
Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
index 1de58a8..4b4e80f 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
@@ -69,7 +69,7 @@ public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEnti
// Realm might be already removed at this point
if (realmEntity != null) {
- mongoStore.pullItemFromList(realmEntity, "defaultRoles", getId(), invContext);
+ mongoStore.pullItemFromList(realmEntity, "defaultRoles", getName(), invContext);
}
}
@@ -79,7 +79,7 @@ public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEnti
// Application might be already removed at this point
if (appEntity != null) {
- mongoStore.pullItemFromList(appEntity, "defaultRoles", getId(), invContext);
+ mongoStore.pullItemFromList(appEntity, "defaultRoles", getName(), invContext);
}
}
diff --git a/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/KeycloakEventBridge.java b/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/KeycloakEventBridge.java
index 3e8be5a..1fd7f15 100755
--- a/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/KeycloakEventBridge.java
+++ b/picketlink/keycloak-picketlink-ldap/src/main/java/org/keycloak/picketlink/idm/KeycloakEventBridge.java
@@ -1,9 +1,11 @@
package org.keycloak.picketlink.idm;
import org.jboss.logging.Logger;
+import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.event.CredentialUpdatedEvent;
import org.picketlink.idm.event.EventBridge;
+import org.picketlink.idm.internal.ContextualIdentityManager;
import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
import org.picketlink.idm.ldap.internal.LDAPOperationManager;
import org.picketlink.idm.model.basic.User;
@@ -37,9 +39,10 @@ public class KeycloakEventBridge implements EventBridge {
if (updateUserAccountAfterPasswordUpdate && event instanceof CredentialUpdatedEvent) {
CredentialUpdatedEvent credEvent = ((CredentialUpdatedEvent) event);
PartitionManager partitionManager = credEvent.getPartitionMananger();
- IdentityContext identityCtx = (IdentityContext)partitionManager.createIdentityManager();
+ ContextualIdentityManager identityManager = (ContextualIdentityManager) partitionManager.createIdentityManager();
+ IdentityContext identityCtx = identityManager.getIdentityContext();
- CredentialStore store = ((StoreSelector)partitionManager).getStoreForCredentialOperation(identityCtx, credEvent.getCredential().getClass());
+ CredentialStore store = identityManager.getStoreSelector().getStoreForCredentialOperation(identityCtx, credEvent.getCredential().getClass());
if (store instanceof LDAPIdentityStore) {
LDAPIdentityStore ldapStore = (LDAPIdentityStore)store;
LDAPOperationManager operationManager = ldapStore.getOperationManager();
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/account/resources/img/logo.png b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/account/resources/img/logo.png
new file mode 100755
index 0000000..d55812d
Binary files /dev/null and b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/account/resources/img/logo.png differ
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/css/styles.css b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/css/styles.css
new file mode 100755
index 0000000..84300fe
--- /dev/null
+++ b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/css/styles.css
@@ -0,0 +1,45 @@
+@import url("../lib/patternfly/css/patternfly.css");
+@import url("../lib/select2-3.4.1/select2.css");
+
+@import url("admin-console.css");
+@import url("tables.css");
+@import url("sprites.css");
+
+.navbar-pf {
+ background-image: url('../img/header-bkgrnd.png');
+ border-top: 3px solid rgba(255, 255, 255, 0.15);
+}
+
+.navbar-pf .navbar-primary {
+ font-size: 13px;
+ background-image: -webkit-linear-gradient(top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,.4) 100%);
+ background-image: linear-gradient(to bottom, rgba(0,0,0,.1) 0%, rgba(0,0,0,.4) 100%);
+}
+
+.navbar-pf .navbar-header {
+ border-bottom: 1px solid rgba(255,255,255,.15);
+}
+
+.navbar-pf .navbar-primary li.dropdown.context > a,
+.navbar-pf .navbar-primary li.dropdown.context > a:hover,
+.navbar-pf .navbar-primary li.dropdown.context.open > a,
+.navbar-pf .navbar-primary > .active > a,
+.navbar-pf .navbar-primary > .active > a:hover{
+ background-color: rgba(0,0,0,0.1);
+ background-image: -webkit-linear-gradient(top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.1) 100%);
+ background-image: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.1) 100%);
+ border-bottom: none;
+ border-right-color: rgba(255,255,255,0.15);
+ border-top-color: rgba(255,255,255,0.05);
+}
+
+/* this is what I added */
+
+.navbar-title {
+ background-image: url('../img/logo.png');
+ height: 65px;
+ background-repeat: no-repeat;
+ width: 203px;
+ margin: 3px 10px 5px;
+ text-indent: -99999px;
+}
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/img/logo.png b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/img/logo.png
new file mode 100755
index 0000000..d55812d
Binary files /dev/null and b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/resources/img/logo.png differ
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/theme.properties b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/theme.properties
new file mode 100755
index 0000000..e328b3f
--- /dev/null
+++ b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/admin/theme.properties
@@ -0,0 +1,4 @@
+parent=keycloak
+import=common/keycloak
+
+styles= ../keycloak/css/styles.css css/styles.css
\ No newline at end of file
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/login/resources/img/logo.png b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/login/resources/img/logo.png
new file mode 100755
index 0000000..d55812d
Binary files /dev/null and b/project-integrations/aerogear-ups/auth-server/src/main/resources/theme/aerogear/login/resources/img/logo.png differ
README.md 2(+1 -1)
diff --git a/README.md b/README.md
index 3ba55db..1d05652 100755
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ It can be used for social applications as well as enterprise applications. It i
Here's some of the features:
* SSO and Single Log Out for browser applications
-* Social Broker. Enable Google, Facebook, Yahoo, Twitter social login with no code required.
+* Social Broker. Enable Google, Facebook, Yahoo, Twitter, GitHub, LinkedIn social login with no code required.
* Optional LDAP/Active Directory integration
* Optional User Registration
* Password and TOTP support (via Google Authenticator or FreeOTP). Client cert auth coming soon.
social/linkedin/.gitignore 1(+1 -0)
diff --git a/social/linkedin/.gitignore b/social/linkedin/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/social/linkedin/.gitignore
@@ -0,0 +1 @@
+/target/
social/linkedin/pom.xml 40(+40 -0)
diff --git a/social/linkedin/pom.xml b/social/linkedin/pom.xml
new file mode 100755
index 0000000..d9fd603
--- /dev/null
+++ b/social/linkedin/pom.xml
@@ -0,0 +1,40 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>keycloak-social-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.2.0.Beta1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>jar</packaging>
+
+ <artifactId>keycloak-social-linkedin</artifactId>
+ <name>Keycloak Social LinkedIn</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-social-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-broker-oidc</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
new file mode 100755
index 0000000..298612e
--- /dev/null
+++ b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.social.linkedin;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+
+import org.codehaus.jackson.JsonNode;
+import org.jboss.logging.Logger;
+import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.social.SocialIdentityProvider;
+
+/**
+ * LinkedIn social provider. See https://developer.linkedin.com/docs/oauth2
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public class LinkedInIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
+
+ private static final Logger log = Logger.getLogger(LinkedInIdentityProvider.class);
+
+ public static final String AUTH_URL = "https://www.linkedin.com/uas/oauth2/authorization";
+ public static final String TOKEN_URL = "https://www.linkedin.com/uas/oauth2/accessToken";
+ public static final String PROFILE_URL = "https://api.linkedin.com/v1/people/~:(id,formatted-name,email-address,public-profile-url)?format=json";
+ public static final String DEFAULT_SCOPE = "r_basicprofile r_emailaddress";
+
+ public LinkedInIdentityProvider(OAuth2IdentityProviderConfig config) {
+ super(config);
+ config.setAuthorizationUrl(AUTH_URL);
+ config.setTokenUrl(TOKEN_URL);
+ config.setUserInfoUrl(PROFILE_URL);
+ }
+
+ @Override
+ protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+ log.debug("doGetFederatedIdentity()");
+ try {
+ JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
+
+ FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "id"));
+
+ user.setUsername(extractUsernameFromProfileURL(getJsonProperty(profile, "publicProfileUrl")));
+ user.setName(getJsonProperty(profile, "formattedName"));
+ user.setEmail(getJsonProperty(profile, "emailAddress"));
+
+ return user;
+ } catch (Exception e) {
+ throw new IdentityBrokerException("Could not obtain user profile from github.", e);
+ }
+ }
+
+ protected static String extractUsernameFromProfileURL(String profileURL) {
+ if (isNotBlank(profileURL)) {
+
+ try {
+ log.debug("go to extract username from profile URL " + profileURL);
+ URL u = new URL(profileURL);
+ String path = u.getPath();
+ if (isNotBlank(path) && path.length() > 1) {
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ String[] pe = path.split("/");
+ if (pe.length >= 2) {
+ return URLDecoder.decode(pe[1], "UTF-8");
+ } else {
+ log.warn("LinkedIn profile URL path is without second part: " + profileURL);
+ }
+ } else {
+ log.warn("LinkedIn profile URL is without path part: " + profileURL);
+ }
+ } catch (MalformedURLException e) {
+ log.warn("LinkedIn profile URL is malformed: " + profileURL);
+ } catch (Exception e) {
+ log.warn("LinkedIn profile URL " + profileURL + " username extraction failed due: " + e.getMessage());
+ }
+ }
+ return null;
+ }
+
+ private static boolean isNotBlank(String s) {
+ return s != null && s.trim().length() > 0;
+ }
+
+ @Override
+ protected String getDefaultScopes() {
+ return DEFAULT_SCOPE;
+ }
+}
diff --git a/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProviderFactory.java b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProviderFactory.java
new file mode 100644
index 0000000..958a513
--- /dev/null
+++ b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProviderFactory.java
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.social.linkedin;
+
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.social.SocialIdentityProviderFactory;
+
+/**
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public class LinkedInIdentityProviderFactory extends AbstractIdentityProviderFactory<LinkedInIdentityProvider>
+ implements SocialIdentityProviderFactory<LinkedInIdentityProvider> {
+
+ public static final String PROVIDER_ID = "linkedin";
+
+ @Override
+ public String getName() {
+ return "LinkedIn";
+ }
+
+ @Override
+ public LinkedInIdentityProvider create(IdentityProviderModel model) {
+ return new LinkedInIdentityProvider(new OAuth2IdentityProviderConfig(model));
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/social/linkedin/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory b/social/linkedin/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
new file mode 100644
index 0000000..5ffef97
--- /dev/null
+++ b/social/linkedin/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.social.linkedin.LinkedInIdentityProviderFactory
\ No newline at end of file
social/pom.xml 1(+1 -0)
diff --git a/social/pom.xml b/social/pom.xml
index 386a215..ded7c60 100755
--- a/social/pom.xml
+++ b/social/pom.xml
@@ -20,6 +20,7 @@
<module>google</module>
<module>twitter</module>
<module>facebook</module>
+ <module>linkedin</module>
</modules>
</project>
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
index 73c78e0..79b36de 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
@@ -47,7 +47,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -292,7 +291,7 @@ public class KeycloakServer {
di.setDeploymentName("Keycloak");
di.setDefaultServletConfig(new DefaultServletConfig(true));
- di.addWelcomePage("theme/welcome/keycloak/resources/index.html");
+ di.addWelcomePage("theme/keycloak/welcome/resources/index.html");
FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
di.addFilter(filter);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index fb78299..00b071e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -207,11 +207,11 @@ public class AccountTest {
changePasswordPage.changePassword("password", "new-password", "new-password2");
- Assert.assertEquals("Password confirmation doesn't match", profilePage.getError());
+ Assert.assertEquals("Password confirmation doesn't match.", profilePage.getError());
changePasswordPage.changePassword("password", "new-password", "new-password");
- Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
@@ -256,7 +256,7 @@ public class AccountTest {
changePasswordPage.changePassword("password", "new-password", "new-password");
- Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
} finally {
@@ -318,7 +318,7 @@ public class AccountTest {
profilePage.updateProfile("New first", "New last", "new@email.com");
- Assert.assertEquals("Your account has been updated", profilePage.getSuccess());
+ Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
@@ -341,7 +341,7 @@ public class AccountTest {
// Error with false code
totpPage.configure(totp.generate(totpPage.getTotpSecret() + "123"));
- Assert.assertEquals("Invalid authenticator code", profilePage.getError());
+ Assert.assertEquals("Invalid authenticator code.", profilePage.getError());
totpPage.configure(totp.generate(totpPage.getTotpSecret()));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java
index 2dd3164..84bbc9c 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderModelTest.java
@@ -24,6 +24,7 @@ import org.keycloak.social.facebook.FacebookIdentityProviderFactory;
import org.keycloak.social.github.GitHubIdentityProviderFactory;
import org.keycloak.social.google.GoogleIdentityProviderFactory;
import org.keycloak.social.twitter.TwitterIdentityProviderFactory;
+import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory;
import org.keycloak.testsuite.model.AbstractModelTest;
import java.util.Collections;
@@ -47,6 +48,7 @@ public abstract class AbstractIdentityProviderModelTest extends AbstractModelTes
this.expectedProviders.add(FacebookIdentityProviderFactory.PROVIDER_ID);
this.expectedProviders.add(GitHubIdentityProviderFactory.PROVIDER_ID);
this.expectedProviders.add(TwitterIdentityProviderFactory.PROVIDER_ID);
+ this.expectedProviders.add(LinkedInIdentityProviderFactory.PROVIDER_ID);
this.expectedProviders = Collections.unmodifiableSet(this.expectedProviders);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 2630878..e6dbfcd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -176,6 +176,9 @@ public abstract class AbstractIdentityProviderTest {
// authenticated and redirected to app
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
// check correct user is created with email as username and bound to correct federated identity
RealmModel realm = getRealm();
@@ -218,6 +221,9 @@ public abstract class AbstractIdentityProviderTest {
authenticateWithIdentityProvider(identityProviderModel, "test-user-noemail");
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
// check correct user is created with username from provider as email is not available
RealmModel realm = getRealm();
UserModel federatedUser = getFederatedUser();
@@ -562,6 +568,9 @@ public abstract class AbstractIdentityProviderTest {
doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail);
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
RealmModel realm = getRealm();
Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
@@ -610,9 +619,12 @@ public abstract class AbstractIdentityProviderTest {
UserSessionStatus userSessionStatus = retrieveSessionStatus();
IDToken idToken = userSessionStatus.getIdToken();
KeycloakSession samlServerSession = brokerServerRule.startSession();
- RealmModel brokerRealm = samlServerSession.realms().getRealm("realm-with-broker");
-
- return samlServerSession.users().getUserById(idToken.getSubject(), brokerRealm);
+ try {
+ RealmModel brokerRealm = samlServerSession.realms().getRealm("realm-with-broker");
+ return samlServerSession.users().getUserById(idToken.getSubject(), brokerRealm);
+ } finally {
+ brokerServerRule.stopSession(samlServerSession, false);
+ }
}
protected void doAfterProviderAuthentication() {
@@ -677,7 +689,7 @@ public abstract class AbstractIdentityProviderTest {
this.session.users().removeFederatedIdentity(realm, user, fedIdentity.getIdentityProvider());
}
- if (!user.getUsername().equals("pedroigor")) {
+ if (!"pedroigor".equals(user.getUsername())) {
this.session.users().removeUser(realm, user);
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
index d2caacf..f596f1b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
@@ -38,6 +38,8 @@ import org.keycloak.social.google.GoogleIdentityProvider;
import org.keycloak.social.google.GoogleIdentityProviderFactory;
import org.keycloak.social.twitter.TwitterIdentityProvider;
import org.keycloak.social.twitter.TwitterIdentityProviderFactory;
+import org.keycloak.social.linkedin.LinkedInIdentityProvider;
+import org.keycloak.social.linkedin.LinkedInIdentityProviderFactory;
import java.io.IOException;
import java.util.HashSet;
@@ -160,6 +162,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertGitHubIdentityProviderConfig(identityProvider);
} else if (TwitterIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
assertTwitterIdentityProviderConfig(identityProvider);
+ } else if (LinkedInIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
+ assertLinkedInIdentityProviderConfig(identityProvider);
} else {
continue;
}
@@ -257,6 +261,23 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals(GitHubIdentityProvider.PROFILE_URL, config.getUserInfoUrl());
}
+ private void assertLinkedInIdentityProviderConfig(IdentityProviderModel identityProvider) {
+ LinkedInIdentityProvider gitHubIdentityProvider = new LinkedInIdentityProviderFactory().create(identityProvider);
+ OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig();
+
+ assertEquals("model-linkedin", config.getAlias());
+ assertEquals(LinkedInIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
+ assertEquals(true, config.isEnabled());
+ assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(false, config.isStoreToken());
+ assertEquals("clientId", config.getClientId());
+ assertEquals("clientSecret", config.getClientSecret());
+ assertEquals(LinkedInIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
+ assertEquals(LinkedInIdentityProvider.TOKEN_URL, config.getTokenUrl());
+ assertEquals(LinkedInIdentityProvider.PROFILE_URL, config.getUserInfoUrl());
+ }
+
private void assertTwitterIdentityProviderConfig(IdentityProviderModel identityProvider) {
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
index 66e4b71..1f31854 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
@@ -33,6 +33,11 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
}
+
+ @Override
+ protected String[] getTestRealms() {
+ return new String[] { "realm-with-oidc-identity-provider" };
+ }
};
@WebResource
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
index 2c57f04..4b235eb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
@@ -38,6 +38,11 @@ public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-saml.json"));
}
+
+ @Override
+ protected String[] getTestRealms() {
+ return new String[] { "realm-with-saml-idp-basic" };
+ }
};
@Override
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
index 124d7f0..5c7ac76 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
@@ -37,6 +37,11 @@ public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityP
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-saml-with-signature.json"));
}
+
+ @Override
+ protected String[] getTestRealms() {
+ return new String[] { "realm-with-saml-signed-idp" };
+ }
};
@Override
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
index 9d59ad9..af994f4 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
@@ -146,7 +146,7 @@ public abstract class AbstractKerberosTest {
// Successfully change password now
changePasswordPage.changePassword("theduke", "newPass", "newPass");
- Assert.assertTrue(driver.getPageSource().contains("Your password has been updated"));
+ Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
// Login with old password doesn't work, but with new password works
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
index 5f484dc..cfaae07 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
@@ -209,7 +209,7 @@ public class FederationProvidersIntegrationTest {
loginPage.login("johnkeycloak", "Password1");
changePasswordPage.changePassword("Password1", "New-password1", "New-password1");
- Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
changePasswordPage.logout();
@@ -224,7 +224,7 @@ public class FederationProvidersIntegrationTest {
// Change password back to previous value
changePasswordPage.open();
changePasswordPage.changePassword("New-password1", "Password1", "Password1");
- Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
+ Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
}
@Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
index 07ca87c..1d94c68 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
@@ -109,7 +109,7 @@ public class KerberosLdapTest extends AbstractKerberosTest {
// Successfully change password now
changePasswordPage.changePassword("theduke", "newPass", "newPass");
- Assert.assertTrue(driver.getPageSource().contains("Your password has been updated"));
+ Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
// Login with old password doesn't work, but with new password works
@@ -136,7 +136,7 @@ public class KerberosLdapTest extends AbstractKerberosTest {
loginPage.login("jduke", "newPass");
changePasswordPage.assertCurrent();
changePasswordPage.changePassword("newPass", "theduke", "theduke");
- Assert.assertTrue(driver.getPageSource().contains("Your password has been updated"));
+ Assert.assertTrue(driver.getPageSource().contains("Your password has been updated."));
changePasswordPage.logout();
spnegoResponse.close();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
index b2ac90e..13c628a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
@@ -128,7 +128,7 @@ public class RegisterTest {
registerPage.register("firstName", "lastName", "registerPasswordPolicy@email", "registerPasswordPolicy", "pass", "pass");
registerPage.assertCurrent();
- Assert.assertEquals("Invalid password: minimum length 8", registerPage.getError());
+ Assert.assertEquals("Invalid password: minimum length 8.", registerPage.getError());
events.expectRegister("registerPasswordPolicy", "registerPasswordPolicy@email").user((String) null).error("invalid_registration").assertEvent();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index a96b660..1b53664 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -394,7 +394,7 @@ public class ResetPasswordTest {
errorPage.assertCurrent();
- assertEquals("Failed to send email, please try again later", errorPage.getError());
+ assertEquals("Failed to send email, please try again later.", errorPage.getError());
Thread.sleep(1000);
@@ -446,7 +446,7 @@ public class ResetPasswordTest {
updatePasswordPage.changePassword("invalid", "invalid");
- assertEquals("Invalid password: minimum length 8", resetPasswordPage.getErrorMessage());
+ assertEquals("Invalid password: minimum length 8.", resetPasswordPage.getErrorMessage());
updatePasswordPage.changePassword("resetPasswordWithPasswordPolicy", "resetPasswordWithPasswordPolicy");
@@ -504,7 +504,7 @@ public class ResetPasswordTest {
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).session(sessionId).detail(Details.USERNAME, username).assertEvent();
assertTrue(infoPage.isCurrent());
- assertEquals("Your password has been updated", infoPage.getInfo());
+ assertEquals("Your password has been updated.", infoPage.getInfo());
loginPage.open();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 228416d..2d173ae 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -302,13 +302,18 @@ public class UserSessionProviderTest {
// Login lifespan is largest
String clientSessionId = session.sessions().createClientSession(realm, realm.findClient("test-app")).getId();
+ resetSession();
Time.setOffset(25);
session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
assertNotNull(session.sessions().getClientSession(clientSessionId));
Time.setOffset(35);
session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
assertNull(session.sessions().getClientSession(clientSessionId));
// User action is largest
@@ -316,13 +321,18 @@ public class UserSessionProviderTest {
Time.setOffset(0);
clientSessionId = session.sessions().createClientSession(realm, realm.findClient("test-app")).getId();
+ resetSession();
Time.setOffset(35);
session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
assertNotNull(session.sessions().getClientSession(clientSessionId));
Time.setOffset(45);
session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
assertNull(session.sessions().getClientSession(clientSessionId));
// Access code is largest
@@ -330,13 +340,18 @@ public class UserSessionProviderTest {
Time.setOffset(0);
clientSessionId = session.sessions().createClientSession(realm, realm.findClient("test-app")).getId();
+ resetSession();
Time.setOffset(45);
session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
assertNotNull(session.sessions().getClientSession(clientSessionId));
Time.setOffset(55);
session.sessions().removeExpiredUserSessions(realm);
+ resetSession();
+
assertNull(session.sessions().getClientSession(clientSessionId));
} finally {
Time.setOffset(0);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
index 0c56d7a..90a0e29 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
@@ -28,10 +28,8 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
-import org.keycloak.events.EventType;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
-import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
@@ -106,7 +104,7 @@ public class AuthorizationCodeTest {
oauth.doLogin("test-user@localhost", "password");
String title = driver.getTitle();
- Assert.assertTrue(title.startsWith("Success code="));
+ Assert.assertEquals("Success code", title);
String code = driver.findElement(By.id(OAuth2Constants.CODE)).getAttribute("value");
keycloakRule.verifyCode(code);
@@ -136,7 +134,7 @@ public class AuthorizationCodeTest {
driver.findElement(By.name("cancel")).click();
String title = driver.getTitle();
- Assert.assertTrue(title.equals("Error error=access_denied"));
+ Assert.assertEquals("Error code: access_denied",title);
String error = driver.findElement(By.id(OAuth2Constants.ERROR)).getText();
assertEquals("access_denied", error);
diff --git a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
index d08df51..96cdc96 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
@@ -62,6 +62,20 @@
}
},
{
+ "alias" : "model-linkedin",
+ "providerId" : "linkedin",
+ "enabled": true,
+ "updateProfileFirstLogin" : "true",
+ "storeToken": false,
+ "config": {
+ "authorizationUrl": "authorizationUrl",
+ "tokenUrl": "tokenUrl",
+ "userInfoUrl": "userInfoUrl",
+ "clientId": "clientId",
+ "clientSecret": "clientSecret"
+ }
+ },
+ {
"alias" : "model-saml-signed-idp",
"providerId" : "saml",
"enabled": true,
@@ -213,4 +227,4 @@
}
]
}
-}
\ No newline at end of file
+}