keycloak-aplcache
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java 19(+14 -5)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java 6(+6 -0)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java 8(+6 -2)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java 74(+47 -27)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java 9(+8 -1)
adapters/oidc/spring-boot/pom.xml 2(+1 -1)
adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java 93(+60 -33)
adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java 29(+17 -12)
adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java 8(+7 -1)
adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties 3(+2 -1)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java 7(+7 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties 3(+2 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java 69(+0 -69)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java 42(+39 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java 5(+1 -4)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java 2(+1 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java 51(+46 -5)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java 117(+108 -9)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java 17(+11 -6)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java 34(+0 -34)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java 40(+38 -2)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java 104(+88 -16)
authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java 21(+1 -20)
authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java 26(+24 -2)
core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java 132(+132 -0)
core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java 70(+1 -69)
core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java 38(+38 -0)
core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java 86(+86 -0)
core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java 28(+28 -0)
core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java 43(+43 -0)
distribution/demo-dist/pom.xml 3(+0 -3)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml 2(+1 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml 2(+1 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml 2(+1 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml 2(+1 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml 2(+1 -1)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml 85(+85 -0)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml 146(+146 -0)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml 127(+127 -0)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml 127(+127 -0)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml 117(+117 -0)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml 24(+24 -0)
distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/standalone/template.xml 2(+1 -1)
distribution/server-dist/pom.xml 11(+10 -1)
distribution/server-overlay/pom.xml 1(+0 -1)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java 4(+4 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java 31(+31 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java 12(+12 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java 69(+69 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java 50(+50 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java 7(+7 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java 5(+5 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java 50(+50 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePolicyResource.java 69(+69 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionResource.java 75(+75 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java 50(+50 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java 50(+50 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPolicyResource.java 69(+69 -0)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java 28(+16 -12)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java 6(+2 -4)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java 22(+11 -11)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java 10(+5 -5)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java 2(+1 -1)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java 23(+10 -13)
model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java 2(+2 -0)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 5(+5 -0)
model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java 5(+3 -2)
pom.xml 16(+10 -6)
server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java 122(+110 -12)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/DefaultPermissionEvaluator.java 8(+2 -6)
server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java 7(+2 -5)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java 5(+4 -1)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java 9(+2 -7)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java 32(+30 -2)
server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java 6(+3 -3)
server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java 355(+123 -232)
services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java 13(+0 -13)
services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java 70(+4 -66)
testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java 60(+32 -28)
testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java 12(+6 -6)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java 41(+39 -2)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java 16(+15 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java 39(+39 -0)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java 2(+1 -1)
testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java 1(+1 -0)
testsuite/integration-arquillian/test-apps/servlet-authz/keycloak.-permissive-authz-service.json 15(+15 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json 265(+265 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp 2(+2 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp 2(+2 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp 11(+11 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json 62(+62 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml 45(+45 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleTestApp.java 56(+51 -5)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java 2(+2 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java 13(+13 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java 12(+12 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java 7(+6 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java 31(+31 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java 43(+43 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java 56(+56 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java 21(+19 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java 75(+73 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java 52(+52 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java 331(+87 -244)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java 208(+208 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java 447(+447 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java 6(+4 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AuthzCleanupTest.java 77(+47 -30)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractPermissionManagementTest.java 169(+169 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java 165(+165 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java 211(+211 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java 159(+159 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java 172(+172 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java 40(+20 -20)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java 229(+229 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java 43(+21 -22)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java 303(+161 -142)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java 82(+70 -12)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java 68(+68 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java 5(+5 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json 195(+195 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak.json 8(+8 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/scripts/client-session-test.js 21(+21 -0)
testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/cluster/EAPSAMLAdapterClusterTest.java 96(+96 -0)
testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/resources/adapter-test/keycloak-saml/employee-distributable/WEB-INF/web.xml 62(+62 -0)
testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6PermissiveModeAdapterTest.java 30(+30 -0)
testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6ServletAuthzAdapterTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPermissiveModeAdapterTest.java 31(+31 -0)
testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java 3(+1 -2)
testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java 30(+30 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/Authorization.java 135(+135 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/AuthorizationSettingsForm.java 51(+51 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java 33(+33 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/PermissionsTable.java 77(+77 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java 34(+34 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PoliciesTable.java 77(+77 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resource.java 41(+41 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java 163(+163 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resources.java 86(+86 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourcesTable.java 80(+80 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java 32(+32 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java 51(+51 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java 69(+69 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopesTable.java 74(+74 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java 6(+6 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java 18(+18 -0)
testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java 80(+80 -0)
testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DefaultAuthorizationSettingsTest.java 69(+69 -0)
testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java 40(+40 -0)
testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java 120(+120 -0)
testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java 70(+70 -0)
themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html 16(+8 -8)
themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html 17(+9 -8)
themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html 4(+2 -2)
themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html 2(+1 -1)
themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html 2(+1 -1)
themes/src/main/resources-community/theme/base/account/messages/messages_ru.properties 304(+154 -150)
themes/src/main/resources-community/theme/base/admin/messages/admin-messages_fr.properties 58(+29 -29)
Details
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index 92b6c88..1ba9cc5 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -28,7 +28,6 @@ import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade.Request;
-import org.keycloak.adapters.spi.HttpFacade.Response;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
@@ -71,7 +70,6 @@ public abstract class AbstractPolicyEnforcer {
if (accessToken != null) {
Request request = httpFacade.getRequest();
- Response response = httpFacade.getResponse();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
@@ -83,7 +81,12 @@ public abstract class AbstractPolicyEnforcer {
}
LOGGER.debugf("Could not find a configuration for path [%s]", path);
- response.sendError(403, "Could not find a configuration for path [" + path + "].");
+
+ if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
+ return createAuthorizationContext(accessToken);
+ }
+
+ handleAccessDenied(httpFacade);
return createEmptyAuthorizationContext(false);
}
@@ -102,9 +105,11 @@ public abstract class AbstractPolicyEnforcer {
}
}
+ LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+
if (!challenge(pathConfig, requiredScopes, httpFacade)) {
- LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
- response.sendError(403, "Authorization failed.");
+ LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
+ handleAccessDenied(httpFacade);
}
}
}
@@ -165,6 +170,10 @@ public abstract class AbstractPolicyEnforcer {
return false;
}
+ protected void handleAccessDenied(OIDCHttpFacade httpFacade) {
+ httpFacade.getResponse().sendError(403);
+ }
+
private boolean isDefaultAccessDeniedUri(Request request, PolicyEnforcerConfig enforcerConfig) {
String accessDeniedPath = enforcerConfig.getOnDenyRedirectTo();
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
index f8f88d4..0cdfab9 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
@@ -56,6 +56,9 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Sending Entitlement challenge");
+ }
}
private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
@@ -66,6 +69,9 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Sending UMA challenge");
+ }
}
private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
index b6df2ea..316a39d 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -83,6 +83,12 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
@Override
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
+ handleAccessDenied(facade);
+ return true;
+ }
+
+ @Override
+ protected void handleAccessDenied(OIDCHttpFacade facade) {
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
HttpFacade.Response response = facade.getResponse();
@@ -92,8 +98,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
} else {
response.sendError(403);
}
-
- return true;
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
index d90a4fd..9efa614 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
@@ -49,7 +49,6 @@ class PathMatcher {
PathConfig matchingAnyPath = null;
PathConfig matchingAnySuffixPath = null;
- PathConfig matchingPath = null;
for (PathConfig entry : paths.values()) {
String expectedUri = entry.getPath();
@@ -132,56 +131,77 @@ class PathMatcher {
return targetUri.startsWith(expectedUri.substring(0, expectedUri.length() - 2));
}
+ String suffix = "/*.";
+ int suffixIndex = expectedUri.indexOf(suffix);
+
+ if (suffixIndex != -1) {
+ return targetUri.endsWith(expectedUri.substring(suffixIndex + suffix.length() - 1));
+ }
+
return false;
}
public String buildUriFromTemplate(String expectedUri, String targetUri) {
int patternStartIndex = expectedUri.indexOf("{");
- if (patternStartIndex >= targetUri.length()) {
+ if (patternStartIndex == -1 || patternStartIndex >= targetUri.length()) {
+ return null;
+ }
+
+ if (expectedUri.split("/").length > targetUri.split("/").length) {
return null;
}
char[] expectedUriChars = expectedUri.toCharArray();
char[] matchingUri = Arrays.copyOfRange(expectedUriChars, 0, patternStartIndex);
+ int matchingUriLastIndex = matchingUri.length;
+ String targetUriParams = targetUri.substring(patternStartIndex);
if (Arrays.equals(matchingUri, Arrays.copyOf(targetUri.toCharArray(), matchingUri.length))) {
- int matchingLastIndex = matchingUri.length;
- matchingUri = Arrays.copyOf(matchingUri, targetUri.length()); // +1 so we can add a slash at the end
- int targetPatternStartIndex = patternStartIndex;
+ matchingUri = Arrays.copyOf(matchingUri, targetUri.length());
+ int paramIndex = 0;
- while (patternStartIndex != -1) {
- int parameterStartIndex = -1;
+ for (int i = patternStartIndex; i < expectedUriChars.length; i++) {
+ if (matchingUriLastIndex >= matchingUri.length) {
+ break;
+ }
- for (int i = targetPatternStartIndex; i < targetUri.length(); i++) {
- char c = targetUri.charAt(i);
+ char c = expectedUriChars[i];
- if (c != '/') {
- if (parameterStartIndex == -1) {
- parameterStartIndex = matchingLastIndex;
+ if (c == '{' || c == '*') {
+ String[] params = targetUriParams.split("/");
+
+ for (int k = paramIndex; k <= (c == '*' ? params.length : paramIndex); k++) {
+ if (k == params.length) {
+ break;
}
- matchingUri[matchingLastIndex] = c;
- matchingLastIndex++;
- }
- if (c == '/' || ((i + 1 == targetUri.length()))) {
- if (matchingUri[matchingLastIndex - 1] != '/' && matchingLastIndex < matchingUri.length) {
- matchingUri[matchingLastIndex] = '/';
- matchingLastIndex++;
+ int paramLength = params[k].length();
+
+ if (matchingUriLastIndex + paramLength > matchingUri.length) {
+ return null;
+ }
+
+ for (int j = 0; j < paramLength; j++) {
+ matchingUri[matchingUriLastIndex++] = params[k].charAt(j);
}
- targetPatternStartIndex = targetUri.indexOf('/', i) + 1;
- break;
+ if (c == '*' && matchingUriLastIndex < matchingUri.length) {
+ matchingUri[matchingUriLastIndex++] = '/';
+ }
}
- }
- if ((patternStartIndex = expectedUri.indexOf('{', patternStartIndex + 1)) == -1) {
- break;
+ i = expectedUri.indexOf('}', i);
+ } else {
+ if (c == '/') {
+ paramIndex++;
+ }
+ matchingUri[matchingUriLastIndex++] = c;
}
+ }
- if ((targetPatternStartIndex == 0 || targetPatternStartIndex == targetUri.length()) && parameterStartIndex != -1) {
- return null;
- }
+ if (matchingUri[matchingUri.length - 1] == '\u0000') {
+ return null;
}
return String.valueOf(matchingUri);
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index 8a6a0a5..679a33c 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -220,7 +220,14 @@ public class PolicyEnforcer {
pathConfig.setId(resourceDescription.getId());
pathConfig.setName(resourceDescription.getName());
- pathConfig.setPath(resourceDescription.getUri());
+
+ String uri = resourceDescription.getUri();
+
+ if (uri == null || "".equals(uri.trim())) {
+ throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
+ }
+
+ pathConfig.setPath(uri);
List<String> scopeNames = new ArrayList<>();
diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index ff7fb6f..3f09229 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -157,7 +157,7 @@
processCallback(callback, initPromise);
return;
} else if (initOptions) {
- if (initOptions.refreshToken) {
+ if (initOptions.token && initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
if (loginIframe.enable) {
@@ -832,7 +832,7 @@
document.body.appendChild(iframe);
var messageCallback = function(event) {
- if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
+ if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
return;
}
@@ -1228,7 +1228,7 @@
break;
default:
if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
- oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + queryParams[param];
+ oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
}
break;
}
adapters/oidc/spring-boot/pom.xml 2(+1 -1)
diff --git a/adapters/oidc/spring-boot/pom.xml b/adapters/oidc/spring-boot/pom.xml
index 0abc3d8..48a2809 100755
--- a/adapters/oidc/spring-boot/pom.xml
+++ b/adapters/oidc/spring-boot/pom.xml
@@ -56,7 +56,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-jetty92-adapter</artifactId>
+ <artifactId>keycloak-jetty93-adapter</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java
index dc2573d..657f8e3 100755
--- a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java
+++ b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java
@@ -141,10 +141,10 @@ public class KeycloakSpringBootConfiguration {
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
- for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
+ io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
+ undertowSecurityConstraint.addRolesAllowed(constraintDefinition.getAuthRoles());
- io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
- undertowSecurityConstraint.addRolesAllowed(collectionDefinition.getAuthRoles());
+ for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
WebResourceCollection webResourceCollection = new WebResourceCollection();
webResourceCollection.addHttpMethods(collectionDefinition.getMethods());
@@ -153,8 +153,9 @@ public class KeycloakSpringBootConfiguration {
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
- undertowSecurityConstraints.add(undertowSecurityConstraint);
}
+
+ undertowSecurityConstraints.add(undertowSecurityConstraint);
}
return undertowSecurityConstraints;
}
@@ -174,42 +175,70 @@ public class KeycloakSpringBootConfiguration {
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
+ /* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
+ on how to map servlet spec to Constraints */
+
List<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition
.getSecurityCollections()) {
-
+ // securityCollection matches servlet spec's web-resource-collection
Constraint jettyConstraint = new Constraint();
- jettyConstraint.setName(securityCollectionDefinition.getName());
- jettyConstraint.setAuthenticate(true);
- if (securityCollectionDefinition.getName() != null) {
- jettyConstraint.setName(securityCollectionDefinition.getName());
+ if (constraintDefinition.getAuthRoles().size() > 0) {
+ jettyConstraint.setAuthenticate(true);
+ jettyConstraint.setRoles(constraintDefinition.getAuthRoles().toArray(new String[0]));
}
- jettyConstraint.setRoles(securityCollectionDefinition.getAuthRoles().toArray(new String[0]));
+ jettyConstraint.setName(securityCollectionDefinition.getName());
- ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
- if (securityCollectionDefinition.getPatterns().size() > 0) {
- //First pattern wins
- jettyConstraintMapping.setPathSpec(securityCollectionDefinition.getPatterns().get(0));
- jettyConstraintMapping.setConstraint(jettyConstraint);
- }
+ // according to the servlet spec each security-constraint has at least one URL pattern
+ for(String pattern : securityCollectionDefinition.getPatterns()) {
+
+ /* the following code is asymmetric as Jetty's ConstraintMapping accepts only one allowed HTTP method,
+ but multiple omitted methods. Therefore we add one ConstraintMapping for each allowed
+ mapping but only one mapping in the cases of omitted methods or no methods.
+ */
+
+ if (securityCollectionDefinition.getMethods().size() > 0) {
+ // according to the servlet spec we have either methods ...
+ for(String method : securityCollectionDefinition.getMethods()) {
+ ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
+ jettyConstraintMappings.add(jettyConstraintMapping);
+
+ jettyConstraintMapping.setConstraint(jettyConstraint);
+ jettyConstraintMapping.setPathSpec(pattern);
+ jettyConstraintMapping.setMethod(method);
+ }
+ } else if (securityCollectionDefinition.getOmittedMethods().size() > 0){
+ // ... omitted methods ...
+ ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
+ jettyConstraintMappings.add(jettyConstraintMapping);
+
+ jettyConstraintMapping.setConstraint(jettyConstraint);
+ jettyConstraintMapping.setPathSpec(pattern);
+ jettyConstraintMapping.setMethodOmissions(
+ securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
+ } else {
+ // ... or no methods at all
+ ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
+ jettyConstraintMappings.add(jettyConstraintMapping);
+
+ jettyConstraintMapping.setConstraint(jettyConstraint);
+ jettyConstraintMapping.setPathSpec(pattern);
+ }
- if (securityCollectionDefinition.getMethods().size() > 0) {
- //First method wins
- jettyConstraintMapping.setMethod(securityCollectionDefinition.getMethods().get(0));
}
- jettyConstraintMapping.setMethodOmissions(
- securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
-
- jettyConstraintMappings.add(jettyConstraintMapping);
}
}
WebAppContext webAppContext = server.getBean(WebAppContext.class);
+ //if not found as registered bean let's try the handler
+ if(webAppContext==null){
+ webAppContext = (WebAppContext) server.getHandler();
+ }
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setConstraintMappings(jettyConstraintMappings);
@@ -235,12 +264,10 @@ public class KeycloakSpringBootConfiguration {
Set<String> authRoles = new HashSet<String>();
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
- for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
- for (String authRole : collection.getAuthRoles()) {
- if (!authRoles.contains(authRole)) {
- context.addSecurityRole(authRole);
- authRoles.add(authRole);
- }
+ for (String authRole : constraint.getAuthRoles()) {
+ if (!authRoles.contains(authRole)) {
+ context.addSecurityRole(authRole);
+ authRoles.add(authRole);
}
}
}
@@ -248,6 +275,10 @@ public class KeycloakSpringBootConfiguration {
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
SecurityConstraint tomcatConstraint = new SecurityConstraint();
+ for (String authRole : constraint.getAuthRoles()) {
+ tomcatConstraint.addAuthRole(authRole);
+ }
+
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
SecurityCollection tomcatSecCollection = new SecurityCollection();
@@ -258,10 +289,6 @@ public class KeycloakSpringBootConfiguration {
tomcatSecCollection.setDescription(collection.getDescription());
}
- for (String authRole : collection.getAuthRoles()) {
- tomcatConstraint.addAuthRole(authRole);
- }
-
for (String pattern : collection.getPatterns()) {
tomcatSecCollection.addPattern(pattern);
}
diff --git a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java
index 2c99eba..788412f 100644
--- a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java
+++ b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java
@@ -43,12 +43,20 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
*/
private List<SecurityConstraint> securityConstraints = new ArrayList<SecurityConstraint>();
+ /**
+ * This matches security-constraint of the servlet spec
+ */
@ConfigurationProperties()
public static class SecurityConstraint {
/**
* A list of security collections
*/
private List<SecurityCollection> securityCollections = new ArrayList<SecurityCollection>();
+ private List<String> authRoles = new ArrayList<String>();
+
+ public List<String> getAuthRoles() {
+ return authRoles;
+ }
public List<SecurityCollection> getSecurityCollections() {
return securityCollections;
@@ -57,7 +65,16 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
public void setSecurityCollections(List<SecurityCollection> securityCollections) {
this.securityCollections = securityCollections;
}
+
+ public void setAuthRoles(List<String> authRoles) {
+ this.authRoles = authRoles;
+ }
+
}
+
+ /**
+ * This matches web-resource-collection of the servlet spec
+ */
@ConfigurationProperties()
public static class SecurityCollection {
/**
@@ -69,10 +86,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
*/
private String description;
/**
- * A list of roles that applies for this security collection
- */
- private List<String> authRoles = new ArrayList<String>();
- /**
* A list of URL patterns that should match to apply the security collection
*/
private List<String> patterns = new ArrayList<String>();
@@ -85,10 +98,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
*/
private List<String> omittedMethods = new ArrayList<String>();
- public List<String> getAuthRoles() {
- return authRoles;
- }
-
public List<String> getPatterns() {
return patterns;
}
@@ -117,10 +126,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
this.description = description;
}
- public void setAuthRoles(List<String> authRoles) {
- this.authRoles = authRoles;
- }
-
public void setPatterns(List<String> patterns) {
this.patterns = patterns;
}
diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java b/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
index 2221bfd..e4ff988 100755
--- a/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
+++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
@@ -167,7 +167,12 @@ public class SharedAttributeDefinitons {
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
-
+ protected static final SimpleAttributeDefinition AUTODETECT_BEARER_ONLY =
+ new SimpleAttributeDefinitionBuilder("autodetect-bearer-only", ModelType.BOOLEAN, true)
+ .setXmlName("autodetect-bearer-only")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
@@ -193,6 +198,7 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
ATTRIBUTES.add(TOKEN_STORE);
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
+ ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
}
/**
diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
index e6a581a..6244b0c 100755
--- a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
@@ -46,7 +46,7 @@ keycloak.realm.register-node-at-startup=Cluster setting
keycloak.realm.register-node-period=how often to re-register node
keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
-
+keycloak.realm.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@@ -83,6 +83,7 @@ keycloak.secure-deployment.principal-attribute=token attribute to use to set Pri
keycloak.secure-deployment.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off
keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
+keycloak.secure-deployment.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment.credential=Credential value
diff --git a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index 53549fa..e9839bc 100755
--- a/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -65,6 +65,7 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@@ -107,6 +108,7 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java
index f9f8a6a..02d14e5 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SharedAttributeDefinitons.java
@@ -166,6 +166,12 @@ public class SharedAttributeDefinitons {
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
+ protected static final SimpleAttributeDefinition AUTODETECT_BEARER_ONLY =
+ new SimpleAttributeDefinitionBuilder("autodetect-bearer-only", ModelType.BOOLEAN, true)
+ .setXmlName("autodetect-bearer-only")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
@@ -193,6 +199,7 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
ATTRIBUTES.add(TOKEN_STORE);
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
+ ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
}
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
index e6a581a..c0ca52e 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
@@ -46,7 +46,7 @@ keycloak.realm.register-node-at-startup=Cluster setting
keycloak.realm.register-node-period=how often to re-register node
keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
-
+keycloak.realm.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@@ -67,6 +67,7 @@ keycloak.secure-deployment.bearer-only=Bearer Token Auth only
keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
keycloak.secure-deployment.public-client=Public client
keycloak.secure-deployment.enable-cors=Enable Keycloak CORS support
+keycloak.secure-deployment.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment.client-keystore=n/a
keycloak.secure-deployment.client-keystore-password=n/a
keycloak.secure-deployment.client-key-password=n/a
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index 7372e82..84399a3 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -65,6 +65,7 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@@ -107,6 +108,7 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
index 3e86973..73443d1 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
@@ -17,19 +17,24 @@
*/
package org.keycloak.authorization.policy.provider.aggregated;
+import java.util.ArrayList;
+import java.util.List;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
+public class AggregatePolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
private AggregatePolicyProvider provider = new AggregatePolicyProvider();
@@ -49,8 +54,8 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
- return new AggregatePolicyAdminResource(resourceServer);
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
+ return null;
}
@Override
@@ -59,6 +64,37 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
+ public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ verifyCircularReference(policy, new ArrayList<>());
+ }
+
+ @Override
+ public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ verifyCircularReference(policy, new ArrayList<>());
+ }
+
+ private void verifyCircularReference(Policy policy, List<String> ids) {
+ if (!policy.getType().equals("aggregate")) {
+ return;
+ }
+
+ if (ids.contains(policy.getId())) {
+ throw new RuntimeException("Circular reference found [" + policy.getName() + "].");
+ }
+
+ ids.add(policy.getId());
+
+ for (Policy associated : policy.getAssociatedPolicies()) {
+ verifyCircularReference(associated, ids);
+ }
+ }
+
+ @Override
+ public void onRemove(Policy policy, AuthorizationProvider authorization) {
+
+ }
+
+ @Override
public void init(Config.Scope config) {
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
index 8cb0029..a0e7874 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
@@ -40,7 +40,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
@@ -78,9 +78,6 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
try {
if (clients.isEmpty()) {
- policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
- dependentPolicy.removeAssociatedPolicy(policy);
- });
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
index b4a5099..617727f 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
@@ -42,7 +42,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
index d7a6b2b..6ea7230 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java
@@ -1,18 +1,20 @@
package org.keycloak.authorization.policy.provider.resource;
+import java.util.Map;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.provider.PolicyProvider;
-import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
+public class ResourcePolicyProviderFactory implements PolicyProviderFactory<ResourcePermissionRepresentation> {
private ResourcePolicyProvider provider = new ResourcePolicyProvider();
@@ -32,8 +34,14 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
- return null;
+ public Class<ResourcePermissionRepresentation> getRepresentationType() {
+ return ResourcePermissionRepresentation.class;
+ }
+
+ @Override
+ public ResourcePermissionRepresentation toRepresentation(Policy policy, ResourcePermissionRepresentation representation) {
+ representation.setResourceType(policy.getConfig().get("defaultResourceType"));
+ return representation;
}
@Override
@@ -42,6 +50,39 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
+ public void onCreate(Policy policy, ResourcePermissionRepresentation representation, AuthorizationProvider authorization) {
+ updateResourceType(policy, representation);
+ }
+
+ @Override
+ public void onUpdate(Policy policy, ResourcePermissionRepresentation representation, AuthorizationProvider authorization) {
+ updateResourceType(policy, representation);
+ }
+
+ private void updateResourceType(Policy policy, ResourcePermissionRepresentation representation) {
+ if (representation != null) {
+ //TODO: remove this check once we migrate to new API
+ if (ResourcePermissionRepresentation.class.equals(representation.getClass())) {
+ ResourcePermissionRepresentation resourcePermission = ResourcePermissionRepresentation.class.cast(representation);
+ Map<String, String> config = policy.getConfig();
+
+ config.compute("defaultResourceType", (key, value) -> {
+ String resourceType = resourcePermission.getResourceType();
+ return resourceType != null ? resourcePermission.getResourceType() : null;
+ });
+
+ policy.setConfig(config);
+
+ }
+ }
+ }
+
+ @Override
+ public void onRemove(Policy policy, AuthorizationProvider authorization) {
+
+ }
+
+ @Override
public void init(Config.Scope config) {
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
index 33db2d5..d5d3917 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
@@ -35,19 +35,23 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
import org.keycloak.models.RoleModel;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.function.Consumer;
+import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class RolePolicyProviderFactory implements PolicyProviderFactory {
+public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePolicyRepresentation> {
private RolePolicyProvider provider = new RolePolicyProvider();
@@ -67,7 +71,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
@@ -77,6 +81,107 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
+ public RolePolicyRepresentation toRepresentation(Policy policy, RolePolicyRepresentation representation) {
+ try {
+ representation.setRoles(JsonSerialization.readValue(policy.getConfig().get("roles"), Set.class));
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize roles", cause);
+ }
+ return representation;
+ }
+
+ @Override
+ public Class<RolePolicyRepresentation> getRepresentationType() {
+ return RolePolicyRepresentation.class;
+ }
+
+ @Override
+ public void onCreate(Policy policy, RolePolicyRepresentation representation, AuthorizationProvider authorization) {
+ updateRoles(policy, representation, authorization);
+ }
+
+ @Override
+ public void onUpdate(Policy policy, RolePolicyRepresentation representation, AuthorizationProvider authorization) {
+ updateRoles(policy, representation, authorization);
+ }
+
+ @Override
+ public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ try {
+ updateRoles(policy, authorization, new HashSet<>(Arrays.asList(JsonSerialization.readValue(representation.getConfig().get("roles"), RolePolicyRepresentation.RoleDefinition[].class))));
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize roles during import", cause);
+ }
+ }
+
+ private void updateRoles(Policy policy, RolePolicyRepresentation representation, AuthorizationProvider authorization) {
+ updateRoles(policy, authorization, representation.getRoles());
+ }
+
+ private void updateRoles(Policy policy, AuthorizationProvider authorization, Set<RolePolicyRepresentation.RoleDefinition> roles) {
+ try {
+ RealmModel realm = authorization.getRealm();
+ Set<RolePolicyRepresentation.RoleDefinition> updatedRoles = new HashSet<>();
+
+ if (roles != null) {
+ for (RolePolicyRepresentation.RoleDefinition definition : roles) {
+ String roleName = definition.getId();
+ String clientId = null;
+ int clientIdSeparator = roleName.indexOf("/");
+
+ if (clientIdSeparator != -1) {
+ clientId = roleName.substring(0, clientIdSeparator);
+ roleName = roleName.substring(clientIdSeparator + 1);
+ }
+
+ RoleModel role;
+
+ if (clientId == null) {
+ role = realm.getRole(roleName);
+
+ if (role == null) {
+ role = realm.getRoleById(roleName);
+ }
+ } else {
+ ClientModel client = realm.getClientByClientId(clientId);
+
+ if (client == null) {
+ throw new RuntimeException("Client with id [" + clientId + "] not found.");
+ }
+
+ role = client.getRole(roleName);
+ }
+
+ // fallback to find any client role with the given name
+ if (role == null) {
+ String finalRoleName = roleName;
+ role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
+ .findFirst().orElse(null);
+ }
+
+ if (role == null) {
+ throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
+ }
+
+ definition.setId(role.getId());
+
+ updatedRoles.add(definition);
+ }
+ try {
+ } catch (Exception e) {
+ throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
+ }
+ }
+
+ Map<String, String> config = policy.getConfig();
+ config.put("roles", JsonSerialization.writeValueAsString(updatedRoles));
+ policy.setConfig(config);
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize roles", cause);
+ }
+ }
+
+ @Override
public void init(Config.Scope config) {
}
@@ -125,12 +230,6 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
try {
if (roles.isEmpty()) {
- policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
- dependentPolicy.removeAssociatedPolicy(policy);
- if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
- policyStore.delete(dependentPolicy.getId());
- }
- });
policyStore.delete(policy.getId());
} else {
Map<String, String> config = policy.getConfig();
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
index 0678eb3..e677a9e 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java
@@ -2,17 +2,17 @@ package org.keycloak.authorization.policy.provider.scope;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.provider.PolicyProvider;
-import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class ScopePolicyProviderFactory implements PolicyProviderFactory {
+public class ScopePolicyProviderFactory implements PolicyProviderFactory<ScopePermissionRepresentation> {
private ScopePolicyProvider provider = new ScopePolicyProvider();
@@ -32,13 +32,18 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProvider create(KeycloakSession session) {
return null;
}
@Override
- public PolicyProvider create(KeycloakSession session) {
- return null;
+ public Class<ScopePermissionRepresentation> getRepresentationType() {
+ return ScopePermissionRepresentation.class;
+ }
+
+ @Override
+ public ScopePermissionRepresentation toRepresentation(Policy policy, ScopePermissionRepresentation representation) {
+ return representation;
}
@Override
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
index 96c1d24..7c2206a 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java
@@ -18,45 +18,11 @@
package org.keycloak.authorization.policy.provider.time;
-import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
-import java.text.SimpleDateFormat;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class TimePolicyAdminResource implements PolicyProviderAdminService {
- @Override
- public void onCreate(Policy policy) {
- validateConfig(policy);
- }
-
- private void validateConfig(Policy policy) {
- String nbf = policy.getConfig().get("nbf");
- String noa = policy.getConfig().get("noa");
-
- if (nbf != null && noa != null) {
- validateFormat(nbf);
- validateFormat(noa);
- }
- }
-
- @Override
- public void onUpdate(Policy policy) {
- validateConfig(policy);
- }
-
- @Override
- public void onRemove(Policy policy) {
- }
-
- private void validateFormat(String date) {
- try {
- new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(date));
- } catch (Exception e) {
- throw new RuntimeException("Could not parse a date using format [" + date + "]");
- }
- }
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
index 94c5aad..920cf45 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
@@ -1,18 +1,22 @@
package org.keycloak.authorization.policy.provider.time;
+import java.text.SimpleDateFormat;
+
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class TimePolicyProviderFactory implements PolicyProviderFactory {
+public class TimePolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
private TimePolicyProvider provider = new TimePolicyProvider();
@@ -32,7 +36,7 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return new TimePolicyAdminResource();
}
@@ -42,6 +46,38 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
+ public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ validateConfig(policy);
+ }
+
+ private void validateConfig(Policy policy) {
+ String nbf = policy.getConfig().get("nbf");
+ String noa = policy.getConfig().get("noa");
+
+ if (nbf != null && noa != null) {
+ validateFormat(nbf);
+ validateFormat(noa);
+ }
+ }
+
+ @Override
+ public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ validateConfig(policy);
+ }
+
+ @Override
+ public void onRemove(Policy policy, AuthorizationProvider authorization) {
+ }
+
+ private void validateFormat(String date) {
+ try {
+ new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(date));
+ } catch (Exception e) {
+ throw new RuntimeException("Could not parse a date using format [" + date + "]");
+ }
+ }
+
+ @Override
public void init(Config.Scope config) {
}
diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
index 09345ec..0fd54df 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
@@ -20,14 +20,16 @@ package org.keycloak.authorization.policy.provider.user;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
-import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
@@ -37,12 +39,15 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.UserRemovedEvent;
+import org.keycloak.models.UserProvider;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class UserPolicyProviderFactory implements PolicyProviderFactory {
+public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPolicyRepresentation> {
private UserPolicyProvider provider = new UserPolicyProvider();
@@ -62,13 +67,86 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProvider create(KeycloakSession session) {
return null;
}
@Override
- public PolicyProvider create(KeycloakSession session) {
- return null;
+ public UserPolicyRepresentation toRepresentation(Policy policy, UserPolicyRepresentation representation) {
+ try {
+ representation.setUsers(JsonSerialization.readValue(policy.getConfig().get("users"), Set.class));
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize roles", cause);
+ }
+ return representation;
+ }
+
+ @Override
+ public Class<UserPolicyRepresentation> getRepresentationType() {
+ return UserPolicyRepresentation.class;
+ }
+
+ @Override
+ public void onCreate(Policy policy, UserPolicyRepresentation representation, AuthorizationProvider authorization) {
+ updateUsers(policy, representation, authorization);
+ }
+
+ @Override
+ public void onUpdate(Policy policy, UserPolicyRepresentation representation, AuthorizationProvider authorization) {
+ updateUsers(policy, representation, authorization);
+ }
+
+ @Override
+ public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ try {
+ updateUsers(policy, authorization, JsonSerialization.readValue(representation.getConfig().get("users"), Set.class));
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize users during import", cause);
+ }
+ }
+
+ private void updateUsers(Policy policy, UserPolicyRepresentation representation, AuthorizationProvider authorization) {
+ updateUsers(policy, authorization, representation.getUsers());
+ }
+
+ private void updateUsers(Policy policy, AuthorizationProvider authorization, Set<String> users) {
+ try {
+ KeycloakSession session = authorization.getKeycloakSession();
+ RealmModel realm = authorization.getRealm();
+ UserProvider userProvider = session.users();
+ Set<String> updatedUsers = new HashSet<>();
+
+ if (users != null) {
+ try {
+ for (String userId : users) {
+ UserModel user = null;
+
+ try {
+ user = userProvider.getUserByUsername(userId, realm);
+ } catch (Exception ignore) {
+ }
+
+ if (user == null) {
+ user = userProvider.getUserById(userId, realm);
+ }
+
+ if (user == null) {
+ throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found.");
+ }
+
+ updatedUsers.add(user.getId());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
+ }
+ }
+
+ Map<String, String> config = policy.getConfig();
+ config.put("users", JsonSerialization.writeValueAsString(updatedUsers));
+ policy.setConfig(config);
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize roles", cause);
+ }
}
@Override
@@ -102,12 +180,6 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
try {
if (users.isEmpty()) {
- policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
- dependentPolicy.removeAssociatedPolicy(policy);
- if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
- policyStore.delete(dependentPolicy.getId());
- }
- });
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
@@ -133,16 +205,16 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
}
static String[] getUsers(Policy policy) {
- String roles = policy.getConfig().get("users");
+ String users = policy.getConfig().get("users");
- if (roles != null) {
+ if (users != null) {
try {
- return JsonSerialization.readValue(roles.getBytes(), String[].class);
+ return JsonSerialization.readValue(users.getBytes(), String[].class);
} catch (IOException e) {
- throw new RuntimeException("Could not parse roles [" + roles + "] from policy config [" + policy.getName() + ".", e);
+ throw new RuntimeException("Could not parse users [" + users + "] from policy config [" + policy.getName() + ".", e);
}
}
- return new String[]{};
+ return new String[0];
}
}
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java
index c6e5701..a9bdbb0 100644
--- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java
@@ -16,8 +16,6 @@
*/
package org.keycloak.authorization.policy.provider.drools;
-import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.kie.api.runtime.KieContainer;
@@ -33,29 +31,12 @@ import javax.ws.rs.core.Response;
*/
public class DroolsPolicyAdminResource implements PolicyProviderAdminService {
- private final ResourceServer resourceServer;
private final DroolsPolicyProviderFactory factory;
- public DroolsPolicyAdminResource(ResourceServer resourceServer, DroolsPolicyProviderFactory factory) {
- this.resourceServer = resourceServer;
+ public DroolsPolicyAdminResource(DroolsPolicyProviderFactory factory) {
this.factory = factory;
}
- @Override
- public void onCreate(Policy policy) {
- this.factory.update(policy);
- }
-
- @Override
- public void onUpdate(Policy policy) {
- this.factory.update(policy);
- }
-
- @Override
- public void onRemove(Policy policy) {
- this.factory.remove(policy);
- }
-
@Path("/resolveModules")
@POST
@Consumes("application/json")
diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
index ce0f834..8bbed33 100644
--- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
@@ -13,6 +13,8 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.kie.api.KieServices;
import org.kie.api.KieServices.Factory;
import org.kie.api.runtime.KieContainer;
@@ -49,8 +51,8 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
- return new DroolsPolicyAdminResource(resourceServer, this);
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
+ return new DroolsPolicyAdminResource(this);
}
@Override
@@ -59,6 +61,26 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
+ public void onCreate(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) {
+ update(policy);
+ }
+
+ @Override
+ public void onUpdate(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) {
+ update(policy);
+ }
+
+ @Override
+ public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+ update(policy);
+ }
+
+ @Override
+ public void onRemove(Policy policy, AuthorizationProvider authorization) {
+ remove(policy);
+ }
+
+ @Override
public void init(Config.Scope config) {
this.ks = Factory.get();
}
diff --git a/common/src/main/java/org/keycloak/common/util/StreamUtil.java b/common/src/main/java/org/keycloak/common/util/StreamUtil.java
index 72ff6df..c14c1da 100755
--- a/common/src/main/java/org/keycloak/common/util/StreamUtil.java
+++ b/common/src/main/java/org/keycloak/common/util/StreamUtil.java
@@ -53,7 +53,6 @@ public final class StreamUtil {
* @param charset Charset used to decode the input stream
* @return String representation of the input stream contents decoded using given charset
* @throws IOException
- * @deprecated Use {@link #readString(java.io.InputStream, java.nio.charset.Charset)} variant.
*/
public static String readString(InputStream in, Charset charset) throws IOException
{
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
index 3cef2a0..dfe4ae6 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.HashMap;
import java.util.Map;
+import java.util.TreeMap;
/**
* Common Adapter configuration
@@ -58,7 +59,7 @@ public class BaseAdapterConfig extends BaseRealmConfig {
@JsonProperty("public-client")
protected boolean publicClient;
@JsonProperty("credentials")
- protected Map<String, Object> credentials = new HashMap<>();
+ protected Map<String, Object> credentials = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
public boolean isUseResourceRoleMappings() {
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
index a1bc179..dd94537 100644
--- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -196,10 +196,12 @@ public class PolicyEnforcerConfig {
'}';
}
+ @JsonIgnore
public boolean hasPattern() {
return getPath().indexOf("{") != -1;
}
+ @JsonIgnore
public boolean isInstance() {
return this.parentConfig != null;
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
new file mode 100644
index 0000000..e0be800
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm.authorization;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AbstractPolicyRepresentation {
+
+ private String id;
+ private String name;
+ private String description;
+ private String type;
+ private Set<String> policies;
+ private Set<String> resources;
+ private Set<String> scopes;
+ private Logic logic = Logic.POSITIVE;
+ private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public DecisionStrategy getDecisionStrategy() {
+ return this.decisionStrategy;
+ }
+
+ public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
+ this.decisionStrategy = decisionStrategy;
+ }
+
+ public Logic getLogic() {
+ return logic;
+ }
+
+ public void setLogic(Logic logic) {
+ this.logic = logic;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return this.description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Set<String> getPolicies() {
+ return policies;
+ }
+
+ public void addPolicy(String... id) {
+ if (this.policies == null) {
+ this.policies = new HashSet<>();
+ }
+ this.policies.addAll(Arrays.asList(id));
+ }
+
+ public Set<String> getResources() {
+ return resources;
+ }
+
+ public void addResource(String id) {
+ if (this.resources == null) {
+ this.resources = new HashSet<>();
+ }
+ this.resources.add(id);
+ }
+
+ public Set<String> getScopes() {
+ return scopes;
+ }
+
+ public void addScope(String... id) {
+ if (this.scopes == null) {
+ this.scopes = new HashSet<>();
+ }
+ this.scopes.addAll(Arrays.asList(id));
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final AbstractPolicyRepresentation policy = (AbstractPolicyRepresentation) o;
+ return Objects.equals(getId(), policy.getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getId());
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
index dc04991..e36d7af 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
@@ -18,53 +18,14 @@ package org.keycloak.representations.idm.authorization;
import java.util.HashMap;
import java.util.Map;
-import java.util.Objects;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class PolicyRepresentation {
+public class PolicyRepresentation extends AbstractPolicyRepresentation {
- private String id;
- private String name;
- private String description;
- private String type;
- private Logic logic = Logic.POSITIVE;
- private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
private Map<String, String> config = new HashMap();
- public String getId() {
- return this.id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getType() {
- return this.type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public DecisionStrategy getDecisionStrategy() {
- return this.decisionStrategy;
- }
-
- public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
- this.decisionStrategy = decisionStrategy;
- }
-
- public Logic getLogic() {
- return logic;
- }
-
- public void setLogic(Logic logic) {
- this.logic = logic;
- }
-
public Map<String, String> getConfig() {
return this.config;
}
@@ -72,33 +33,4 @@ public class PolicyRepresentation {
public void setConfig(Map<String, String> config) {
this.config = config;
}
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDescription() {
- return this.description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- final PolicyRepresentation policy = (PolicyRepresentation) o;
- return Objects.equals(getId(), policy.getId());
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getId());
- }
}
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java
new file mode 100644
index 0000000..80f7106
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermissionRepresentation extends AbstractPolicyRepresentation {
+
+ private String resourceType;
+
+ @Override
+ public String getType() {
+ return "resource";
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java
new file mode 100644
index 0000000..2a89710
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/RolePolicyRepresentation.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm.authorization;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicyRepresentation extends AbstractPolicyRepresentation {
+
+ private Set<RoleDefinition> roles;
+
+ public Set<RoleDefinition> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Set<RoleDefinition> roles) {
+ this.roles = roles;
+ }
+
+ public void addRole(String name, boolean required) {
+ if (roles == null) {
+ roles = new HashSet<>();
+ }
+ roles.add(new RoleDefinition(name, required));
+ }
+
+ public void addRole(String name) {
+ addRole(name, false);
+ }
+
+ public void addClientRole(String clientId, String name) {
+ addRole(clientId + "/" +name, false);
+ }
+
+ public void addClientRole(String clientId, String name, boolean required) {
+ addRole(clientId + "/" + name, required);
+ }
+
+ public static class RoleDefinition {
+
+ private String id;
+ private boolean required;
+
+ public RoleDefinition() {
+ this(null, false);
+ }
+
+ public RoleDefinition(String id, boolean required) {
+ this.id = id;
+ this.required = required;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java
new file mode 100644
index 0000000..b6a02b4
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePermissionRepresentation extends AbstractPolicyRepresentation {
+
+ @Override
+ public String getType() {
+ return "scope";
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java
new file mode 100644
index 0000000..dc38e65
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/UserPolicyRepresentation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm.authorization;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicyRepresentation extends AbstractPolicyRepresentation {
+
+ private Set<String> users;
+
+ public Set<String> getUsers() {
+ return users;
+ }
+
+ public void setUsers(Set<String> users) {
+ this.users= users;
+ }
+
+ public void addUser(String name) {
+ if (users == null) {
+ users = new HashSet<>();
+ }
+ users.add(name);
+ }
+}
distribution/demo-dist/pom.xml 3(+0 -3)
diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml
index dbe33cc..da01c09 100755
--- a/distribution/demo-dist/pom.xml
+++ b/distribution/demo-dist/pom.xml
@@ -206,7 +206,6 @@
</execution>
</executions>
</plugin>
-
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
@@ -223,12 +222,10 @@
<outputDirectory>target</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
<appendAssemblyId>false</appendAssemblyId>
- <tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
-
</plugins>
</build>
</profile>
diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml
index 27f4bc5..0a3bb68 100755
--- a/distribution/feature-packs/adapter-feature-pack/pom.xml
+++ b/distribution/feature-packs/adapter-feature-pack/pom.xml
@@ -79,6 +79,7 @@
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
+ <version>${wildfly.build-tools.version}</version>
<executions>
<execution>
<id>feature-pack-build</id>
@@ -111,7 +112,6 @@
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
- <tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
diff --git a/distribution/feature-packs/server-feature-pack/assembly.xml b/distribution/feature-packs/server-feature-pack/assembly.xml
index 1fdde3f..c118dec 100644
--- a/distribution/feature-packs/server-feature-pack/assembly.xml
+++ b/distribution/feature-packs/server-feature-pack/assembly.xml
@@ -28,6 +28,43 @@
<fileSet>
<directory>target/${project.build.finalName}</directory>
<outputDirectory/>
+ <excludes>
+ <exclude>configuration/**</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>target/unpacked-themes/theme</directory>
+ <outputDirectory>content/themes</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/identity/module</directory>
+ <includes>
+ <include>**/**</include>
+ </includes>
+ <outputDirectory>modules/system/layers/keycloak/org/jboss/as/product/${product.slot}</outputDirectory>
+ <filtered>true</filtered>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/identity</directory>
+ <includes>
+ <include>product.conf</include>
+ </includes>
+ <outputDirectory>content/bin</outputDirectory>
+ <filtered>true</filtered>
+ </fileSet>
+ <fileSet>
+ <directory>${configDir}</directory>
+ <includes>
+ <include>**/**</include>
+ </includes>
+ <outputDirectory>configuration</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>../../../</directory>
+ <includes>
+ <include>License.html</include>
+ </includes>
+ <outputDirectory>content</outputDirectory>
</fileSet>
</fileSets>
-</assembly>
+</assembly>
\ No newline at end of file
diff --git a/distribution/feature-packs/server-feature-pack/pom.xml b/distribution/feature-packs/server-feature-pack/pom.xml
index 46fee49..92253a0 100644
--- a/distribution/feature-packs/server-feature-pack/pom.xml
+++ b/distribution/feature-packs/server-feature-pack/pom.xml
@@ -75,126 +75,6 @@
</plugin>
<plugin>
- <artifactId>maven-resources-plugin</artifactId>
- <executions>
- <execution>
- <id>copy-configuration</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/configuration</outputDirectory>
- <resources>
- <resource>
- <directory>src/main/resources/configuration</directory>
- <filtering>true</filtering>
- </resource>
- </resources>
- </configuration>
- </execution>
- <execution>
- <id>copy-modules</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/modules</outputDirectory>
- <resources>
- <resource>
- <directory>src/main/resources/modules</directory>
- </resource>
- </resources>
- </configuration>
- </execution>
- <execution>
- <id>copy-content</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/content</outputDirectory>
- <resources>
- <resource>
- <directory>src/main/resources/content</directory>
- </resource>
- </resources>
- </configuration>
- </execution>
- <execution>
- <id>copy-identity</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/content/bin</outputDirectory>
- <resources>
- <resource>
- <directory>src/main/resources/identity</directory>
- <includes>
- <include>**/product.conf</include>
- </includes>
- <filtering>true</filtering>
- </resource>
- </resources>
- </configuration>
- </execution>
- <execution>
- <id>copy-identity-module</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/modules/system/layers/keycloak/org/jboss/as/product/${product.slot}</outputDirectory>
- <resources>
- <resource>
- <directory>src/main/resources/identity/module</directory>
- <filtering>true</filtering>
- </resource>
- </resources>
- </configuration>
- </execution>
- <execution>
- <id>copy-themes</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/content/themes</outputDirectory>
- <resources>
- <resource>
- <directory>target/unpacked-themes/theme</directory>
- </resource>
- </resources>
- </configuration>
- </execution>
- <execution>
- <id>copy-license</id>
- <phase>validate</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <outputDirectory>target/resources/content</outputDirectory>
- <resources>
- <resource>
- <directory>../../../</directory>
- <includes>
- <include>License.html</include>
- </includes>
- </resource>
- </resources>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
<executions>
@@ -206,11 +86,11 @@
<phase>compile</phase>
<configuration>
<config-file>feature-pack-build.xml</config-file>
- <resources-dir>target/resources</resources-dir>
</configuration>
</execution>
</executions>
</plugin>
+
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
@@ -226,11 +106,9 @@
<descriptor>assembly.xml</descriptor>
</descriptors>
<recompressZippedFiles>true</recompressZippedFiles>
- <finalName>${project.build.finalName}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
- <tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
@@ -248,8 +126,9 @@
</activation>
<properties>
+ <build-tools.version>${wildfly.build-tools.version}</build-tools.version>
<feature.parent>org.wildfly:wildfly-feature-pack</feature.parent>
- <xmlns.domain>urn:jboss:domain:4.0</xmlns.domain>
+ <configDir>src/main/resources/configuration</configDir>
</properties>
<dependencies>
@@ -266,7 +145,9 @@
<id>wf11</id>
<properties>
- <xmlns.domain>urn:jboss:domain:5.0</xmlns.domain>
+ <build-tools.version>${wildfly11.build-tools.version}</build-tools.version>
+ <feature.parent>org.wildfly:wildfly-feature-pack</feature.parent>
+ <configDir>src/main/resources-wf11/configuration</configDir>
</properties>
<dependencies>
@@ -304,6 +185,7 @@
</plugins>
</build>
</profile>
+
<profile>
<id>product</id>
<activation>
@@ -313,8 +195,9 @@
</activation>
<properties>
+ <build-tools.version>${eap.build-tools.version}</build-tools.version>
<feature.parent>org.jboss.eap:wildfly-feature-pack</feature.parent>
- <xmlns.domain>urn:jboss:domain:5.0</xmlns.domain>
+ <configDir>src/main/resources-wf11/configuration</configDir>
</properties>
<dependencies>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml
index 7a9d6a0..e7b5885 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml
@@ -17,7 +17,7 @@
~ limitations under the License.
-->
-<domain xmlns="${xmlns.domain}">
+<domain xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml
index 1dce732..a5c9afb 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml
@@ -23,7 +23,7 @@
via host-slave.xml
-->
-<host name="master" xmlns="${xmlns.domain}">
+<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml
index 6366860..f5d89ee 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml
@@ -22,7 +22,7 @@
is also started by this host controller file. The other instance must be started
via host-slave.xml
-->
-<host name="master" xmlns="${xmlns.domain}">
+<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml
index e90b782..f8695d7 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml
@@ -17,7 +17,7 @@
~ limitations under the License.
-->
-<host xmlns="${xmlns.domain}">
+<host xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml
index 0a72df3..c0cc9e5 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml
@@ -17,7 +17,7 @@
~ limitations under the License.
-->
-<server xmlns="${xmlns.domain}">
+<server xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml
new file mode 100755
index 0000000..d2a8706
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/subsystems.xml
@@ -0,0 +1,85 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
+<config>
+ <subsystems name="load-balancer">
+ <!-- Each subsystem to be included relative to the src/main/resources directory -->
+ <subsystem>logging.xml</subsystem>
+ <subsystem>io.xml</subsystem>
+ <subsystem supplement="domain">jmx.xml</subsystem>
+ <subsystem>naming.xml</subsystem>
+ <subsystem>remoting.xml</subsystem>
+ <subsystem>request-controller.xml</subsystem>
+ <subsystem>security.xml</subsystem>
+ <subsystem>security-manager.xml</subsystem>
+ </subsystems>
+ <subsystems name="auth-server-standalone">
+ <!-- Each subsystem to be included relative to the src/main/resources directory -->
+ <subsystem>logging.xml</subsystem>
+ <subsystem>bean-validation.xml</subsystem>
+ <subsystem supplement="default">keycloak-datasources.xml</subsystem>
+ <subsystem>ee.xml</subsystem>
+ <subsystem>ejb3.xml</subsystem>
+ <subsystem>io.xml</subsystem>
+ <subsystem>keycloak-infinispan.xml</subsystem>
+ <subsystem>jaxrs.xml</subsystem>
+ <subsystem>jca.xml</subsystem>
+ <subsystem>jdr.xml</subsystem>
+ <subsystem supplement="domain">jmx.xml</subsystem>
+ <subsystem>jpa.xml</subsystem>
+ <subsystem>jsf.xml</subsystem>
+ <subsystem>mail.xml</subsystem>
+ <subsystem>naming.xml</subsystem>
+ <subsystem>remoting.xml</subsystem>
+ <subsystem>request-controller.xml</subsystem>
+ <subsystem>security.xml</subsystem>
+ <subsystem>security-manager.xml</subsystem>
+ <subsystem>transactions.xml</subsystem>
+ <subsystem>undertow.xml</subsystem>
+ <subsystem>keycloak-server.xml</subsystem>
+ </subsystems>
+
+ <subsystems name="auth-server-clustered">
+ <!-- Each subsystem to be included relative to the src/main/resources directory -->
+ <subsystem>logging.xml</subsystem>
+ <subsystem>bean-validation.xml</subsystem>
+ <subsystem supplement="domain">keycloak-datasources.xml</subsystem>
+ <subsystem>ee.xml</subsystem>
+ <subsystem supplement="ha">ejb3.xml</subsystem>
+ <subsystem>io.xml</subsystem>
+ <subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
+ <subsystem>jaxrs.xml</subsystem>
+ <subsystem>jca.xml</subsystem>
+ <subsystem>jdr.xml</subsystem>
+ <subsystem>jgroups.xml</subsystem>
+ <subsystem supplement="domain">jmx.xml</subsystem>
+ <subsystem>jpa.xml</subsystem>
+ <subsystem>jsf.xml</subsystem>
+ <subsystem>mail.xml</subsystem>
+ <subsystem>mod_cluster.xml</subsystem>
+ <subsystem>naming.xml</subsystem>
+ <subsystem>remoting.xml</subsystem>
+ <subsystem>request-controller.xml</subsystem>
+ <subsystem>security.xml</subsystem>
+ <subsystem>security-manager.xml</subsystem>
+ <subsystem>transactions.xml</subsystem>
+ <subsystem supplement="ha">undertow.xml</subsystem>
+ <subsystem>keycloak-server.xml</subsystem>
+ </subsystems>
+</config>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml
new file mode 100755
index 0000000..e7b5885
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/domain/template.xml
@@ -0,0 +1,146 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<domain xmlns="urn:jboss:domain:4.0">
+
+ <extensions>
+ <?EXTENSIONS?>
+ </extensions>
+
+ <system-properties>
+ <!-- IPv4 is not required, but setting this helps avoid unintended use of IPv6 -->
+ <property name="java.net.preferIPv4Stack" value="true"/>
+ </system-properties>
+
+ <management>
+ <access-control provider="simple">
+ <role-mapping>
+ <role name="SuperUser">
+ <include>
+ <user name="$local"/>
+ </include>
+ </role>
+ </role-mapping>
+ </access-control>
+ </management>
+
+ <profiles>
+ <!-- Non clustered authentication server profile -->
+ <profile name="auth-server-standalone">
+ <?SUBSYSTEMS socket-binding-group="standard-sockets"?>
+ </profile>
+ <!--
+ Clustering authentication server setup.
+
+ You must configure a remote shared external database like PostgreSQL or MySql if you want this to be
+ able to work on multiple machines.
+ -->
+ <profile name="auth-server-clustered">
+ <?SUBSYSTEMS socket-binding-group="ha-sockets"?>
+ </profile>
+ <!--
+ This is a profile for the built-in Underto Loadbalancer
+ It should be removed in production systems and replaced with a better software or hardware based one
+ -->
+ <profile name="load-balancer">
+ <?SUBSYSTEMS socket-binding-group="load-balancer-sockets"?>
+ <subsystem xmlns="urn:jboss:domain:undertow:3.0">
+ <buffer-cache name="default"/>
+ <server name="default-server">
+ <http-listener name="default" socket-binding="http" redirect-socket="https"/>
+ <host name="default-host" alias="localhost">
+ <location name="/" handler="lb-handler"/>
+ <filter-ref name="server-header"/>
+ <filter-ref name="x-powered-by-header"/>
+ </host>
+ </server>
+ <servlet-container name="default">
+ <jsp-config/>
+ <websockets/>
+ </servlet-container>
+ <handlers>
+ <reverse-proxy name="lb-handler">
+ <host name="host1" outbound-socket-binding="remote-host1" scheme="ajp" path="/" instance-id="myroute1"/>
+ <host name="host2" outbound-socket-binding="remote-host2" scheme="ajp" path="/" instance-id="myroute2"/>
+ </reverse-proxy>
+ </handlers>
+ <filters>
+ <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
+ <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
+ </filters>
+ </subsystem>
+ </profile>
+ </profiles>
+
+ <!--
+ Named interfaces that can be referenced elsewhere in the configuration. The configuration
+ for how to associate these logical names with an actual network interface can either
+ be specified here or can be declared on a per-host basis in the equivalent element in host.xml.
+
+ These default configurations require the binding specification to be done in host.xml.
+ -->
+ <interfaces>
+ <interface name="management">
+ <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
+ </interface>
+ <interface name="public">
+ <inet-address value="${jboss.bind.address:127.0.0.1}"/>
+ </interface>
+ <?INTERFACES?>
+ </interfaces>
+
+ <socket-binding-groups>
+ <socket-binding-group name="standard-sockets" default-interface="public">
+ <?SOCKET-BINDINGS?>
+ </socket-binding-group>
+ <socket-binding-group name="ha-sockets" default-interface="public">
+ <?SOCKET-BINDINGS?>
+ </socket-binding-group>
+ <!-- load-balancer-sockets should be removed in production systems and replaced with a better softare or hardare based one -->
+ <socket-binding-group name="load-balancer-sockets" default-interface="public">
+ <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
+ <socket-binding name="http" port="${jboss.http.port:8080}"/>
+ <socket-binding name="https" port="${jboss.https.port:8443}"/>
+ <outbound-socket-binding name="remote-host1">
+ <remote-destination host="localhost" port="8159"/>
+ </outbound-socket-binding>
+ <outbound-socket-binding name="remote-host2">
+ <remote-destination host="localhost" port="8259"/>
+ </outbound-socket-binding>
+ <?SOCKET-BINDINGS?>
+ </socket-binding-group>
+ </socket-binding-groups>
+
+ <server-groups>
+ <!-- load-balancer-group should be removed in production systems and replaced with a better softare or hardare based one -->
+ <server-group name="load-balancer-group" profile="load-balancer">
+ <jvm name="default">
+ <heap size="64m" max-size="512m"/>
+ </jvm>
+ <socket-binding-group ref="load-balancer-sockets"/>
+ </server-group>
+ <server-group name="auth-server-group" profile="auth-server-clustered">
+ <jvm name="default">
+ <heap size="64m" max-size="512m"/>
+ </jvm>
+ <socket-binding-group ref="ha-sockets"/>
+ </server-group>
+ </server-groups>
+
+</domain>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml
new file mode 100755
index 0000000..a5c9afb
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host.xml
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!--
+ Runs an HTTP Loadbalancer that balances to two separate auth server instances. The first auth server instance
+ is also started by this host controller file. The other instance must be started
+ via host-slave.xml
+-->
+
+<host name="master" xmlns="urn:jboss:domain:4.0">
+ <extensions>
+ <?EXTENSIONS?>
+ </extensions>
+
+ <management>
+ <security-realms>
+ <security-realm name="ManagementRealm">
+ <authentication>
+ <local default-user="$local" skip-group-loading="true"/>
+ <properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
+ </authentication>
+ <authorization map-groups-to-roles="false">
+ <properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
+ </authorization>
+ </security-realm>
+ <security-realm name="ApplicationRealm">
+ <authentication>
+ <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
+ <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
+ </authentication>
+ <authorization>
+ <properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
+ </authorization>
+ </security-realm>
+ </security-realms>
+ <audit-log>
+ <formatters>
+ <json-formatter name="json-formatter"/>
+ </formatters>
+ <handlers>
+ <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
+ <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
+ </handlers>
+ <logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="host-file"/>
+ </handlers>
+ </logger>
+ <server-logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="server-file"/>
+ </handlers>
+ </server-logger>
+ </audit-log>
+ <management-interfaces>
+ <native-interface security-realm="ManagementRealm">
+ <socket interface="management" port="${jboss.management.native.port:9999}"/>
+ </native-interface>
+ <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
+ <socket interface="management" port="${jboss.management.http.port:9990}"/>
+ </http-interface>
+ </management-interfaces>
+ </management>
+
+ <domain-controller>
+ <local/>
+ </domain-controller>
+
+ <interfaces>
+ <interface name="management">
+ <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
+ </interface>
+ <interface name="public">
+ <inet-address value="${jboss.bind.address:127.0.0.1}"/>
+ </interface>
+
+ <?INTERFACES?>
+
+ </interfaces>
+
+ <jvms>
+ <jvm name="default">
+ <heap size="64m" max-size="256m"/>
+ <jvm-options>
+ <option value="-server"/>
+ </jvm-options>
+ </jvm>
+ </jvms>
+
+ <servers>
+ <!-- load-balancer should be removed in production systems and replaced with a better softare or hardare based one -->
+ <server name="load-balancer" group="load-balancer-group">
+ </server>
+ <server name="server-one" group="auth-server-group" auto-start="true">
+ <!-- Remote JPDA debugging for a specific server
+ <jvm name="default">
+ <jvm-options>
+ <option value="-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"/>
+ </jvm-options>
+ </jvm>
+ -->
+ <!-- server-two avoids port conflicts by incrementing the ports in
+ the default socket-group declared in the server-group -->
+ <socket-bindings port-offset="150"/>
+ </server>
+ </servers>
+
+ <profile>
+ <?SUBSYSTEMS socket-binding-group="standard-sockets"?>
+ </profile>
+</host>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml
new file mode 100755
index 0000000..f5d89ee
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-master.xml
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!--
+ Runs an HTTP Loadbalancer that balances to two separate auth server instances. The first auth server instance
+ is also started by this host controller file. The other instance must be started
+ via host-slave.xml
+-->
+<host name="master" xmlns="urn:jboss:domain:4.0">
+ <extensions>
+ <?EXTENSIONS?>
+ </extensions>
+
+ <management>
+ <security-realms>
+ <security-realm name="ManagementRealm">
+ <authentication>
+ <local default-user="$local" skip-group-loading="true"/>
+ <properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
+ </authentication>
+ <authorization map-groups-to-roles="false">
+ <properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
+ </authorization>
+ </security-realm>
+ <security-realm name="ApplicationRealm">
+ <authentication>
+ <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
+ <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
+ </authentication>
+ <authorization>
+ <properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
+ </authorization>
+ </security-realm>
+ </security-realms>
+ <audit-log>
+ <formatters>
+ <json-formatter name="json-formatter"/>
+ </formatters>
+ <handlers>
+ <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
+ <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
+ </handlers>
+ <logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="host-file"/>
+ </handlers>
+ </logger>
+ <server-logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="server-file"/>
+ </handlers>
+ </server-logger>
+ </audit-log>
+ <management-interfaces>
+ <native-interface security-realm="ManagementRealm">
+ <socket interface="management" port="${jboss.management.native.port:9999}"/>
+ </native-interface>
+ <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
+ <socket interface="management" port="${jboss.management.http.port:9990}"/>
+ </http-interface>
+ </management-interfaces>
+ </management>
+
+ <domain-controller>
+ <local/>
+ </domain-controller>
+
+ <interfaces>
+ <interface name="management">
+ <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
+ </interface>
+ <interface name="public">
+ <inet-address value="${jboss.bind.address:127.0.0.1}"/>
+ </interface>
+
+ <?INTERFACES?>
+
+ </interfaces>
+
+ <jvms>
+ <jvm name="default">
+ <heap size="64m" max-size="256m"/>
+ <jvm-options>
+ <option value="-server"/>
+ </jvm-options>
+ </jvm>
+ </jvms>
+
+ <servers>
+ <!-- load-balancer should be removed in production systems and replaced with a better softare or hardare based one -->
+ <server name="load-balancer" group="load-balancer-group">
+ </server>
+ <server name="server-one" group="auth-server-group" auto-start="true">
+ <!-- Remote JPDA debugging for a specific server
+ <jvm name="default">
+ <jvm-options>
+ <option value="-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"/>
+ </jvm-options>
+ </jvm>
+ -->
+ <!-- server-two avoids port conflicts by incrementing the ports in
+ the default socket-group declared in the server-group -->
+ <socket-bindings port-offset="150"/>
+ </server>
+ </servers>
+
+ <profile>
+ <?SUBSYSTEMS socket-binding-group="standard-sockets"?>
+ </profile>
+
+</host>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml
new file mode 100755
index 0000000..f8695d7
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/host-slave.xml
@@ -0,0 +1,117 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<host xmlns="urn:jboss:domain:4.0">
+ <extensions>
+ <?EXTENSIONS?>
+ </extensions>
+
+ <management>
+ <security-realms>
+ <security-realm name="ManagementRealm">
+ <server-identities>
+ <!-- Replace this with either a base64 password of your own, or use a vault with a vault expression -->
+ <secret value="c2xhdmVfdXNlcl9wYXNzd29yZA=="/>
+ </server-identities>
+
+ <authentication>
+ <local default-user="$local" skip-group-loading="true"/>
+ <properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
+ </authentication>
+ <authorization map-groups-to-roles="false">
+ <properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
+ </authorization>
+ </security-realm>
+ <security-realm name="ApplicationRealm">
+ <authentication>
+ <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
+ <properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
+ </authentication>
+ <authorization>
+ <properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
+ </authorization>
+ </security-realm>
+ </security-realms>
+ <audit-log>
+ <formatters>
+ <json-formatter name="json-formatter"/>
+ </formatters>
+ <handlers>
+ <file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
+ <file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
+ </handlers>
+ <logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="host-file"/>
+ </handlers>
+ </logger>
+ <server-logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="server-file"/>
+ </handlers>
+ </server-logger>
+ </audit-log>
+ <management-interfaces>
+ <native-interface security-realm="ManagementRealm">
+ <socket interface="management" port="${jboss.management.native.port:3456}"/>
+ </native-interface>
+ </management-interfaces>
+ </management>
+
+ <domain-controller>
+ <remote security-realm="ManagementRealm">
+ <discovery-options>
+ <static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address:127.0.0.1}" port="${jboss.domain.master.port:9999}"/>
+ </discovery-options>
+ </remote>
+ </domain-controller>
+
+ <interfaces>
+ <interface name="management">
+ <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
+ </interface>
+ <interface name="public">
+ <inet-address value="${jboss.bind.address:127.0.0.1}"/>
+ </interface>
+
+ <?INTERFACES?>
+
+ </interfaces>
+
+ <jvms>
+ <jvm name="default">
+ <heap size="64m" max-size="256m"/>
+ <jvm-options>
+ <option value="-server"/>
+ </jvm-options>
+ </jvm>
+ </jvms>
+
+ <servers>
+ <server name="server-two" group="auth-server-group" auto-start="true">
+ <!-- server-two avoids port conflicts by incrementing the ports in
+ the default socket-group declared in the server-group -->
+ <socket-bindings port-offset="250"/>
+ </server>
+ </servers>
+
+ <profile>
+ <?SUBSYSTEMS socket-binding-group="standard-sockets"?>
+ </profile>
+</host>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml
new file mode 100644
index 0000000..ada31ff
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/host/subsystems.xml
@@ -0,0 +1,24 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
+<config>
+ <subsystems>
+ <subsystem>jmx.xml</subsystem>
+ </subsystems>
+</config>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/standalone/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/standalone/template.xml
index e28a49b..7b13afe 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/standalone/template.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources-wf11/configuration/standalone/template.xml
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
-<server xmlns="${xmlns.domain}">
+<server xmlns="urn:jboss:domain:5.0">
<extensions>
<?EXTENSIONS?>
distribution/server-dist/pom.xml 11(+10 -1)
diff --git a/distribution/server-dist/pom.xml b/distribution/server-dist/pom.xml
index c870aba..57b9d52 100755
--- a/distribution/server-dist/pom.xml
+++ b/distribution/server-dist/pom.xml
@@ -42,6 +42,7 @@
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-server-provisioning-maven-plugin</artifactId>
+ <version>${build-tools.version}</version>
<executions>
<execution>
<id>server-provisioning</id>
@@ -74,7 +75,6 @@
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<workDirectory>${project.build.directory}/assembly/work</workDirectory>
- <tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
@@ -114,6 +114,7 @@
</property>
</activation>
<properties>
+ <build-tools.version>${wildfly.build-tools.version}</build-tools.version>
<assemblyFile>assembly.xml</assemblyFile>
</properties>
<build>
@@ -122,6 +123,13 @@
</profile>
<profile>
+ <id>wf11</id>
+ <properties>
+ <build-tools.version>${wildfly11.build-tools.version}</build-tools.version>
+ </properties>
+ </profile>
+
+ <profile>
<id>product</id>
<activation>
<property>
@@ -129,6 +137,7 @@
</property>
</activation>
<properties>
+ <build-tools.version>${eap.build-tools.version}</build-tools.version>
<assemblyFile>assembly.xml</assemblyFile>
<profileExcludes>%regex[(providers.*)|(docs/contrib.*)|(docs/examples.*)|(docs/schema.*)]</profileExcludes>
</properties>
distribution/server-overlay/pom.xml 1(+0 -1)
diff --git a/distribution/server-overlay/pom.xml b/distribution/server-overlay/pom.xml
index 6b6143f..a0eec7e 100755
--- a/distribution/server-overlay/pom.xml
+++ b/distribution/server-overlay/pom.xml
@@ -138,7 +138,6 @@
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<workDirectory>${project.build.directory}/assembly/work</workDirectory>
- <tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
index fa7ed05..a37e085 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -20,6 +20,7 @@ package org.keycloak.admin.client;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider;
import org.keycloak.admin.client.resource.BearerAuthFilter;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RealmsResource;
@@ -31,7 +32,6 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.net.URI;
-import java.security.KeyStore;
import static org.keycloak.OAuth2Constants.PASSWORD;
@@ -66,12 +66,20 @@ public class Keycloak {
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext) {
- ResteasyClient client = new ResteasyClientBuilder()
+ return getInstance(serverUrl, realm, username, password, clientId, clientSecret, sslContext, null);
+ }
+
+ public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, ResteasyJackson2Provider customJacksonProvider) {
+ ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder()
.sslContext(sslContext)
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD)
- .connectionPoolSize(10).build();
+ .connectionPoolSize(10);
+
+ if (customJacksonProvider != null) {
+ clientBuilder.register(customJacksonProvider);
+ }
- return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, client, null);
+ return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, clientBuilder.build(), null);
}
private static ResteasyClientBuilder newResteasyClientBuilder() {
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java
index 07276ec..de8d958 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java
@@ -23,6 +23,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@@ -58,4 +59,7 @@ public interface AuthorizationResource {
@Path("/policy")
PoliciesResource policies();
+
+ @Path("/permission")
+ PermissionsResource permissions();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java
new file mode 100644
index 0000000..679137b
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import javax.ws.rs.Path;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PermissionsResource {
+
+ @Path("resource")
+ ResourcePermissionsResource resource();
+
+ @Path("scope")
+ ScopePermissionsResource scope();
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
index e5120c6..433a112 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
@@ -28,6 +28,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@@ -45,6 +46,12 @@ public interface PoliciesResource {
@Path("{id}")
PolicyResource policy(@PathParam("id") String id);
+ @Path("/search")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ PolicyRepresentation findByName(@QueryParam("name") String name);
+
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@@ -62,4 +69,9 @@ public interface PoliciesResource {
@Path("evaluate")
PolicyEvaluationResponse evaluate(PolicyEvaluationRequest evaluationRequest);
+ @Path("role")
+ RolePoliciesResource roles();
+
+ @Path("user")
+ UserPoliciesResource users();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java
new file mode 100644
index 0000000..ec49ec8
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ResourcePermissionResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ ResourcePermissionRepresentation toRepresentation();
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ void update(ResourcePermissionRepresentation representation);
+
+ @DELETE
+ void remove();
+
+ @Path("/associatedPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> associatedPolicies();
+
+ @Path("/dependentPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> dependentPolicies();
+
+ @Path("/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ResourceRepresentation> resources();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
new file mode 100644
index 0000000..c833f01
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ResourcePermissionsResource {
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ Response create(ResourcePermissionRepresentation representation);
+
+ @Path("{id}")
+ ResourcePermissionResource findById(@PathParam("id") String id);
+
+ @Path("/search")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ ResourcePermissionRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java
index 3a48114..ddcaf53 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java
@@ -25,6 +25,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@@ -46,4 +47,10 @@ public interface ResourceScopesResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ScopeRepresentation> scopes();
+
+ @Path("/search")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ ScopeRepresentation findByName(@QueryParam("name") String name);
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
index 07438d0..e7daaa1 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
@@ -57,5 +57,10 @@ public interface ResourcesResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
+ List<ResourceRepresentation> findByName(@QueryParam("name") String name);
+
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> resources();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java
new file mode 100644
index 0000000..f9f1f98
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePoliciesResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface RolePoliciesResource {
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ Response create(RolePolicyRepresentation representation);
+
+ @Path("{id}")
+ RolePolicyResource findById(@PathParam("id") String id);
+
+ @Path("/search")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ RolePolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePolicyResource.java
new file mode 100644
index 0000000..824554f
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RolePolicyResource.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface RolePolicyResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ RolePolicyRepresentation toRepresentation();
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ void update(RolePolicyRepresentation representation);
+
+ @DELETE
+ void remove();
+
+ @Path("/associatedPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> associatedPolicies();
+
+ @Path("/dependentPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> dependentPolicies();
+
+ @Path("/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ResourceRepresentation> resources();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionResource.java
new file mode 100644
index 0000000..199e412
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionResource.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ScopePermissionResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ ScopePermissionRepresentation toRepresentation();
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ void update(ScopePermissionRepresentation representation);
+
+ @DELETE
+ void remove();
+
+ @Path("/associatedPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> associatedPolicies();
+
+ @Path("/dependentPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> dependentPolicies();
+
+ @Path("/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ResourceRepresentation> resources();
+
+ @Path("/scopes")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ScopeRepresentation> scopes();
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java
new file mode 100644
index 0000000..ab0dd5c
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ScopePermissionsResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ScopePermissionsResource {
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ Response create(ScopePermissionRepresentation representation);
+
+ @Path("{id}")
+ ScopePermissionResource findById(@PathParam("id") String id);
+
+ @Path("/search")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ ScopePermissionRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java
new file mode 100644
index 0000000..702995f
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPoliciesResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface UserPoliciesResource {
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ Response create(UserPolicyRepresentation representation);
+
+ @Path("{id}")
+ UserPolicyResource findById(@PathParam("id") String id);
+
+ @Path("/search")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ UserPolicyRepresentation findByName(@QueryParam("name") String name);
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPolicyResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPolicyResource.java
new file mode 100644
index 0000000..d12378c
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserPolicyResource.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface UserPolicyResource {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ UserPolicyRepresentation toRepresentation();
+
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ void update(UserPolicyRepresentation representation);
+
+ @DELETE
+ void remove();
+
+ @Path("/associatedPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> associatedPolicies();
+
+ @Path("/dependentPolicies")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ List<PolicyRepresentation> dependentPolicies();
+
+ @Path("/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ List<ResourceRepresentation> resources();
+
+}
diff --git a/misc/keycloak-test-helper/src/main/java/org/keycloak/test/builders/ClientBuilder.java b/misc/keycloak-test-helper/src/main/java/org/keycloak/test/builders/ClientBuilder.java
index 0e51c90..b4c78bc 100644
--- a/misc/keycloak-test-helper/src/main/java/org/keycloak/test/builders/ClientBuilder.java
+++ b/misc/keycloak-test-helper/src/main/java/org/keycloak/test/builders/ClientBuilder.java
@@ -28,6 +28,8 @@ public class ClientBuilder {
private ClientRepresentation rep;
+ public enum AccessType { BEARER_ONLY, PUBLIC, CONFIDENTIAL };
+
public static ClientBuilder create(String clientId) {
ClientRepresentation rep = new ClientRepresentation();
rep.setEnabled(Boolean.TRUE);
@@ -39,9 +41,19 @@ public class ClientBuilder {
this.rep = rep;
}
- public ClientRepresentation bearerOnly(boolean bearerOnly) {
- rep.setBearerOnly(bearerOnly);
- return rep;
+ public ClientRepresentation accessType(AccessType accessType) {
+ switch (accessType) {
+ case BEARER_ONLY:
+ rep.setBearerOnly(true);
+ break;
+ case PUBLIC:
+ rep.setPublicClient(true);
+ break;
+ case CONFIDENTIAL:
+ rep.setPublicClient(false);
+ break;
+ }
+ return defaultSettings();
}
public ClientBuilder rootUrl(String rootUrl) {
@@ -64,9 +76,13 @@ public class ClientBuilder {
return this;
}
- public ClientRepresentation publicClient(boolean publicClient) {
+ public ClientBuilder secret(String secret) {
+ rep.setSecret(secret);
+ return this;
+ }
+
+ private ClientRepresentation defaultSettings() {
rep.setFullScopeAllowed(true);
- rep.setPublicClient(publicClient);
rep.setDirectAccessGrantsEnabled(true);
rep.setAdminUrl(rep.getRootUrl());
diff --git a/misc/keycloak-test-helper/src/main/java/org/keycloak/test/TestsHelper.java b/misc/keycloak-test-helper/src/main/java/org/keycloak/test/TestsHelper.java
index fecfbc7..27bf7de 100644
--- a/misc/keycloak-test-helper/src/main/java/org/keycloak/test/TestsHelper.java
+++ b/misc/keycloak-test-helper/src/main/java/org/keycloak/test/TestsHelper.java
@@ -43,6 +43,8 @@ import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
+import static org.keycloak.test.builders.ClientBuilder.AccessType.PUBLIC;
+
public class TestsHelper {
public static String baseUrl;
@@ -83,7 +85,7 @@ public class TestsHelper {
}
public static String createDirectGrantClient() {
- return createClient(ClientBuilder.create("test-dga").publicClient(true));
+ return createClient(ClientBuilder.create("test-dga").accessType(PUBLIC));
}
public static void deleteClient(String clientId) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
index 9cd4e74..b517098 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
@@ -42,6 +42,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
import org.keycloak.models.authorization.infinispan.entities.CachedPolicy;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
@@ -53,15 +54,14 @@ public class CachedPolicyStore implements PolicyStore {
private static final String POLICY_ID_CACHE_PREFIX = "policy-id-";
private final Cache<String, Map<String, List<CachedPolicy>>> cache;
- private final KeycloakSession session;
+ private final CachedStoreFactoryProvider cacheStoreFactory;
private final CacheTransaction transaction;
private final List<String> cacheKeys;
- private StoreFactory storeFactory;
+ private final StoreFactory storeFactory;
private PolicyStore delegate;
- private CachedStoreFactoryProvider cachedStoreFactory;
- public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
- this.session = session;
+ public CachedPolicyStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
+ this.cacheStoreFactory = cacheStoreFactory;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
@@ -70,12 +70,12 @@ public class CachedPolicyStore implements PolicyStore {
cacheKeys.add("findByResourceType");
cacheKeys.add("findByScopeIds");
cacheKeys.add("findByType");
- this.storeFactory = storeFactory;
+ this.storeFactory = delegate;
}
@Override
- public Policy create(String name, String type, ResourceServer resourceServer) {
- Policy policy = getDelegate().create(name, type, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
+ public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
+ Policy policy = getDelegate().create(representation, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
String id = policy.getId();
this.transaction.whenRollback(() -> {
@@ -102,6 +102,10 @@ public class CachedPolicyStore implements PolicyStore {
@Override
public Policy findById(String id, String resourceServerId) {
+ if (resourceServerId == null) {
+ return getDelegate().findById(id, null);
+ }
+
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
List<CachedPolicy> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy);
@@ -285,6 +289,7 @@ public class CachedPolicyStore implements PolicyStore {
public void removeScope(Scope scope) {
getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()));
cached.removeScope(scope);
+ scopes.remove(scope);
}
@Override
@@ -297,6 +302,7 @@ public class CachedPolicyStore implements PolicyStore {
public void removeAssociatedPolicy(Policy associatedPolicy) {
getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId()));
cached.removeAssociatedPolicy(associatedPolicy);
+ associatedPolicies.remove(associatedPolicy);
}
@Override
@@ -309,6 +315,7 @@ public class CachedPolicyStore implements PolicyStore {
public void removeResource(Resource resource) {
getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
cached.removeResource(resource);
+ resources.remove(resource);
}
@Override
@@ -401,10 +408,7 @@ public class CachedPolicyStore implements PolicyStore {
}
private CachedStoreFactoryProvider getCachedStoreFactory() {
- if (cachedStoreFactory == null) {
- cachedStoreFactory = session.getProvider(CachedStoreFactoryProvider.class);
- }
- return cachedStoreFactory;
+ return cacheStoreFactory;
}
private void invalidateCache(String resourceServerId) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
index 95d3dba..a98a34e 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
@@ -41,18 +41,16 @@ public class CachedResourceServerStore implements ResourceServerStore {
private static final String RS_ID_CACHE_PREFIX = "rs-id-";
private static final String RS_CLIENT_ID_CACHE_PREFIX = "rs-client-id-";
- private final KeycloakSession session;
private final CacheTransaction transaction;
private StoreFactory storeFactory;
private ResourceServerStore delegate;
private final Cache<String, Map<String, List<CachedResourceServer>>> cache;
- public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
- this.session = session;
+ public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory delegate) {
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
- this.storeFactory = storeFactory;
+ this.storeFactory = delegate;
}
@Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
index b016702..e820ab0 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
@@ -49,15 +49,15 @@ public class CachedResourceStore implements ResourceStore {
private static final String RESOURCE_ID_CACHE_PREFIX = "rsc-id-";
private static final String RESOURCE_NAME_CACHE_PREFIX = "rsc-name-";
- private final KeycloakSession session;
+ private final CachedStoreFactoryProvider cacheStoreFactory;
private final CacheTransaction transaction;
private final List<String> cacheKeys;
- private StoreFactory storeFactory;
+ private StoreFactory delegateStoreFactory;
private ResourceStore delegate;
private final Cache<String, Map<String, List<CachedResource>>> cache;
- public CachedResourceStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
- this.session = session;
+ public CachedResourceStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
+ this.cacheStoreFactory = cacheStoreFactory;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
this.transaction = transaction;
@@ -65,12 +65,12 @@ public class CachedResourceStore implements ResourceStore {
cacheKeys.add("findByOwner");
cacheKeys.add("findByUri");
cacheKeys.add("findByName");
- this.storeFactory = storeFactory;
+ this.delegateStoreFactory = delegate;
}
@Override
public Resource create(String name, ResourceServer resourceServer, String owner) {
- Resource resource = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
+ Resource resource = getDelegate().create(name, getDelegateStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
this.transaction.whenRollback(() -> {
resolveResourceServerCache(resourceServer.getId()).remove(getCacheKeyForResource(resource.getId()));
@@ -176,14 +176,14 @@ public class CachedResourceStore implements ResourceStore {
private ResourceStore getDelegate() {
if (this.delegate == null) {
- this.delegate = getStoreFactory().getResourceStore();
+ this.delegate = getDelegateStoreFactory().getResourceStore();
}
return this.delegate;
}
- private StoreFactory getStoreFactory() {
- return this.storeFactory;
+ private StoreFactory getDelegateStoreFactory() {
+ return this.delegateStoreFactory;
}
private Resource createAdapter(CachedResource cached) {
@@ -270,7 +270,7 @@ public class CachedResourceStore implements ResourceStore {
@Override
public void updateScopes(Set<Scope> scopes) {
- getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
+ getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getDelegateStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
cached.updateScopes(scopes);
}
@@ -293,7 +293,7 @@ public class CachedResourceStore implements ResourceStore {
}
private CachedStoreFactoryProvider getCachedStoreFactory() {
- return session.getProvider(CachedStoreFactoryProvider.class);
+ return cacheStoreFactory;
}
private List<Resource> cacheResult(String resourceServerId, String key, Supplier<List<Resource>> provider) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
index ef2e19b..fb878d0 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
@@ -44,17 +44,17 @@ public class CachedScopeStore implements ScopeStore {
private static final String SCOPE_NAME_CACHE_PREFIX = "scp-name-";
private final Cache<String, Map<String, List<CachedScope>>> cache;
- private final KeycloakSession session;
+ private final CachedStoreFactoryProvider cacheStoreFactory;
private final CacheTransaction transaction;
private ScopeStore delegate;
private StoreFactory storeFactory;
- public CachedScopeStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
- this.session = session;
+ public CachedScopeStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
+ this.cacheStoreFactory = cacheStoreFactory;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
- this.storeFactory = storeFactory;
+ this.storeFactory = delegate;
}
@Override
@@ -220,7 +220,7 @@ public class CachedScopeStore implements ScopeStore {
}
private CachedStoreFactoryProvider getCachedStoreFactory() {
- return session.getProvider(CachedStoreFactoryProvider.class);
+ return cacheStoreFactory;
}
private void invalidateCache(String resourceServerId) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
index 775cf64..c7bef79 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
@@ -160,7 +160,7 @@ public class CachedPolicy implements Policy, Serializable {
@Override
public void removeResource(Resource resource) {
- this.resourcesIds.add(resource.getId());
+ this.resourcesIds.remove(resource.getId());
}
@Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
index ff66da6..bbc3848 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreFactoryProvider.java
@@ -18,6 +18,9 @@
package org.keycloak.models.authorization.infinispan;
+import java.util.ArrayList;
+import java.util.List;
+
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
@@ -27,31 +30,25 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvider {
- private final KeycloakSession session;
private final CacheTransaction transaction;
- private final StoreFactory storeFactory;
private final CachedResourceStore resourceStore;
private final CachedScopeStore scopeStore;
private final CachedPolicyStore policyStore;
private ResourceServerStore resourceServerStore;
- InfinispanStoreFactoryProvider(KeycloakSession delegate) {
- this.session = delegate;
+ public InfinispanStoreFactoryProvider(KeycloakSession session) {
this.transaction = new CacheTransaction();
- this.session.getTransactionManager().enlistAfterCompletion(transaction);
- storeFactory = this.session.getProvider(StoreFactory.class);
- resourceStore = new CachedResourceStore(this.session, this.transaction, storeFactory);
- resourceServerStore = new CachedResourceServerStore(this.session, this.transaction, storeFactory);
- scopeStore = new CachedScopeStore(this.session, this.transaction, storeFactory);
- policyStore = new CachedPolicyStore(this.session, this.transaction, storeFactory);
+ session.getTransactionManager().enlistAfterCompletion(transaction);
+ StoreFactory delegate = session.getProvider(StoreFactory.class);
+ resourceStore = new CachedResourceStore(session, this, this.transaction, delegate);
+ resourceServerStore = new CachedResourceServerStore(session, this.transaction, delegate);
+ scopeStore = new CachedScopeStore(session, this, this.transaction, delegate);
+ policyStore = new CachedPolicyStore(session, this, this.transaction, delegate);
}
@Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java
index ff7452b..ad58890 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/InfinispanStoreProviderFactory.java
@@ -19,6 +19,8 @@
package org.keycloak.models.authorization.infinispan;
import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 7d68c18..85ace35 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -637,6 +637,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
while(itr.hasNext()) {
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
Set<String> currClientSessions = entity.getClientSessions();
+
+ if (currClientSessions == null) {
+ continue;
+ }
+
for (String clientSessionId : currClientSessions) {
ClientSessionEntity cls = (ClientSessionEntity) offlineSessionCache.get(clientSessionId);
if (cls != null) {
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java
index 8788884..8750807 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAAuthorizationStoreFactory.java
@@ -18,14 +18,15 @@
package org.keycloak.authorization.jpa.store;
+import javax.persistence.EntityManager;
+
import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.store.AuthorizationStoreFactory;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
-import javax.persistence.EntityManager;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
index 22fb951..c6671de 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
@@ -17,14 +17,10 @@
*/
package org.keycloak.authorization.jpa.store;
-import org.keycloak.authorization.jpa.entities.PolicyEntity;
-import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
-import org.keycloak.authorization.jpa.entities.ScopeEntity;
-import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.model.Scope;
-import org.keycloak.authorization.store.PolicyStore;
-import org.keycloak.models.utils.KeycloakModelUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
@@ -33,11 +29,14 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+
+import org.keycloak.authorization.jpa.entities.PolicyEntity;
+import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -51,23 +50,19 @@ public class JPAPolicyStore implements PolicyStore {
}
@Override
- public Policy create(String name, String type, ResourceServer resourceServer) {
+ public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
PolicyEntity entity = new PolicyEntity();
entity.setId(KeycloakModelUtils.generateId());
- entity.setName(name);
- entity.setType(type);
+ entity.setType(representation.getType());
+ entity.setName(representation.getName());
entity.setResourceServer((ResourceServerEntity) resourceServer);
this.entityManager.persist(entity);
-
+ this.entityManager.flush();
return entity;
}
- public EntityManager getEntityManager() {
- return this.entityManager;
- }
-
@Override
public void delete(String id) {
Policy policy = entityManager.find(PolicyEntity.class, id);
@@ -99,7 +94,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public Policy findByName(String name, String resourceServerId) {
try {
- Query query = getEntityManager().createQuery("from PolicyEntity where name = :name and resourceServer.id = :serverId");
+ Query query = entityManager.createQuery("from PolicyEntity where name = :name and resourceServer.id = :serverId");
query.setParameter("name", name);
query.setParameter("serverId", resourceServerId);
@@ -112,7 +107,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResourceServer(final String resourceServerId) {
- Query query = getEntityManager().createQuery("from PolicyEntity where resourceServer.id = :serverId");
+ Query query = entityManager.createQuery("from PolicyEntity where resourceServer.id = :serverId");
query.setParameter("serverId", resourceServerId);
@@ -158,7 +153,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResource(final String resourceId, String resourceServerId) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)");
+ Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)");
query.setParameter("resourceId", resourceId);
query.setParameter("serverId", resourceServerId);
@@ -168,7 +163,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResourceType(final String resourceType, String resourceServerId) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type");
+ Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type");
query.setParameter("serverId", resourceServerId);
query.setParameter("type", resourceType);
@@ -183,7 +178,7 @@ public class JPAPolicyStore implements PolicyStore {
}
// Use separate subquery to handle DB2 and MSSSQL
- Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))");
+ Query query = entityManager.createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))");
query.setParameter("serverId", resourceServerId);
query.setParameter("scopeIds", scopeIds);
@@ -193,7 +188,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByType(String type, String resourceServerId) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type");
+ Query query = entityManager.createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type");
query.setParameter("serverId", resourceServerId);
query.setParameter("type", type);
@@ -203,7 +198,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findDependentPolicies(String policyId, String resourceServerId) {
- Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)");
+ Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)");
query.setParameter("serverId", resourceServerId);
query.setParameter("policyId", policyId);
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
index 5dad6af..e45d343 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
@@ -18,43 +18,49 @@
package org.keycloak.authorization.jpa.store;
+import javax.persistence.EntityManager;
+
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
-import javax.persistence.EntityManager;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class JPAStoreFactory implements StoreFactory {
- private final EntityManager entityManager;
+ private final PolicyStore policyStore;
+ private final ResourceServerStore resourceServerStore;
+ private final ResourceStore resourceStore;
+ private final ScopeStore scopeStore;
public JPAStoreFactory(EntityManager entityManager) {
- this.entityManager = entityManager;
+ policyStore = new JPAPolicyStore(entityManager);
+ resourceServerStore = new JPAResourceServerStore(entityManager);
+ resourceStore = new JPAResourceStore(entityManager);
+ scopeStore = new JPAScopeStore(entityManager);
}
@Override
public PolicyStore getPolicyStore() {
- return new JPAPolicyStore(this.entityManager);
+ return policyStore;
}
@Override
public ResourceServerStore getResourceServerStore() {
- return new JPAResourceServerStore(this.entityManager);
+ return resourceServerStore;
}
@Override
public ResourceStore getResourceStore() {
- return new JPAResourceStore(this.entityManager);
+ return resourceStore;
}
@Override
public ScopeStore getScopeStore() {
- return new JPAScopeStore(this.entityManager);
+ return scopeStore;
}
@Override
pom.xml 16(+10 -6)
diff --git a/pom.xml b/pom.xml
index 364121c..9fcf0f1 100755
--- a/pom.xml
+++ b/pom.xml
@@ -42,10 +42,12 @@
<!-- WildFly -->
<jboss.as.version>7.2.0.Final</jboss.as.version>
<wildfly.version>10.0.0.Final</wildfly.version>
+ <wildfly.build-tools.version>1.1.3.Final</wildfly.build-tools.version>
<wildfly11.version>11.0.0.Alpha1</wildfly11.version> <!-- for testing with wf11 pre-releases -->
+ <wildfly11.build-tools.version>1.1.8.Final</wildfly11.build-tools.version>
<eap.version>7.1.0.Alpha1-redhat-16</eap.version>
+ <eap.build-tools.version>1.1.8.Final</eap.build-tools.version>
<wildfly.core.version>2.0.10.Final</wildfly.core.version>
- <wildfly.build-tools.version>1.1.8.Final</wildfly.build-tools.version>
<version.org.wildfly.security.wildfly-elytron>1.1.0.Beta32</version.org.wildfly.security.wildfly-elytron>
<version.org.wildfly.security.elytron-web.undertow-server>1.0.0.Beta14</version.org.wildfly.security.elytron-web.undertow-server>
@@ -1393,6 +1395,13 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <tarLongFileMode>posix</tarLongFileMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.2</version>
<configuration>
@@ -1463,11 +1472,6 @@
</plugin>
<plugin>
<groupId>org.wildfly.build</groupId>
- <artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
- <version>${wildfly.build-tools.version}</version>
- </plugin>
- <plugin>
- <groupId>org.wildfly.build</groupId>
<artifactId>wildfly-server-provisioning-maven-plugin</artifactId>
<version>${wildfly.build-tools.version}</version>
</plugin>
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index 3aafae3..c5b75d3 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -21,17 +21,23 @@ package org.keycloak.authorization;
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.permission.evaluator.Evaluators;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.Provider;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* <p>The main contract here is the creation of {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances. Usually
@@ -62,25 +68,19 @@ import org.keycloak.provider.Provider;
public final class AuthorizationProvider implements Provider {
private final DefaultPolicyEvaluator policyEvaluator;
- private final Executor scheduller;
- private final Supplier<StoreFactory> storeFactory;
+ private StoreFactory storeFactory;
private final Map<String, PolicyProviderFactory> policyProviderFactories;
private final KeycloakSession keycloakSession;
private final RealmModel realm;
- public AuthorizationProvider(KeycloakSession session, RealmModel realm, Supplier<StoreFactory> storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories, Executor scheduller) {
+ public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories) {
this.keycloakSession = session;
this.realm = realm;
this.storeFactory = storeFactory;
- this.scheduller = scheduller;
this.policyProviderFactories = policyProviderFactories;
this.policyEvaluator = new DefaultPolicyEvaluator(this);
}
- public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories) {
- this(session, realm, () -> storeFactory, policyProviderFactories, Runnable::run);
- }
-
/**
* Returns a {@link Evaluators} instance from where {@link org.keycloak.authorization.policy.evaluation.PolicyEvaluator} instances
* can be obtained.
@@ -88,7 +88,7 @@ public final class AuthorizationProvider implements Provider {
* @return a {@link Evaluators} instance
*/
public Evaluators evaluators() {
- return new Evaluators(this.policyEvaluator, this.scheduller);
+ return new Evaluators(policyEvaluator);
}
/**
@@ -97,7 +97,105 @@ public final class AuthorizationProvider implements Provider {
* @return the {@link StoreFactory}
*/
public StoreFactory getStoreFactory() {
- return this.storeFactory.get();
+ return createStoreFactory();
+ }
+
+ private StoreFactory createStoreFactory() {
+ return new StoreFactory() {
+ @Override
+ public ResourceStore getResourceStore() {
+ return storeFactory.getResourceStore();
+ }
+
+ @Override
+ public ResourceServerStore getResourceServerStore() {
+ return storeFactory.getResourceServerStore();
+ }
+
+ @Override
+ public ScopeStore getScopeStore() {
+ return storeFactory.getScopeStore();
+ }
+
+ @Override
+ public PolicyStore getPolicyStore() {
+ PolicyStore policyStore = storeFactory.getPolicyStore();
+ return new PolicyStore() {
+ @Override
+ public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
+ return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(representation, resourceServer));
+ }
+
+ @Override
+ public void delete(String id) {
+ Policy policy = findById(id, null);
+
+ if (policy != null) {
+ ResourceServer resourceServer = policy.getResourceServer();
+
+ findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
+ dependentPolicy.removeAssociatedPolicy(policy);
+ if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
+ delete(dependentPolicy.getId());
+ }
+ });
+
+ policyStore.delete(id);
+ }
+ }
+
+ @Override
+ public Policy findById(String id, String resourceServerId) {
+ return policyStore.findById(id, resourceServerId);
+ }
+
+ @Override
+ public Policy findByName(String name, String resourceServerId) {
+ return policyStore.findByName(name, resourceServerId);
+ }
+
+ @Override
+ public List<Policy> findByResourceServer(String resourceServerId) {
+ return policyStore.findByResourceServer(resourceServerId);
+ }
+
+ @Override
+ public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+ return policyStore.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+ }
+
+ @Override
+ public List<Policy> findByResource(String resourceId, String resourceServerId) {
+ return policyStore.findByResource(resourceId, resourceServerId);
+ }
+
+ @Override
+ public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
+ return policyStore.findByResourceType(resourceType, resourceServerId);
+ }
+
+ @Override
+ public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
+ return policyStore.findByScopeIds(scopeIds, resourceServerId);
+ }
+
+ @Override
+ public List<Policy> findByType(String type, String resourceServerId) {
+ return policyStore.findByType(type, resourceServerId);
+ }
+
+ @Override
+ public List<Policy> findDependentPolicies(String id, String resourceServerId) {
+ return policyStore.findDependentPolicies(id, resourceServerId);
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+ storeFactory.close();
+ }
+ };
}
/**
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
index ed1aa89..00e1191 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java
@@ -19,7 +19,6 @@
package org.keycloak.authorization.permission.evaluator;
import java.util.List;
-import java.util.concurrent.Executor;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
@@ -33,11 +32,9 @@ import org.keycloak.authorization.policy.evaluation.EvaluationContext;
public final class Evaluators {
private final DefaultPolicyEvaluator policyEvaluator;
- private final Executor scheduler;
- public Evaluators(DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
+ public Evaluators(DefaultPolicyEvaluator policyEvaluator) {
this.policyEvaluator = policyEvaluator;
- this.scheduler = scheduler;
}
public PermissionEvaluator from(List<ResourcePermission> permissions, EvaluationContext evaluationContext) {
@@ -45,6 +42,6 @@ public final class Evaluators {
}
public PermissionEvaluator schedule(List<ResourcePermission> permissions, EvaluationContext evaluationContext) {
- return new ScheduledPermissionEvaluator(new IterablePermissionEvaluator(permissions.iterator(), evaluationContext, this.policyEvaluator), this.scheduler);
+ return new DefaultPermissionEvaluator(new IterablePermissionEvaluator(permissions.iterator(), evaluationContext, this.policyEvaluator));
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
index 094607a..1ec8887 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -35,6 +35,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
@@ -46,11 +47,13 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
private final AuthorizationProvider authorization;
private final StoreFactory storeFactory;
private final PolicyStore policyStore;
+ private final ResourceStore resourceStore;
public DefaultPolicyEvaluator(AuthorizationProvider authorization) {
this.authorization = authorization;
storeFactory = this.authorization.getStoreFactory();
policyStore = storeFactory.getPolicyStore();
+ resourceStore = storeFactory.getResourceStore();
}
@Override
@@ -159,7 +162,7 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
String type = resource.getType();
if (type != null) {
- List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type, resource.getResourceServer().getId());
+ List<Resource> resourcesByType = resourceStore.findByType(type, resource.getResourceServer().getId());
for (Resource resourceType : resourcesByType) {
if (resourceType.getOwner().equals(resource.getResourceServer().getClientId())) {
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java
index d26208e..b19c55b 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java
@@ -18,16 +18,11 @@
package org.keycloak.authorization.policy.provider;
-import org.keycloak.authorization.model.Policy;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public interface PolicyProviderAdminService {
+public interface PolicyProviderAdminService<R extends AbstractPolicyRepresentation> {
- void onCreate(Policy policy);
-
- void onUpdate(Policy policy);
-
- void onRemove(Policy policy);
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
index f7041b5..af95b00 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
@@ -22,11 +22,13 @@ import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
+public interface PolicyProviderFactory<R extends AbstractPolicyRepresentation> extends ProviderFactory<PolicyProvider> {
String getName();
@@ -34,5 +36,31 @@ public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
PolicyProvider create(AuthorizationProvider authorization);
- PolicyProviderAdminService getAdminResource(ResourceServer resourceServer);
+ default R toRepresentation(Policy policy, R representation) {
+ return representation;
+ }
+
+ default Class<R> getRepresentationType() {
+ return (Class<R>) PolicyRepresentation.class;
+ }
+
+ default void onCreate(Policy policy, R representation, AuthorizationProvider authorization) {
+
+ }
+
+ default void onUpdate(Policy policy, R representation, AuthorizationProvider authorization) {
+
+ }
+
+ default void onRemove(Policy policy, AuthorizationProvider authorization) {
+
+ }
+
+ default void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+
+ }
+
+ default PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
+ return null;
+ }
}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java
index 78a08bd..76a3fab 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java
@@ -18,6 +18,9 @@
package org.keycloak.authorization.store;
+import java.util.HashMap;
+import java.util.Map;
+
import org.keycloak.authorization.store.syncronization.ClientApplicationSynchronizer;
import org.keycloak.authorization.store.syncronization.RealmSynchronizer;
import org.keycloak.authorization.store.syncronization.Synchronizer;
@@ -29,9 +32,6 @@ import org.keycloak.models.UserModel.UserRemovedEvent;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderFactory;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
index 626e317..793185e 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java
@@ -18,12 +18,13 @@
package org.keycloak.authorization.store;
-import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.model.ResourceServer;
-
import java.util.List;
import java.util.Map;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+
/**
* A {@link PolicyStore} is responsible to manage the persistence of {@link Policy} instances.
*
@@ -35,12 +36,11 @@ public interface PolicyStore {
* Creates a new {@link Policy} instance. The new instance is not necessarily persisted though, which may require
* a call to the {#save} method to actually make it persistent.
*
- * @param name the name of the policy
- * @param type the type of the policy
+ * @param representation the policy representation
* @param resourceServer the resource server to which this policy belongs
* @return a new instance of {@link Policy}
*/
- Policy create(String name, String type, ResourceServer resourceServer);
+ Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer);
/**
* Deletes a policy from the underlying persistence mechanism.
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 3090c6e..360f287 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -33,6 +33,7 @@ import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
@@ -87,6 +88,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@@ -793,16 +795,29 @@ public class ModelToRepresentation {
return server;
}
- public static PolicyRepresentation toRepresentation(Policy model) {
- PolicyRepresentation representation = new PolicyRepresentation();
+ public static <R extends AbstractPolicyRepresentation> R toRepresentation(Policy policy, Class<R> representationType, AuthorizationProvider authorization) {
+ R representation;
- representation.setId(model.getId());
- representation.setName(model.getName());
- representation.setDescription(model.getDescription());
- representation.setType(model.getType());
- representation.setDecisionStrategy(model.getDecisionStrategy());
- representation.setLogic(model.getLogic());
- representation.setConfig(new HashMap<>(model.getConfig()));
+ try {
+ representation = representationType.newInstance();
+ } catch (Exception cause) {
+ throw new RuntimeException("Could not create policy [" + policy.getType() + "] representation", cause);
+ }
+
+ PolicyProviderFactory providerFactory = authorization.getProviderFactory(policy.getType());
+
+ representation.setId(policy.getId());
+ representation.setName(policy.getName());
+ representation.setDescription(policy.getDescription());
+ representation.setType(policy.getType());
+ representation.setDecisionStrategy(policy.getDecisionStrategy());
+ representation.setLogic(policy.getLogic());
+
+ if (representation instanceof PolicyRepresentation) {
+ PolicyRepresentation.class.cast(representation).setConfig(policy.getConfig());
+ } else {
+ representation = (R) providerFactory.toRepresentation(policy, representation);
+ }
return representation;
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 2557bfd..4391341 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -17,6 +17,18 @@
package org.keycloak.models.utils;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
import org.jboss.logging.Logger;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.AuthorizationProviderFactory;
@@ -24,7 +36,7 @@ import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
-import org.keycloak.authorization.policy.provider.PolicyProvider;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
@@ -92,6 +104,7 @@ import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
@@ -103,18 +116,6 @@ import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.util.JsonSerialization;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
public class RepresentationToModel {
private static Logger logger = Logger.getLogger(RepresentationToModel.class);
@@ -1896,9 +1897,6 @@ public class RepresentationToModel {
resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode());
resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement());
- StoreFactory storeFactory = authorization.getStoreFactory();
- ScopeStore scopeStore = storeFactory.getScopeStore();
-
rep.getScopes().forEach(scope -> {
toModel(scope, resourceServer, authorization);
});
@@ -1932,138 +1930,12 @@ public class RepresentationToModel {
private static Policy importPolicies(AuthorizationProvider authorization, ResourceServer resourceServer, List<PolicyRepresentation> policiesToImport, String parentPolicyName) {
StoreFactory storeFactory = authorization.getStoreFactory();
- KeycloakSession session = authorization.getKeycloakSession();
- RealmModel realm = authorization.getRealm();
for (PolicyRepresentation policyRepresentation : policiesToImport) {
if (parentPolicyName != null && !parentPolicyName.equals(policyRepresentation.getName())) {
continue;
}
Map<String, String> config = policyRepresentation.getConfig();
-
- String roles = config.get("roles");
-
- if (roles != null && !roles.isEmpty()) {
- try {
- List<Map> rolesMap = (List<Map>) JsonSerialization.readValue(roles, List.class);
- config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
- String roleName = roleConfig.get("id").toString();
- String clientId = null;
- int clientIdSeparator = roleName.indexOf("/");
-
- if (clientIdSeparator != -1) {
- clientId = roleName.substring(0, clientIdSeparator);
- roleName = roleName.substring(clientIdSeparator + 1);
- }
-
- RoleModel role;
-
- if (clientId == null) {
- role = realm.getRole(roleName);
- } else {
- role = realm.getClientByClientId(clientId).getRole(roleName);
- }
-
- // fallback to find any client role with the given name
- if (role == null) {
- String finalRoleName = roleName;
- role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
- .findFirst().orElse(null);
- }
-
- if (role == null) {
- role = realm.getRoleById(roleName);
-
- if (role == null) {
- String finalRoleName1 = roleName;
- role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName1)).filter(roleModel -> roleModel != null)
- .findFirst().orElse(null);
- }
- }
-
- if (role == null) {
- throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
- }
-
- roleConfig.put("id", role.getId());
- return roleConfig;
- }).collect(Collectors.toList())));
- } catch (Exception e) {
- throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
- }
- }
-
- String users = config.get("users");
-
- if (users != null && !users.isEmpty()) {
- try {
- List<String> usersMap = (List<String>) JsonSerialization.readValue(users, List.class);
- config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userId -> {
- UserModel user = session.users().getUserByUsername(userId, realm);
-
- if (user == null) {
- user = session.users().getUserById(userId, realm);
- }
-
- if (user == null) {
- throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found.");
- }
-
- return user.getId();
- }).collect(Collectors.toList())));
- } catch (Exception e) {
- throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
- }
- }
-
- String scopes = config.get("scopes");
-
- if (scopes != null && !scopes.isEmpty()) {
- try {
- ScopeStore scopeStore = storeFactory.getScopeStore();
- List<String> scopesMap = (List<String>) JsonSerialization.readValue(scopes, List.class);
- config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
- Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
-
- if (newScope == null) {
- newScope = scopeStore.findById(scopeName, resourceServer.getId());
- }
-
- if (newScope == null) {
- throw new RuntimeException("Scope with name [" + scopeName + "] not defined.");
- }
-
- return newScope.getId();
- }).collect(Collectors.toList())));
- } catch (Exception e) {
- throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
- }
- }
-
- String policyResources = config.get("resources");
-
- if (policyResources != null && !policyResources.isEmpty()) {
- ResourceStore resourceStore = storeFactory.getResourceStore();
- try {
- List<String> resources = JsonSerialization.readValue(policyResources, List.class);
- config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> {
- Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
-
- if (resource == null) {
- resource = resourceStore.findById(resourceName, resourceServer.getId());
- }
-
- if (resource == null) {
- throw new RuntimeException("Resource with name [" + resourceName + "] not defined.");
- }
-
- return resource.getId();
- }).collect(Collectors.toList())));
- } catch (Exception e) {
- throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
- }
- }
-
String applyPolicies = config.get("applyPolicies");
if (applyPolicies != null && !applyPolicies.isEmpty()) {
@@ -2087,91 +1959,111 @@ public class RepresentationToModel {
return policy.getId();
}).collect(Collectors.toList())));
} catch (Exception e) {
- throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
+ throw new RuntimeException("Error while importing policy [" + policyRepresentation.getName() + "].", e);
}
}
- if (parentPolicyName == null) {
- toModel(policyRepresentation, resourceServer, authorization);
- } else if (parentPolicyName.equals(policyRepresentation.getName())) {
- return toModel(policyRepresentation, resourceServer, authorization);
+ PolicyStore policyStore = storeFactory.getPolicyStore();
+ Policy policy = policyStore.findById(policyRepresentation.getId(), resourceServer.getId());
+
+ if (policy == null) {
+ policy = policyStore.findByName(policyRepresentation.getName(), resourceServer.getId());
+ }
+
+ if (policy == null) {
+ policy = policyStore.create(policyRepresentation, resourceServer);
+ } else {
+ policy = toModel(policyRepresentation, authorization, policy);
+ }
+
+ if (parentPolicyName != null && parentPolicyName.equals(policyRepresentation.getName())) {
+ return policy;
}
}
return null;
}
- public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorization) {
- String type = policy.getType();
- PolicyProvider provider = authorization.getProvider(type);
+ public static Policy toModel(AbstractPolicyRepresentation representation, AuthorizationProvider authorization, Policy model) {
+ model.setName(representation.getName());
+ model.setDescription(representation.getDescription());
+ model.setDecisionStrategy(representation.getDecisionStrategy());
+ model.setLogic(representation.getLogic());
- if (provider == null) {
- //TODO: temporary, remove this check on future versions as drools type is now deprecated
- if ("drools".equalsIgnoreCase(type)) {
- type = "rules";
- }
- if (authorization.getProvider(type) == null) {
- throw new RuntimeException("Unknown polucy type [" + type + "]. Could not find a provider for this type.");
+ Set resources = representation.getResources();
+ Set scopes = representation.getScopes();
+ Set policies = representation.getPolicies();
+
+ if (representation instanceof PolicyRepresentation) {
+ PolicyRepresentation policy = PolicyRepresentation.class.cast(representation);
+ String resourcesConfig = policy.getConfig().get("resources");
+
+ if (resourcesConfig != null) {
+ try {
+ resources = JsonSerialization.readValue(resourcesConfig, Set.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
- }
- PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
- Policy existing;
+ String scopesConfig = policy.getConfig().get("scopes");
- if (policy.getId() != null) {
- existing = policyStore.findById(policy.getId(), resourceServer.getId());
- } else {
- existing = policyStore.findByName(policy.getName(), resourceServer.getId());
- }
+ if (scopesConfig != null) {
+ try {
+ scopes = JsonSerialization.readValue(scopesConfig, Set.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
- if (existing != null) {
- existing.setName(policy.getName());
- existing.setDescription(policy.getDescription());
- existing.setConfig(policy.getConfig());
- existing.setDecisionStrategy(policy.getDecisionStrategy());
- existing.setLogic(policy.getLogic());
+ String policiesConfig = policy.getConfig().get("applyPolicies");
- updateResources(existing, authorization);
- updateAssociatedPolicies(existing, resourceServer, authorization);
- updateScopes(existing, authorization);
+ if (policiesConfig != null) {
+ try {
+ policies = JsonSerialization.readValue(policiesConfig, Set.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
- return existing;
+ model.setConfig(policy.getConfig());
}
- Policy model = policyStore.create(policy.getName(), type, resourceServer);
+ StoreFactory storeFactory = authorization.getStoreFactory();
+
+ updateResources(resources, model, storeFactory);
+ updateScopes(scopes, model, storeFactory);
+ updateAssociatedPolicies(policies, model, storeFactory);
+
+ PolicyProviderFactory provider = authorization.getProviderFactory(model.getType());
- model.setDescription(policy.getDescription());
- model.setDecisionStrategy(policy.getDecisionStrategy());
- model.setLogic(policy.getLogic());
- model.setConfig(policy.getConfig());
+ if (representation instanceof PolicyRepresentation) {
+ provider.onImport(model, PolicyRepresentation.class.cast(representation), authorization);
+ } else if (representation.getId() == null) {
+ provider.onCreate(model, representation, authorization);
+ } else {
+ provider.onUpdate(model, representation, authorization);
+ }
- updateResources(model, authorization);
- updateAssociatedPolicies(model, resourceServer, authorization);
- updateScopes(model, authorization);
- policy.setId(model.getId());
+ representation.setId(model.getId());
return model;
}
- private static void updateScopes(Policy policy, AuthorizationProvider authorization) {
- String scopes = policy.getConfig().get("scopes");
- if (scopes != null) {
- String[] scopeIds;
-
- try {
- scopeIds = JsonSerialization.readValue(scopes, String[].class);
- } catch (IOException e) {
- throw new RuntimeException(e);
+ private static void updateScopes(Set<String> scopeIds, Policy policy, StoreFactory storeFactory) {
+ if (scopeIds != null) {
+ if (scopeIds.isEmpty()) {
+ for (Scope scope : new HashSet<Scope>(policy.getScopes())) {
+ policy.removeScope(scope);
+ }
+ return;
}
-
- StoreFactory storeFactory = authorization.getStoreFactory();
-
for (String scopeId : scopeIds) {
boolean hasScope = false;
for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
- if (scopeModel.getId().equals(scopeId)) {
+ if (scopeModel.getId().equals(scopeId) || scopeModel.getName().equals(scopeId)) {
hasScope = true;
}
}
@@ -2180,7 +2072,10 @@ public class RepresentationToModel {
Scope scope = storeFactory.getScopeStore().findById(scopeId, resourceServer.getId());
if (scope == null) {
- storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId());
+ scope = storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId());
+ if (scope == null) {
+ throw new RuntimeException("Scope with id or name [" + scopeId + "] does not exist");
+ }
}
policy.addScope(scope);
@@ -2191,7 +2086,7 @@ public class RepresentationToModel {
boolean hasScope = false;
for (String scopeId : scopeIds) {
- if (scopeModel.getId().equals(scopeId)) {
+ if (scopeModel.getId().equals(scopeId) || scopeModel.getName().equals(scopeId)) {
hasScope = true;
}
}
@@ -2199,24 +2094,22 @@ public class RepresentationToModel {
policy.removeScope(scopeModel);
}
}
-
- policy.getConfig().remove("scopes");
}
- }
- private static void updateAssociatedPolicies(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization) {
- String policies = policy.getConfig().get("applyPolicies");
+ policy.getConfig().remove("scopes");
+ }
- if (policies != null) {
- String[] policyIds;
+ private static void updateAssociatedPolicies(Set<String> policyIds, Policy policy, StoreFactory storeFactory) {
+ ResourceServer resourceServer = policy.getResourceServer();
- try {
- policyIds = JsonSerialization.readValue(policies, String[].class);
- } catch (IOException e) {
- throw new RuntimeException(e);
+ if (policyIds != null) {
+ if (policyIds.isEmpty()) {
+ for (Policy associated: new HashSet<Policy>(policy.getAssociatedPolicies())) {
+ policy.removeAssociatedPolicy(associated);
+ }
+ return;
}
- StoreFactory storeFactory = authorization.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
for (String policyId : policyIds) {
@@ -2228,12 +2121,14 @@ public class RepresentationToModel {
}
}
-
if (!hasPolicy) {
Policy associatedPolicy = policyStore.findById(policyId, resourceServer.getId());
if (associatedPolicy == null) {
associatedPolicy = policyStore.findByName(policyId, resourceServer.getId());
+ if (associatedPolicy == null) {
+ throw new RuntimeException("Policy with id or name [" + policyId + "] does not exist");
+ }
}
policy.addAssociatedPolicy(associatedPolicy);
@@ -2250,31 +2145,24 @@ public class RepresentationToModel {
}
if (!hasPolicy) {
policy.removeAssociatedPolicy(policyModel);
- ;
}
}
-
- policy.getConfig().remove("applyPolicies");
}
- }
- private static void updateResources(Policy policy, AuthorizationProvider authorization) {
- String resources = policy.getConfig().get("resources");
- if (resources != null) {
- String[] resourceIds;
+ policy.getConfig().remove("applyPolicies");
+ }
- try {
- resourceIds = JsonSerialization.readValue(resources, String[].class);
- } catch (IOException e) {
- throw new RuntimeException(e);
+ private static void updateResources(Set<String> resourceIds, Policy policy, StoreFactory storeFactory) {
+ if (resourceIds != null) {
+ if (resourceIds.isEmpty()) {
+ for (Scope scope : new HashSet<Scope>(policy.getScopes())) {
+ policy.removeScope(scope);
+ }
}
-
- StoreFactory storeFactory = authorization.getStoreFactory();
-
for (String resourceId : resourceIds) {
boolean hasResource = false;
for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
- if (resourceModel.getId().equals(resourceId)) {
+ if (resourceModel.getId().equals(resourceId) || resourceModel.getName().equals(resourceId)) {
hasResource = true;
}
}
@@ -2282,7 +2170,10 @@ public class RepresentationToModel {
Resource resource = storeFactory.getResourceStore().findById(resourceId, policy.getResourceServer().getId());
if (resource == null) {
- throw new RuntimeException("Resource [" + resourceId + "] not found.");
+ resource = storeFactory.getResourceStore().findByName(resourceId, policy.getResourceServer().getId());
+ if (resource == null) {
+ throw new RuntimeException("Resource with id or name [" + resourceId + "] does not exist");
+ }
}
policy.addResource(resource);
@@ -2293,7 +2184,7 @@ public class RepresentationToModel {
boolean hasResource = false;
for (String resourceId : resourceIds) {
- if (resourceModel.getId().equals(resourceId)) {
+ if (resourceModel.getId().equals(resourceId) || resourceModel.getName().equals(resourceId)) {
hasResource = true;
}
}
@@ -2302,9 +2193,9 @@ public class RepresentationToModel {
policy.removeResource(resourceModel);
}
}
-
- policy.getConfig().remove("resources");
}
+
+ policy.getConfig().remove("resources");
}
public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
new file mode 100644
index 0000000..4ada87d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.admin;
+
+import java.util.List;
+import java.util.Map;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionService extends PolicyService {
+
+ public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+ super(resourceServer, authorization, auth);
+ }
+
+ @Override
+ protected PolicyResourceService doCreatePolicyResource(Policy policy) {
+ return new PolicyTypeResourceService(policy, resourceServer, authorization, auth);
+ }
+
+ @Override
+ protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
+ filters.put("permission", new String[] {Boolean.TRUE.toString()});
+ return super.doSearch(firstResult, maxResult, filters);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
new file mode 100644
index 0000000..85e0943
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.admin;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.services.resources.admin.RealmAuth;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyResourceService {
+
+ private final Policy policy;
+ protected final ResourceServer resourceServer;
+ protected final AuthorizationProvider authorization;
+ protected final RealmAuth auth;
+
+ public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+ this.policy = policy;
+ this.resourceServer = resourceServer;
+ this.authorization = authorization;
+ this.auth = auth;
+ }
+
+ @PUT
+ @Consumes("application/json")
+ @Produces("application/json")
+ @NoCache
+ public Response update(String payload) {
+ this.auth.requireManage();
+
+ AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ representation.setId(policy.getId());
+
+ RepresentationToModel.toModel(representation, authorization, policy);
+
+ return Response.status(Status.CREATED).build();
+ }
+
+ @DELETE
+ public Response delete() {
+ this.auth.requireManage();
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ StoreFactory storeFactory = authorization.getStoreFactory();
+ PolicyStore policyStore = storeFactory.getPolicyStore();
+ PolicyProviderFactory resource = getProviderFactory(policy.getType());
+
+ resource.onRemove(policy, authorization);
+
+ policyStore.delete(policy.getId());
+
+ return Response.noContent().build();
+ }
+
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response findById() {
+ this.auth.requireView();
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(toRepresentation(policy, authorization)).build();
+ }
+
+ protected AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
+ return ModelToRepresentation.toRepresentation(policy, PolicyRepresentation.class, authorization);
+ }
+
+ @Path("/dependentPolicies")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getDependentPolicies() {
+ this.auth.requireView();
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(policy.getId(), resourceServer.getId());
+
+ return Response.ok(policies.stream().map(policy -> {
+ PolicyRepresentation representation1 = new PolicyRepresentation();
+
+ representation1.setId(policy.getId());
+ representation1.setName(policy.getName());
+ representation1.setType(policy.getType());
+
+ return representation1;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("/scopes")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getScopes() {
+ this.auth.requireView();
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(policy.getScopes().stream().map(scope -> {
+ ScopeRepresentation representation = new ScopeRepresentation();
+
+ representation.setId(scope.getId());
+ representation.setName(scope.getName());
+
+ return representation;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("/resources")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getResources() {
+ this.auth.requireView();
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(policy.getResources().stream().map(resource -> {
+ ResourceRepresentation representation = new ResourceRepresentation();
+
+ representation.setId(resource.getId());
+ representation.setName(resource.getName());
+
+ return representation;
+ }).collect(Collectors.toList())).build();
+ }
+
+ @Path("/associatedPolicies")
+ @GET
+ @Produces("application/json")
+ @NoCache
+ public Response getAssociatedPolicies() {
+ this.auth.requireView();
+
+ if (policy == null) {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ return Response.ok(policy.getAssociatedPolicies().stream().map(policy -> {
+ PolicyRepresentation representation1 = new PolicyRepresentation();
+
+ representation1.setId(policy.getId());
+ representation1.setName(policy.getName());
+ representation1.setType(policy.getType());
+
+ return representation1;
+ }).collect(Collectors.toList())).build();
+ }
+
+ protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
+ PolicyRepresentation representation;
+
+ try {
+ representation = JsonSerialization.readValue(payload, PolicyRepresentation.class);
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize representation", cause);
+ }
+
+ return representation;
+ }
+
+ private PolicyProviderFactory getProviderFactory(String policyType) {
+ return authorization.getProviderFactory(policyType);
+ }
+
+ protected Policy getPolicy() {
+ return policy;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index 8982a29..011aa2d 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -17,9 +17,7 @@
*/
package org.keycloak.authorization.admin;
-import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
-import static org.keycloak.models.utils.RepresentationToModel.toModel;
-
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -29,14 +27,13 @@ import java.util.Map;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@@ -48,24 +45,24 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
-import org.keycloak.authorization.store.ResourceStore;
-import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.Constants;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
-import org.keycloak.representations.idm.authorization.ResourceRepresentation;
-import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.admin.RealmAuth;
+import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyService {
- private final ResourceServer resourceServer;
- private final AuthorizationProvider authorization;
- private final RealmAuth auth;
+ protected final ResourceServer resourceServer;
+ protected final AuthorizationProvider authorization;
+ protected final RealmAuth auth;
public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
this.resourceServer = resourceServer;
@@ -73,210 +70,66 @@ public class PolicyService {
this.auth = auth;
}
- @POST
- @Consumes("application/json")
- @Produces("application/json")
- @NoCache
- public Response create(PolicyRepresentation representation) {
- this.auth.requireManage();
- Policy policy = toModel(representation, this.resourceServer, authorization);
- PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
-
- if (resource != null) {
- try {
- resource.onCreate(policy);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- representation.setId(policy.getId());
-
- return Response.status(Status.CREATED).entity(representation).build();
- }
-
- @Path("{id}")
- @PUT
- @Consumes("application/json")
- @Produces("application/json")
- @NoCache
- public Response update(@PathParam("id") String id, PolicyRepresentation representation) {
- this.auth.requireManage();
- representation.setId(id);
- StoreFactory storeFactory = authorization.getStoreFactory();
- Policy policy = storeFactory.getPolicyStore().findById(representation.getId(), resourceServer.getId());
-
- if (policy == null) {
- return Response.status(Status.NOT_FOUND).build();
- }
-
- policy = toModel(representation, resourceServer, authorization);
-
- PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
-
- if (resource != null) {
- try {
- resource.onUpdate(policy);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- return Response.status(Status.CREATED).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response delete(@PathParam("id") String id) {
- this.auth.requireManage();
- StoreFactory storeFactory = authorization.getStoreFactory();
- PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.findById(id, resourceServer.getId());
-
- if (policy == null) {
- return Response.status(Status.NOT_FOUND).build();
- }
-
- PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
+ @Path("{type}")
+ public Object getResource(@PathParam("type") String type) {
+ PolicyProviderFactory providerFactory = getPolicyProviderFactory(type);
- if (resource != null) {
- try {
- resource.onRemove(policy);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ if (providerFactory != null) {
+ return new PolicyTypeService(type, resourceServer, authorization, auth);
}
- policyStore.findDependentPolicies(id, resourceServer.getId()).forEach(dependentPolicy -> {
- if (dependentPolicy.getAssociatedPolicies().size() == 1) {
- policyStore.delete(dependentPolicy.getId());
- } else {
- dependentPolicy.removeAssociatedPolicy(policy);
- }
- });
+ Policy policy = authorization.getStoreFactory().getPolicyStore().findById(type, resourceServer.getId());
- policyStore.delete(policy.getId());
-
- return Response.noContent().build();
+ return doCreatePolicyResource(policy);
}
- @Path("{id}")
- @GET
- @Produces("application/json")
- @NoCache
- public Response findById(@PathParam("id") String id) {
- this.auth.requireView();
- StoreFactory storeFactory = authorization.getStoreFactory();
- Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
-
- if (model == null) {
- return Response.status(Status.NOT_FOUND).build();
- }
-
- return Response.ok(toRepresentation(model)).build();
+ protected Object doCreatePolicyResource(Policy policy) {
+ return new PolicyResourceService(policy, resourceServer, authorization, auth);
}
- @Path("{id}/dependentPolicies")
- @GET
- @Produces("application/json")
- @NoCache
- public Response getDependentPolicies(@PathParam("id") String id) {
- this.auth.requireView();
- StoreFactory storeFactory = authorization.getStoreFactory();
- Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
-
- if (model == null) {
- return Response.status(Status.NOT_FOUND).build();
- }
-
- List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId(), resourceServer.getId());
-
- return Response.ok(policies.stream().map(policy -> {
- PolicyRepresentation representation1 = new PolicyRepresentation();
-
- representation1.setId(policy.getId());
- representation1.setName(policy.getName());
- representation1.setType(policy.getType());
-
- return representation1;
- }).collect(Collectors.toList())).build();
- }
-
- @Path("{id}/scopes")
- @GET
- @Produces("application/json")
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
@NoCache
- public Response getScopes(@PathParam("id") String id) {
- this.auth.requireView();
- StoreFactory storeFactory = authorization.getStoreFactory();
- Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
-
- if (model == null) {
- return Response.status(Status.NOT_FOUND).build();
- }
+ public Response create(String payload) {
+ this.auth.requireManage();
- return Response.ok(model.getScopes().stream().map(scope -> {
- ScopeRepresentation representation = new ScopeRepresentation();
+ AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
+ Policy policy = create(representation);
- representation.setId(scope.getId());
- representation.setName(scope.getName());
+ representation.setId(policy.getId());
- return representation;
- }).collect(Collectors.toList())).build();
+ return Response.status(Status.CREATED).entity(representation).build();
}
- @Path("{id}/resources")
- @GET
- @Produces("application/json")
- @NoCache
- public Response getResources(@PathParam("id") String id) {
- this.auth.requireView();
- StoreFactory storeFactory = authorization.getStoreFactory();
- Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
+ protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
+ PolicyRepresentation representation;
- if (model == null) {
- return Response.status(Status.NOT_FOUND).build();
+ try {
+ representation = JsonSerialization.readValue(payload, PolicyRepresentation.class);
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to deserialize representation", cause);
}
- return Response.ok(model.getResources().stream().map(resource -> {
- ResourceRepresentation representation = new ResourceRepresentation();
-
- representation.setId(resource.getId());
- representation.setName(resource.getName());
-
- return representation;
- }).collect(Collectors.toList())).build();
+ return representation;
}
- @Path("{id}/associatedPolicies")
- @GET
- @Produces("application/json")
- @NoCache
- public Response getAssociatedPolicies(@PathParam("id") String id) {
- this.auth.requireView();
- StoreFactory storeFactory = authorization.getStoreFactory();
- Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
+ public Policy create(AbstractPolicyRepresentation representation) {
+ PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+ Policy existing = policyStore.findByName(representation.getName(), resourceServer.getId());
- if (model == null) {
- return Response.status(Status.NOT_FOUND).build();
+ if (existing != null) {
+ throw new ErrorResponseException("Policy with name [" + representation.getName() + "] already exists", "Conflicting policy", Status.CONFLICT);
}
- return Response.ok(model.getAssociatedPolicies().stream().map(policy -> {
- PolicyRepresentation representation1 = new PolicyRepresentation();
-
- representation1.setId(policy.getId());
- representation1.setName(policy.getName());
- representation1.setType(policy.getType());
-
- return representation1;
- }).collect(Collectors.toList())).build();
+ return policyStore.create(representation, resourceServer);
}
@Path("/search")
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
@NoCache
- public Response find(@QueryParam("name") String name) {
+ public Response findByName(@QueryParam("name") String name) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
@@ -290,11 +143,11 @@ public class PolicyService {
return Response.status(Status.OK).build();
}
- return Response.ok(toRepresentation(model)).build();
+ return Response.ok(toRepresentation(model, authorization)).build();
}
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response findAll(@QueryParam("policyId") String id,
@QueryParam("name") String name,
@@ -363,15 +216,24 @@ public class PolicyService {
}
return Response.ok(
- policyStore.findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
- .map(policy -> toRepresentation(policy))
- .collect(Collectors.toList()))
+ doSearch(firstResult, maxResult, search))
.build();
}
+ protected AbstractPolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) {
+ return ModelToRepresentation.toRepresentation(model, PolicyRepresentation.class, authorization);
+ }
+
+ protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
+ PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+ return policyStore.findByResourceServer(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
+ .map(policy -> toRepresentation(policy, authorization))
+ .collect(Collectors.toList());
+ }
+
@Path("providers")
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response findPolicyProviders() {
this.auth.requireView();
@@ -400,20 +262,12 @@ public class PolicyService {
return resource;
}
- @Path("{policyType}")
- public Object getPolicyTypeResource(@PathParam("policyType") String policyType) {
- this.auth.requireView();
- return getPolicyProviderAdminResource(policyType, this.authorization);
+ protected PolicyProviderAdminService getPolicyProviderAdminResource(String policyType) {
+ return getPolicyProviderFactory(policyType).getAdminResource(resourceServer, authorization);
}
- private PolicyProviderAdminService getPolicyProviderAdminResource(String policyType, AuthorizationProvider authorization) {
- PolicyProviderFactory providerFactory = authorization.getProviderFactory(policyType);
-
- if (providerFactory != null) {
- return providerFactory.getAdminResource(this.resourceServer);
- }
-
- return null;
+ protected PolicyProviderFactory getPolicyProviderFactory(String policyType) {
+ return authorization.getProviderFactory(policyType);
}
private void findAssociatedPolicies(Policy policy, List<Policy> policies) {
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java
new file mode 100644
index 0000000..8756721
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.admin;
+
+import java.io.IOException;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.services.resources.admin.RealmAuth;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyTypeResourceService extends PolicyResourceService {
+
+ public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+ super(policy, resourceServer, authorization, auth);
+ }
+
+ @Override
+ protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
+ String type = getPolicy().getType();
+ Class<? extends AbstractPolicyRepresentation> representationType = authorization.getProviderFactory(type).getRepresentationType();
+
+ if (representationType == null) {
+ throw new RuntimeException("Policy provider for type [" + type + "] returned a null representation type.");
+ }
+
+ AbstractPolicyRepresentation representation;
+
+ try {
+ representation = JsonSerialization.readValue(payload, representationType);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to deserialize JSON using policy provider for type [" + type + "].", e);
+ }
+
+ representation.setType(type);
+
+ return representation;
+ }
+
+ @Override
+ protected AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
+ PolicyProviderFactory providerFactory = authorization.getProviderFactory(policy.getType());
+ return ModelToRepresentation.toRepresentation(policy, providerFactory.getRepresentationType(), authorization);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
new file mode 100644
index 0000000..c2e4db5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.admin;
+
+import java.io.IOException;
+
+import javax.ws.rs.Path;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
+import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.services.resources.admin.RealmAuth;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyTypeService extends PolicyService {
+
+ private final String type;
+
+ PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+ super(resourceServer, authorization, auth);
+ this.type = type;
+ }
+
+ @Path("/provider")
+ public Object getPolicyAdminResourceProvider() {
+ PolicyProviderAdminService resource = getPolicyProviderAdminResource(type);
+
+ if (resource == null) {
+ return null;
+ }
+
+ ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+ return resource;
+ }
+
+ @Override
+ protected Object doCreatePolicyResource(Policy policy) {
+ return new PolicyTypeResourceService(policy, resourceServer,authorization, auth);
+ }
+
+ @Override
+ protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
+ PolicyProviderFactory provider = getPolicyProviderFactory(type);
+ Class<? extends AbstractPolicyRepresentation> representationType = provider.getRepresentationType();
+
+ if (representationType == null) {
+ throw new RuntimeException("Policy provider for type [" + type + "] returned a null representation type.");
+ }
+
+ AbstractPolicyRepresentation representation;
+
+ try {
+ representation = JsonSerialization.readValue(payload, representationType);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to deserialize JSON using policy provider for type [" + type + "].", e);
+ }
+
+ representation.setType(type);
+
+ return representation;
+ }
+
+ @Override
+ protected AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
+ PolicyProviderFactory providerFactory = authorization.getProviderFactory(policy.getType());
+ return ModelToRepresentation.toRepresentation(policy, providerFactory.getRepresentationType(), authorization);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
index 52d6a38..15f1db7 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
@@ -17,6 +17,22 @@
*/
package org.keycloak.authorization.admin;
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
@@ -34,25 +50,11 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-import java.io.IOException;
-import java.util.HashMap;
-
-import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@@ -171,22 +173,26 @@ public class ResourceServerService {
return resource;
}
+ @Path("/permission")
+ public Object getPermissionTypeResource() {
+ this.auth.requireView();
+ PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth);
+
+ ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+ return resource;
+ }
+
private void createDefaultPermission(ResourceRepresentation resource, PolicyRepresentation policy) {
- PolicyRepresentation defaultPermission = new PolicyRepresentation();
+ ResourcePermissionRepresentation defaultPermission = new ResourcePermissionRepresentation();
defaultPermission.setName("Default Permission");
- defaultPermission.setType("resource");
defaultPermission.setDescription("A permission that applies to the default resource type");
defaultPermission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
defaultPermission.setLogic(Logic.POSITIVE);
- HashMap<String, String> defaultPermissionConfig = new HashMap<>();
-
- defaultPermissionConfig.put("default", "true");
- defaultPermissionConfig.put("defaultResourceType", resource.getType());
- defaultPermissionConfig.put("applyPolicies", "[\"" + policy.getName() + "\"]");
-
- defaultPermission.setConfig(defaultPermissionConfig);
+ defaultPermission.setResourceType(resource.getType());
+ defaultPermission.addPolicy(policy.getName());
getPolicyResource().create(defaultPermission);
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index bb241d8..5d65923 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -58,7 +58,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
import java.util.stream.Collectors;
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index 7724830..1ec9a13 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -41,6 +41,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@@ -69,8 +70,8 @@ public class ScopeService {
}
@POST
- @Consumes("application/json")
- @Produces("application/json")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
public Response create(ScopeRepresentation scope) {
this.auth.requireManage();
Scope model = toModel(scope, this.resourceServer, authorization);
@@ -82,8 +83,8 @@ public class ScopeService {
@Path("{id}")
@PUT
- @Consumes("application/json")
- @Produces("application/json")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
public Response update(@PathParam("id") String id, ScopeRepresentation scope) {
this.auth.requireManage();
scope.setId(id);
@@ -134,7 +135,7 @@ public class ScopeService {
@Path("{id}")
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
public Response findById(@PathParam("id") String id) {
this.auth.requireView();
Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id, resourceServer.getId());
@@ -148,7 +149,7 @@ public class ScopeService {
@Path("{id}/resources")
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
public Response getResources(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = this.authorization.getStoreFactory();
@@ -170,7 +171,7 @@ public class ScopeService {
@Path("{id}/permissions")
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
public Response getPermissions(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = this.authorization.getStoreFactory();
@@ -195,7 +196,7 @@ public class ScopeService {
@Path("/search")
@GET
- @Produces("application/json")
+ @Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response find(@QueryParam("name") String name) {
this.auth.requireView();
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
index 7891e18..6b5b027 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -139,12 +139,19 @@ public class KeycloakIdentity implements Identity {
if (this.accessToken.getClientSession() != null) {
ClientSessionModel clientSession = this.keycloakSession.sessions().getClientSession(this.accessToken.getClientSession());
- clientUser = this.keycloakSession.users().getServiceAccount(clientSession.getClient());
- } else if (this.accessToken.getIssuedFor() != null) {
- ClientModel clientModel = this.keycloakSession.realms().getClientById(this.accessToken.getIssuedFor(), this.realm);
- clientUser = this.keycloakSession.users().getServiceAccount(clientModel);
+
+ if (clientSession != null) {
+ clientUser = this.keycloakSession.users().getServiceAccount(clientSession.getClient());
+ }
}
+ if (this.accessToken.getIssuedFor() != null) {
+ ClientModel clientModel = this.keycloakSession.realms().getClientById(this.accessToken.getIssuedFor(), this.realm);
+
+ if (clientModel != null) {
+ clientUser = this.keycloakSession.users().getServiceAccount(clientModel);
+ }
+ }
if (clientUser == null) {
return false;
diff --git a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
index 159e5aa..cc06284 100644
--- a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
+++ b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
@@ -21,7 +21,6 @@ package org.keycloak.authorization;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Executor;
import org.keycloak.Config;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@@ -38,7 +37,6 @@ import org.keycloak.provider.ProviderFactory;
*/
public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory {
- private Executor scheduler;
private Map<String, PolicyProviderFactory> policyProviderFactories;
@Override
@@ -48,15 +46,6 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
@Override
public void init(Config.Scope config) {
- //TODO: user-defined configuration
-// Executor executor = Executors.newWorkStealingPool();
-// this.scheduler = command -> {
-// Map<Class<?>, Object> contextDataMap = ResteasyProviderFactory.getContextDataMap();
-// executor.execute(() -> {
-// ResteasyProviderFactory.pushContextDataMap(contextDataMap);
-// command.run();
-// });
-// };
}
@Override
@@ -77,11 +66,9 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
@Override
public AuthorizationProvider create(KeycloakSession session, RealmModel realm) {
StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class);
-
if (storeFactory == null) {
storeFactory = session.getProvider(StoreFactory.class);
}
-
return new AuthorizationProvider(session, realm, storeFactory, policyProviderFactories);
}
diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
index 620916c..326deb6 100644
--- a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
+++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java
@@ -139,7 +139,7 @@ public class EntitlementService {
.allowedOrigins(identity.getAccessToken())
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else {
- asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+ asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements, identity.getAccessToken())))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
}
}
});
@@ -192,7 +192,7 @@ public class EntitlementService {
.allowedOrigins(identity.getAccessToken())
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else {
- asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
+ asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements, identity.getAccessToken())))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
}
}
});
@@ -207,8 +207,7 @@ public class EntitlementService {
}
}
- private String createRequestingPartyToken(List<Permission> permissions) {
- AccessToken accessToken = Tokens.getAccessToken(this.authorization.getKeycloakSession());
+ private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken) {
RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
AccessToken.Authorization authorization = new AccessToken.Authorization();
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index 403551f..51ed405 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -337,7 +337,7 @@ public class ExportUtils {
RealmModel realm = authorizationProvider.getRealm();
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
try {
- PolicyRepresentation rep = toRepresentation(policy);
+ PolicyRepresentation rep = toRepresentation(policy, PolicyRepresentation.class, authorizationProvider);
rep.setId(null);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
index d1fc565..b4f8622 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
@@ -17,24 +17,22 @@
package org.keycloak.protocol.oidc.endpoints;
-import org.keycloak.Config;
-import org.keycloak.common.util.StreamUtil;
import org.keycloak.common.util.UriUtils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.protocol.oidc.utils.WebOriginsUtils;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.services.util.P3PHelper;
import org.keycloak.utils.MediaType;
-import javax.ws.rs.*;
-import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
@@ -59,10 +57,6 @@ public class LoginStatusIframeEndpoint {
@Produces(MediaType.TEXT_HTML_UTF_8)
public Response getLoginStatusIframe(@QueryParam("client_id") String client_id,
@QueryParam("origin") String origin) {
- if (client_id != null && origin != null) {
- return getLoginStatusIframeDeprecated(client_id, origin);
- }
-
InputStream resource = getClass().getClassLoader().getResourceAsStream("login-status-iframe.html");
if (resource != null) {
P3PHelper.addP3PHeader(session);
@@ -90,60 +84,4 @@ public class LoginStatusIframeEndpoint {
return Response.status(Response.Status.FORBIDDEN).build();
}
- // Support for old keycloak.js
- private Response getLoginStatusIframeDeprecated(@QueryParam("client_id") String client_id,
- @QueryParam("origin") String origin) {
- if (!UriUtils.isOrigin(origin)) {
- throw new WebApplicationException(Response.Status.BAD_REQUEST);
- }
-
- ClientModel client = realm.getClientByClientId(client_id);
- if (client == null) {
- throw new WebApplicationException(Response.Status.BAD_REQUEST);
- }
-
- InputStream is = getClass().getClassLoader().getResourceAsStream("login-status-iframe-deprecated.html");
- if (is == null) throw new org.jboss.resteasy.spi.NotFoundException("Could not find login-status-iframe-deprecated.html ");
-
- boolean valid = false;
- for (String o : client.getWebOrigins()) {
- if (o.equals("*") || o.equals(origin)) {
- valid = true;
- break;
- }
- }
-
- for (String r : RedirectUtils.resolveValidRedirects(uriInfo, client.getRootUrl(), client.getRedirectUris())) {
- int i = r.indexOf('/', 8);
- if (i != -1) {
- r = r.substring(0, i);
- }
-
- if (r.equals(origin)) {
- valid = true;
- break;
- }
- }
-
- if (!valid) {
- throw new WebApplicationException(Response.Status.BAD_REQUEST);
- }
-
- try {
- String file = StreamUtil.readString(is);
- file = file.replace("ORIGIN", origin);
-
- P3PHelper.addP3PHeader(session);
-
- CacheControl cacheControl = new CacheControl();
- cacheControl.setNoTransform(false);
- cacheControl.setMaxAge(Config.scope("theme").getInt("staticMaxAge", -1));
-
- return Response.ok(file).cacheControl(cacheControl).build();
- } catch (IOException e) {
- throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
- }
- }
-
-
}
diff --git a/services/src/main/java/org/keycloak/scripting/DefaultScriptingProvider.java b/services/src/main/java/org/keycloak/scripting/DefaultScriptingProvider.java
index 5772f53..601da8e 100644
--- a/services/src/main/java/org/keycloak/scripting/DefaultScriptingProvider.java
+++ b/services/src/main/java/org/keycloak/scripting/DefaultScriptingProvider.java
@@ -110,7 +110,13 @@ public class DefaultScriptingProvider implements ScriptingProvider {
* Looks-up a {@link ScriptEngine} based on the MIME-type provided by the given {@link Script}.
*/
private ScriptEngine lookupScriptEngineFor(ScriptModel script) {
- return scriptEngineManager.getEngineByMimeType(script.getMimeType());
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(DefaultScriptingProvider.class.getClassLoader());
+ return scriptEngineManager.getEngineByMimeType(script.getMimeType());
+ } finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
}
@Override
diff --git a/services/src/main/java/org/keycloak/scripting/DefaultScriptingProviderFactory.java b/services/src/main/java/org/keycloak/scripting/DefaultScriptingProviderFactory.java
index b00a058..4ed1efd 100644
--- a/services/src/main/java/org/keycloak/scripting/DefaultScriptingProviderFactory.java
+++ b/services/src/main/java/org/keycloak/scripting/DefaultScriptingProviderFactory.java
@@ -29,9 +29,13 @@ public class DefaultScriptingProviderFactory implements ScriptingProviderFactory
static final String ID = "script-based-auth";
+ private ScriptEngineManager scriptEngineManager;
+
@Override
public ScriptingProvider create(KeycloakSession session) {
- return new DefaultScriptingProvider(ScriptEngineManagerHolder.SCRIPT_ENGINE_MANAGER);
+ lazyInit();
+
+ return new DefaultScriptingProvider(scriptEngineManager);
}
@Override
@@ -54,11 +58,14 @@ public class DefaultScriptingProviderFactory implements ScriptingProviderFactory
return ID;
}
- /**
- * Holder class for lazy initialization of {@link ScriptEngineManager}.
- */
- private static class ScriptEngineManagerHolder {
-
- private static final ScriptEngineManager SCRIPT_ENGINE_MANAGER = new ScriptEngineManager();
+ private void lazyInit() {
+ if (scriptEngineManager == null) {
+ synchronized (this) {
+ if (scriptEngineManager == null) {
+ scriptEngineManager = new ScriptEngineManager();
+ }
+ }
+ }
}
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
index 2b796c5..645afaf 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java
@@ -296,6 +296,7 @@ public class AdminConsole {
authUrl = authUrl.substring(0, authUrl.length() - 1);
map.put("authUrl", authUrl);
+ map.put("consoleBaseUrl", Urls.adminConsoleRoot(baseUri, realm.getId()));
map.put("resourceUrl", Urls.themeRoot(baseUri) + "/admin/" + theme.getName());
map.put("masterRealm", Config.getAdminRealm());
map.put("resourceVersion", Version.RESOURCES_VERSION);
diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index a23a26a..059c84d 100755
--- a/services/src/main/java/org/keycloak/services/Urls.java
+++ b/services/src/main/java/org/keycloak/services/Urls.java
@@ -25,6 +25,7 @@ import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.ThemeResource;
+import org.keycloak.services.resources.admin.AdminRoot;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
@@ -34,6 +35,10 @@ import java.net.URI;
*/
public class Urls {
+ public static URI adminConsoleRoot(URI baseUri, String realmId) {
+ return UriBuilder.fromUri(baseUri).path(AdminRoot.class).path("{realm}/console/").build(realmId);
+ }
+
public static URI accountApplicationsPage(URI baseUri, String realmId) {
return accountBase(baseUri).path(AccountService.class, "applicationsPage").build(realmId);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
index 642e116..7be8fb4 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/AbstractPhotozAdminTest.java
@@ -38,7 +38,9 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.util.JsonSerialization;
@@ -246,10 +248,14 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
return onAuthorizationSession(authorizationProvider -> {
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.create("Administration Policy", "aggregate", resourceServer);
+ PolicyRepresentation representation = new PolicyRepresentation();
- policy.addAssociatedPolicy(anyAdminPolicy);
- policy.addAssociatedPolicy(onlyFromSpecificAddressPolicy);
+ representation.setName("Administration Policy");
+ representation.setType("aggregate");
+ representation.addPolicy(anyAdminPolicy.getName());
+ representation.addPolicy(onlyFromSpecificAddressPolicy.getName());
+
+ Policy policy = policyStore.create(representation, resourceServer);
return policy;
});
@@ -259,19 +265,22 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
return onAuthorizationSession(authorizationProvider -> {
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.create("Only From a Specific Client Address", "js", resourceServer);
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setName("Only From a Specific Client Address");
+ representation.setType("js");
HashedMap config = new HashedMap();
config.put("code",
"var contextAttributes = $evaluation.getContext().getAttributes();" +
- "var networkAddress = contextAttributes.getValue('kc.client.network.ip_address');" +
- "if ('127.0.0.1'.equals(networkAddress.asInetAddress(0).getHostAddress())) {" +
- "$evaluation.grant();" +
- "}");
+ "var networkAddress = contextAttributes.getValue('kc.client.network.ip_address');" +
+ "if ('127.0.0.1'.equals(networkAddress.asInetAddress(0).getHostAddress())) {" +
+ "$evaluation.grant();" +
+ "}");
- policy.setConfig(config);
+ representation.setConfig(config);
- return policy;
+ return policyStore.create(representation, resourceServer);
});
}
@@ -279,24 +288,13 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
return onAuthorizationSession(authorizationProvider -> {
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.create("Any Admin Policy", "role", resourceServer);
- HashedMap config = new HashedMap();
- RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
- RoleModel adminRole = realm.getRole("admin");
-
- Map role = new HashMap();
-
- role.put("id", adminRole.getId());
-
- try {
- config.put("roles", JsonSerialization.writeValueAsString(new Map[] {role}));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
- policy.setConfig(config);
+ representation.setName("Any Admin Policy");
+ representation.setType("role");
+ representation.addRole("admin", false);
- return policy;
+ return policyStore.create(representation, resourceServer);
});
}
@@ -358,7 +356,11 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
return onAuthorizationSession(authorizationProvider -> {
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.create("Any User Policy", "role", resourceServer);
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setName("Any User Policy");
+ representation.setType("role");
+
HashedMap config = new HashedMap();
RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
RoleModel userRole = realm.getRole("user");
@@ -373,7 +375,9 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
throw new RuntimeException(e);
}
- policy.setConfig(config);
+ representation.setConfig(config);
+
+ Policy policy = policyStore.create(representation, resourceServer);
return policy;
});
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
index 4708a2a..be17a80 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/authorization/ResourcePermissionManagementTest.java
@@ -46,12 +46,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -392,7 +389,10 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
return onAuthorizationSession(authorizationProvider -> {
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
- Policy policy = policyStore.create("Client-Based Policy", "client", resourceServer);
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setName("Client-Based Policy");
+ representation.setType("client");
List<String> clientIds = new ArrayList<>();
for (ClientModel client : allowedClients) {
@@ -408,9 +408,9 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
throw new RuntimeException(e);
}
- policy.setConfig(config);
+ representation.setConfig(config);
- return policy;
+ return policyStore.create(representation, resourceServer);
});
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java
index 0982d5d..3f8e854 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java
@@ -432,7 +432,7 @@ public class LDAPProvidersIntegrationTest {
loginPage.open();
loginPage.clickRegister();
registerPage.assertCurrent();
-
+
// check existing username
registerPage.register("firstName", "lastName", "email@mail.cz", "existing", "Password1", "Password1");
registerPage.assertCurrent();
@@ -443,7 +443,44 @@ public class LDAPProvidersIntegrationTest {
registerPage.assertCurrent();
Assert.assertEquals("Email already exists.", registerPage.getError());
}
-
+
+
+
+ //
+ // KEYCLOAK-4533
+ //
+ @Test
+ public void testLDAPUserDeletionImport() {
+
+ KeycloakSession session = keycloakRule.startSession();
+ RealmModel appRealm = new RealmManager(session).getRealmByName("test");
+ LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
+ LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig();
+
+ // Make sure mary is gone
+ LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, appRealm, config, "maryjane");
+
+ // Create the user in LDAP and register him
+
+ LDAPObject mary = LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "maryjane", "mary", "yram", "mj@testing.redhat.cz", null, "12398");
+ LDAPTestUtils.updateLDAPPassword(ldapProvider, mary, "Password1");
+
+ try {
+
+ // Log in and out of the user
+ loginSuccessAndLogout("maryjane", "Password1");
+
+ // Delete LDAP User
+ LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, appRealm, config, "maryjane");
+
+ // Make sure the deletion took place.
+ List<UserModel> deletedUsers = session.users().searchForUser("mary yram", appRealm);
+ Assert.assertTrue(deletedUsers.isEmpty());
+
+ } finally {
+ keycloakRule.stopSession(session, false);
+ }
+ }
@Test
public void registerUserLdapSuccess() {
loginPage.open();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java
index f41a7ba..6a90636 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPTestUtils.java
@@ -32,6 +32,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.SynchronizationResultRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.ldap.LDAPStorageProvider;
+import org.keycloak.storage.ldap.LDAPConfig;
import org.keycloak.storage.ldap.LDAPUtils;
import org.keycloak.storage.ldap.idm.model.LDAPObject;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
@@ -263,7 +264,20 @@ public class LDAPTestUtils {
ldapStore.remove(ldapUser);
}
}
-
+
+ public static void removeLDAPUserByUsername(LDAPStorageProvider ldapProvider, RealmModel realm, LDAPConfig config, String username) {
+ LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore();
+ LDAPQuery ldapQuery = LDAPUtils.createQueryForUserSearch(ldapProvider, realm);
+ List<LDAPObject> allUsers = ldapQuery.getResultList();
+
+ // This is ugly, we are iterating over the entire set of ldap users and deleting the one where the username matches. TODO: Find a better way!
+ for (LDAPObject ldapUser : allUsers) {
+ if (username.equals(LDAPUtils.getUsername(ldapUser, config))) {
+ ldapStore.remove(ldapUser);
+ }
+ }
+ }
+
public static void removeAllLDAPRoles(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, String mapperName) {
ComponentModel mapperModel = getSubcomponentByName(appRealm, ldapModel, mapperName);
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java
index 5aa1c94..a2841d8 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java
@@ -211,6 +211,45 @@ public class LDAPProvidersIntegrationNoImportTest {
oauth.openLogout();
}
+
+ @Test
+ public void testLDAPUserImportOnCreationOrLogin() {
+
+ KeycloakSession session = keycloakRule.startSession();
+ RealmModel appRealm = new RealmManager(session).getRealmByName("test");
+ LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
+ LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig();
+
+ // Make sure mary is gone
+ LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, appRealm, config, "maryjane");
+
+
+ // Create the user in LDAP and register him
+ //
+ LDAPObject mary = LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "maryjane", "mary", "yram", "mj@testing.redhat.cz", null, "12398");
+ LDAPTestUtils.updateLDAPPassword(ldapProvider, mary, "Password1");
+
+ try {
+
+ // Log in and out of the user
+ loginSuccessAndLogout("maryjane", "Password1");
+
+ // Delete LDAP User
+ LDAPTestUtils.removeLDAPUserByUsername(ldapProvider, appRealm, config, "maryjane");
+
+ // Make sure the deletion took place.
+ List<UserModel> deletedUsers = session.users().searchForUser("mary yram", appRealm);
+ Assert.assertTrue(deletedUsers.isEmpty());
+
+ } finally {
+ keycloakRule.stopSession(session, false);
+ }
+ }
+
+
+
+
+
@Test
public void caseInsensitiveSearch() {
KeycloakSession session = keycloakRule.startSession();
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
index b3a0475..8e07ee2 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
@@ -47,7 +47,7 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
- public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
+ public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java
index 5f7520f..c894674 100644
--- a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java
+++ b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java
@@ -79,6 +79,7 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
di.addFilter(filter);
di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
+ filter.setAsyncSupported(true);
return di;
}
diff --git a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
index 0f1d3f2..65d58f1 100755
--- a/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
+++ b/testsuite/integration-arquillian/test-apps/js-console/src/main/webapp/index.html
@@ -43,12 +43,18 @@
<button onclick="createBearerRequest()">Create Bearer Request</button>
<button onclick="output(showTime())">Show current time</button>
<button onclick="cert()">Cert request</button>
- <input id="timeSkewInput"/>
<button onclick="addToTimeSkew()">timeSkew offset</button>
<button onclick="refreshTimeSkew()">refresh timeSkew</button>
<button onclick="sendBearerToKeycloak()">Bearer to keycloak</button>
<button onclick="createUser()">Create user</button>
<button onclick="reentrancyCallback()">Reentrancy callback</button>
+ <button onclick='initWithDifferentRealmName(getInput())'>Init with different realm name</button>
+ <button onclick='keycloakInit(null, {refreshToken: getInput2()})'>Init with refresh token</button>
+ <button onclick='keycloakInit(null, {token: getInput(), refreshToken: getInput2()})'>Init with both tokens</button>
+ <button onclick='keycloakInit(null, {token: getInput(), refreshToken: getInput2(), timeSkew: parseInt(getInput3())})'>Init with TimeSkew</button>
+ <input id="inputField" value="input"/>
+ <input id="inputField2" value="input2"/>
+ <input id="inputField3" value="input3"/>
<select id="flowSelect">
@@ -78,6 +84,15 @@ TimeSkew:
<script>
+ function getInput() {
+ return document.getElementById("inputField").value;
+ }
+ function getInput2() {
+ return document.getElementById("inputField2").value;
+ }
+ function getInput3() {
+ return document.getElementById("inputField3").value;
+ }
function loadProfile() {
keycloak.loadUserProfile().success(function (profile) {
output(profile);
@@ -150,8 +165,7 @@ TimeSkew:
}
function addToTimeSkew() {
- var offset = document.getElementById("timeSkewInput").value;
- keycloak.timeSkew += parseInt(offset);
+ keycloak.timeSkew += parseInt(getInput());
document.getElementById("timeSkew").innerHTML = keycloak.timeSkew;
}
@@ -281,10 +295,28 @@ TimeSkew:
);
}
+ function initWithDifferentRealmName(realmName) {
+ var url = 'http://localhost:8180/auth';
+ if (window.location.href.indexOf("8643") > -1) {
+ url = url.replace("8180","8543");
+ url = url.replace("http","https");
+ }
+
+ keycloakInit({
+ url: url,
+ realm: realmName,
+ clientId: 'js-console'
+ });
+
+ }
+
var keycloak;
- function keycloakInit() {
- keycloak = Keycloak();
+ function keycloakInit(configuration, initOptions) {
+ if (!configuration)
+ keycloak = Keycloak();
+ else
+ keycloak = Keycloak(configuration);
keycloak.onAuthSuccess = function () {
event('Auth Success');
@@ -311,11 +343,14 @@ TimeSkew:
event('Access token expired.');
};
- var initOptions = {
- onLoad: document.getElementById("onLoad").value,
- flow: document.getElementById("flowSelect").value,
- responseMode: document.getElementById("responseModeSelect").value
- };
+ if (!initOptions) {
+ var initOptions = {
+ onLoad: document.getElementById("onLoad").value,
+ flow: document.getElementById("flowSelect").value,
+ responseMode: document.getElementById("responseModeSelect").value
+ };
+ }
+
keycloak.init(initOptions).success(function (authenticated) {
output('Init Success (' + (authenticated ? 'Authenticated' : 'Not Authenticated') + ')');
}).error(function () {
diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml
index 53f9980..5cdca9e 100644
--- a/testsuite/integration-arquillian/test-apps/pom.xml
+++ b/testsuite/integration-arquillian/test-apps/pom.xml
@@ -21,6 +21,7 @@
<module>photoz</module>
<module>hello-world-authz-service</module>
<module>servlet-authz</module>
+ <module>servlet-policy-enforcer</module>
<module>servlets</module>
<module>app-profile-jee</module>
<module>cors</module>
diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak.-permissive-authz-service.json b/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak.-permissive-authz-service.json
new file mode 100644
index 0000000..89c92f2
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/keycloak.-permissive-authz-service.json
@@ -0,0 +1,15 @@
+{
+ "realm": "servlet-authz",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "resource" : "servlet-authz-app",
+ "public-client" : false,
+ "credentials": {
+ "secret": "secret"
+ },
+ "policy-enforcer": {
+ "enforcement-mode" : "PERMISSIVE",
+ "on-deny-redirect-to" : "/servlet-authz-app/accessDenied.jsp"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml
new file mode 100755
index 0000000..d6eb833
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+
+<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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-test-apps</artifactId>
+ <version>3.1.0.CR1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>servlet-policy-enforcer</artifactId>
+ <packaging>war</packaging>
+
+ <name>Keycloak Authz: Simple Servlet App with Policy Enforcer</name>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jboss.as.plugins</groupId>
+ <artifactId>jboss-as-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.wildfly.plugins</groupId>
+ <artifactId>wildfly-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
new file mode 100644
index 0000000..cef6f00
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
@@ -0,0 +1,265 @@
+{
+ "realm": "servlet-policy-enforcer-authz",
+ "enabled": true,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [
+ "password"
+ ],
+ "users": [
+ {
+ "username": "alice",
+ "enabled": true,
+ "credentials": [
+ {
+ "type": "password",
+ "value": "alice"
+ }
+ ],
+ "realmRoles": [
+ "uma_authorization"
+ ]
+ },
+ {
+ "username": "jdoe",
+ "enabled": true,
+ "credentials": [
+ {
+ "type": "password",
+ "value": "jdoe"
+ }
+ ],
+ "realmRoles": [
+ "uma_authorization"
+ ]
+ },
+ {
+ "username": "service-account-servlet-policy-enforcer",
+ "enabled": true,
+ "serviceAccountClientId": "servlet-policy-enforcer",
+ "clientRoles": {
+ "servlet-policy-enforcer": [
+ "uma_protection"
+ ]
+ }
+ }
+ ],
+ "clients": [
+ {
+ "clientId": "servlet-policy-enforcer",
+ "secret": "secret",
+ "authorizationServicesEnabled": true,
+ "enabled": true,
+ "redirectUris": [
+ "/servlet-policy-enforcer/*"
+ ],
+ "baseUrl": "/servlet-policy-enforcer",
+ "adminUrl": "/servlet-policy-enforcer",
+ "directAccessGrantsEnabled": true,
+ "authorizationSettings": {
+ "allowRemoteResourceManagement": false,
+ "policyEnforcementMode": "ENFORCING",
+ "resources": [
+ {
+ "name": "Welcome Resource",
+ "uri": "",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 1",
+ "uri": "",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 2",
+ "uri": "/resource/resource-a",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 3",
+ "uri": "/resource/resource-b/test",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 4",
+ "uri": "/resource-c",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 5",
+ "uri": "/resource/d/resource-d",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 6",
+ "uri": "",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 7",
+ "uri": "",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 8",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 9",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 10",
+ "typedScopes": []
+ },
+ {
+ "name": "Pattern 11",
+ "typedScopes": []
+ }
+ ],
+ "policies": [
+ {
+ "name": "Default Policy",
+ "type": "js",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n",
+ "applyPolicies": "[]"
+ }
+ },
+ {
+ "name": "Deny Policy",
+ "type": "js",
+ "logic": "NEGATIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "code": "$evaluation.grant();"
+ }
+ },
+ {
+ "name": "Pattern 3 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "resources": "[\"Pattern 3\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 2 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "resources": "[\"Pattern 2\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 4 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 4\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 5 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 5\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 7 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "resources": "[\"Pattern 7\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 8 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 8\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 9 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 9\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 6 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "resources": "[\"Pattern 6\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Welcome Resource Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Welcome Resource\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 1 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 1\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 10 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 10\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ },
+ {
+ "name": "Pattern 11 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "resources": "[\"Pattern 11\"]",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
+ }
+ ],
+ "scopes": []
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp
new file mode 100644
index 0000000..ef8b4a6
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/denied.jsp
@@ -0,0 +1,2 @@
+<%@include file="logout-include.jsp"%>
+<p>You can not access this resource</p>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp
new file mode 100644
index 0000000..cf0e035
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/index.jsp
@@ -0,0 +1,2 @@
+<%@include file="logout-include.jsp"%>
+<p>Welcome</p>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp
new file mode 100644
index 0000000..3c55fd9
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/logout-include.jsp
@@ -0,0 +1,11 @@
+<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %>
+<%@ page import="org.keycloak.constants.ServiceUrlConstants" %>
+<%
+ String scheme = request.getScheme();
+ String host = request.getServerName();
+ int port = request.getServerPort();
+ String contextPath = request.getContextPath();
+ String redirectUri = scheme + "://" + host + ":" + port + contextPath;
+%>
+<h2>Click here <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8180/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+ .queryParam("redirect_uri", redirectUri).build("servlet-policy-enforcer-authz").toString()%>">Sign Out</a></h2>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
new file mode 100644
index 0000000..d8742d3
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
@@ -0,0 +1,62 @@
+{
+ "realm": "servlet-policy-enforcer-authz",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required": "external",
+ "resource": "servlet-policy-enforcer",
+ "credentials": {
+ "secret": "secret"
+ },
+ "policy-enforcer": {
+ "on-deny-redirect-to": "/servlet-policy-enforcer/denied.jsp",
+ "paths": [
+ {
+ "name": "Welcome Resource",
+ "path": "/index.jsp"
+ },
+ {
+ "name": "Pattern 1",
+ "path": "/resource/{pattern}/{sub-resource}"
+ },
+ {
+ "name": "Pattern 2",
+ "path": "/{pattern}/resource-a"
+ },
+ {
+ "name": "Pattern 3",
+ "path": "/{pattern}/resource-b"
+ },
+ {
+ "name": "Pattern 4",
+ "path": "/resource-c"
+ },
+ {
+ "name": "Pattern 5",
+ "path": "/resource/{pattern}/resource-d"
+ },
+ {
+ "name": "Pattern 6",
+ "path": "/resource/{pattern}"
+ },
+ {
+ "name": "Pattern 7",
+ "path": "/resource/{pattern}/f/{resource}"
+ },
+ {
+ "name": "Pattern 8",
+ "path": "/resource"
+ },
+ {
+ "name": "Pattern 9",
+ "path": "/file/*.suffix"
+ },
+ {
+ "name": "Pattern 10",
+ "path": "/resource/{pattern}/i/{resource}/*"
+ },
+ {
+ "name": "Pattern 11",
+ "path": "/api/{version}/{resource}"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3916450
--- /dev/null
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>servlet-policy-enforcer</module-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>All Resources</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>uma_authorization</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>servlet-policy-enforcer</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>uma_authorization</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
index ff9f05d..e3200c5 100755
--- a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
+++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml
@@ -51,6 +51,14 @@
<exclude name="**/subsystem-config.xml"/>
</fileset>
</copy>
+ <copy todir="target/test-apps/servlet-policy-enforcer" overwrite="true">
+ <fileset dir="../servlet-policy-enforcer">
+ <exclude name="**/target/**"/>
+ <exclude name="**/*.iml"/>
+ <exclude name="**/*.unconfigured"/>
+ <exclude name="**/subsystem-config.xml"/>
+ </fileset>
+ </copy>
<copy todir="target/test-apps/cors" overwrite="true">
<fileset dir="../cors">
<exclude name="**/target/**"/>
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index 02cd878..65081de 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -96,6 +96,7 @@
<filtering>true</filtering>
<includes>
<include>migration-test/*</include>
+ <include>authorization-test/*</include>
</includes>
</testResource>
<testResource>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleTestApp.java
index 551993a..40063da 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleTestApp.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleTestApp.java
@@ -48,6 +48,14 @@ public class JSConsoleTestApp extends AbstractPageWithInjectedUrl {
@FindBy(xpath = "//button[text() = 'Init']")
private WebElement initButton;
+ @FindBy(xpath = "//button[text() = 'Init with both tokens']")
+ private WebElement initWithBothTokens;
+ @FindBy(xpath = "//button[text() = 'Init with refresh token']")
+ private WebElement initWithRefreshToken;
+ @FindBy(xpath = "//button[text() = 'Init with TimeSkew']")
+ private WebElement initWithTimeSkew;
+ @FindBy(xpath = "//button[text() = 'Init with different realm name']")
+ private WebElement initWithDifferentRealmName;
@FindBy(xpath = "//button[text() = 'Login']")
private WebElement logInButton;
@FindBy(xpath = "//button[text() = 'Logout']")
@@ -88,8 +96,12 @@ public class JSConsoleTestApp extends AbstractPageWithInjectedUrl {
@FindBy(id = "timeSkew")
private WebElement timeSkewValue;
- @FindBy(id = "timeSkewInput")
- private WebElement timeSkewInput;
+ @FindBy(id = "inputField")
+ private WebElement generalInput;
+ @FindBy(id = "inputField2")
+ private WebElement generalInput2;
+ @FindBy(id = "inputField3")
+ private WebElement generalInput3;
@FindBy(xpath = "//button[text() = 'timeSkew offset']")
private WebElement timeSkewButton;
@@ -143,6 +155,22 @@ public class JSConsoleTestApp extends AbstractPageWithInjectedUrl {
initButton.click();
}
+ public void initWithBothTokens() {
+ initWithBothTokens.click();
+ }
+
+ public void initWithRefreshToken() {
+ initWithRefreshToken.click();
+ }
+
+ public void initWithTimeSkew() {
+ initWithTimeSkew.click();
+ }
+
+ public void initWithDifferentRealmName() {
+ initWithDifferentRealmName.click();
+ }
+
public void createBearerRequest() {
createBearerRequest.click();
}
@@ -175,9 +203,27 @@ public class JSConsoleTestApp extends AbstractPageWithInjectedUrl {
return timeSkewValue;
}
- public void setTimeSkewOffset(int value) {
- timeSkewInput.clear();
- timeSkewInput.sendKeys(Integer.toString(value));
+ public void setInput(String value) {
+ generalInput.clear();
+ generalInput.sendKeys(value);
+ }
+
+ public void setInput2(String value) {
+ generalInput2.clear();
+ generalInput2.sendKeys(value);
+ }
+
+ public void setInput3(String value) {
+ generalInput3.clear();
+ generalInput3.sendKeys(value);
+ }
+
+ public void setInput(int value) {
+ setInput(Integer.toString(value));
+ }
+
+ public void setTimeSkew(int value) {
+ setInput(value);
timeSkewButton.click();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
index 8f5018c..4d11f93 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
@@ -108,11 +108,13 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
public void requestEntitlement() {
entitlement.click();
pause(WAIT_AFTER_OPERATION);
+ pause(WAIT_AFTER_OPERATION);
}
public void requestEntitlements() {
entitlements.click();
pause(WAIT_AFTER_OPERATION);
+ pause(WAIT_AFTER_OPERATION);
}
public void login(String username, String password, String... scopes) throws InterruptedException {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java
index dfad4f5..fab303a 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java
@@ -45,6 +45,12 @@ public final class SuiteContext {
private boolean adminPasswordUpdated;
private final Map<String, String> smtpServer = new HashMap<>();
+ /**
+ * True if the testsuite is running in the adapter backward compatibility testing mode,
+ * i.e. if the tests are running against newer auth server
+ */
+ private static final boolean adapterCompatTesting = Boolean.parseBoolean(System.getProperty("testsuite.adapter.compat.testing"));
+
public SuiteContext(Set<ContainerInfo> arquillianContainers) {
this.container = arquillianContainers;
this.adminPasswordUpdated = false;
@@ -101,6 +107,10 @@ public final class SuiteContext {
return container;
}
+ public boolean isAdapterCompatTesting() {
+ return adapterCompatTesting;
+ }
+
@Override
public String toString() {
String containers = "Auth server: " + (isAuthServerCluster() ? "\nFrontend: " : "")
@@ -111,6 +121,9 @@ public final class SuiteContext {
if (isAuthServerMigrationEnabled()) {
containers += "Migrated from: " + System.getProperty("migrated.auth.server.version") + "\n";
}
+ if (isAdapterCompatTesting()) {
+ containers += "Adapter backward compatibility testing mode!\n";
+ }
return "SUITE CONTEXT:\n"
+ containers;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java
index 706cec9..0e55060 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java
@@ -59,6 +59,18 @@ public class AccountFields extends Form {
return this;
}
+ public String getEmail() {
+ return Form.getInputValue(emailInput);
+ }
+
+ public String getFirstName() {
+ return Form.getInputValue(firstNameInput);
+ }
+
+ public String getLastName() {
+ return Form.getInputValue(lastNameInput);
+ }
+
public void setValues(UserRepresentation user) {
setUsername(user.getUsername());
setEmail(user.getEmail());
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
index fe55695..1ee26f6 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
@@ -41,4 +41,7 @@ public class UpdateAccount extends Authenticate {
submit();
}
+ public AccountFields fields() {
+ return accountFields;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java
index 91fcb13..b5476b5 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java
@@ -14,6 +14,7 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@@ -181,7 +182,11 @@ public abstract class AbstractExec {
}
public List<String> stderrLines() {
- return parseStreamAsLines(new ByteArrayInputStream(stderr.toByteArray()));
+ return filterAgentsOutput(parseStreamAsLines(new ByteArrayInputStream(stderr.toByteArray())));
+ }
+
+ public static List<String> filterAgentsOutput(List<String> lines) {
+ return lines.stream().filter(line -> !line.contains("JAVA_TOOL_OPTIONS")).collect(Collectors.toList());
}
public String stderrString() {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java
new file mode 100644
index 0000000..142a7f5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/AbstractSocialLoginPage.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public abstract class AbstractSocialLoginPage {
+ @Drone
+ protected WebDriver driver;
+
+ public abstract void login(String user, String password);
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java
new file mode 100644
index 0000000..e0909db
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/FacebookLoginPage.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class FacebookLoginPage extends AbstractSocialLoginPage {
+ @FindBy(id = "email")
+ private WebElement emailInput;
+
+ @FindBy(id = "pass")
+ private WebElement passwordInput;
+
+ @FindBy(id = "loginbutton")
+ private WebElement loginButton;
+
+ @Override
+ public void login(String user, String password) {
+ emailInput.sendKeys(user);
+ passwordInput.sendKeys(password);
+ loginButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java
new file mode 100644
index 0000000..328d4b6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GitHubLoginPage.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class GitHubLoginPage extends AbstractSocialLoginPage {
+ @FindBy(id = "login_field")
+ private WebElement usernameInput;
+
+ @FindBy(id = "password")
+ private WebElement passwordInput;
+
+ @FindBy(name = "commit")
+ private WebElement loginButton;
+
+ @Override
+ public void login(String user, String password) {
+ usernameInput.sendKeys(user);
+ passwordInput.sendKeys(password);
+ loginButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java
new file mode 100644
index 0000000..4a0eaeb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/GoogleLoginPage.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class GoogleLoginPage extends AbstractSocialLoginPage {
+ @FindBy(id = "Email")
+ private WebElement emailInput;
+
+ @FindBy(id = "Passwd")
+ private WebElement passwordInput;
+
+ @FindBy(id = "next")
+ private WebElement nextButton;
+
+ @FindBy(id = "signIn")
+ private WebElement signInButton;
+
+ @FindBy(id = "submit_approve_access")
+ private WebElement approveAccessButton;
+
+ @FindBy(id = "PersistentCookie")
+ private WebElement persisentCookieCheckbox;
+
+ @Override
+ public void login(String user, String password) {
+ emailInput.sendKeys(user);
+ nextButton.click();
+ passwordInput.sendKeys(password);
+ persisentCookieCheckbox.click();
+ signInButton.click();
+
+ WaitUtils.waitUntilElement(approveAccessButton).is().enabled();
+ approveAccessButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java
new file mode 100644
index 0000000..d8332ad
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/LinkedInLoginPage.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class LinkedInLoginPage extends AbstractSocialLoginPage {
+ @FindBy(id = "session_key-oauth2SAuthorizeForm")
+ private WebElement usernameInput;
+
+ @FindBy(id = "session_password-oauth2SAuthorizeForm")
+ private WebElement passwordInput;
+
+ @FindBy(name = "authorize")
+ private WebElement loginButton;
+
+ @Override
+ public void login(String user, String password) {
+ usernameInput.sendKeys(user);
+ passwordInput.sendKeys(password);
+ loginButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java
new file mode 100644
index 0000000..f9e507c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/MicrosoftLoginPage.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class MicrosoftLoginPage extends AbstractSocialLoginPage {
+ @FindBy(name = "loginfmt")
+ private WebElement usernameInput;
+
+ @FindBy(name = "passwd")
+ private WebElement passwordInput;
+
+ @FindBy(id = "idSIButton9")
+ private WebElement submitButton;
+
+ @Override
+ public void login(String user, String password) {
+ usernameInput.sendKeys(user);
+ submitButton.click();
+ passwordInput.sendKeys(password);
+ submitButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java
new file mode 100644
index 0000000..8302f01
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/StackOverflowLoginPage.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class StackOverflowLoginPage extends AbstractSocialLoginPage {
+ @FindBy(xpath = "//a[@title='log in with Stack_Exchange']")
+ private WebElement loginInitButton;
+
+ @FindBy(id = "affiliate-signin-iframe")
+ private WebElement loginFrame;
+
+ @FindBy(name = "email")
+ private WebElement usernameInput;
+
+ @FindBy(id = "password")
+ private WebElement passwordInput;
+
+ @FindBy(xpath = "//input[@value='Sign In']")
+ private WebElement loginButton;
+
+ @Override
+ public void login(String user, String password) {
+ waitUntilElement(loginInitButton).is().visible();
+ loginInitButton.click();
+
+ driver.switchTo().frame(loginFrame);
+
+ usernameInput.sendKeys(user);
+ passwordInput.sendKeys(password);
+
+ loginButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java
new file mode 100644
index 0000000..911afcf
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/social/TwitterLoginPage.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.pages.social;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
+ */
+public class TwitterLoginPage extends AbstractSocialLoginPage {
+ @FindBy(id = "username_or_email")
+ private WebElement usernameInput;
+
+ @FindBy(id = "password")
+ private WebElement passwordInput;
+
+ @FindBy(id = "allow")
+ private WebElement loginButton;
+
+ @Override
+ public void login(String user, String password) {
+ usernameInput.sendKeys(user);
+ passwordInput.sendKeys(password);
+ loginButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java
index 9543373..c1869d7 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java
@@ -26,7 +26,10 @@ import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.ssl.SSLContexts;
+import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
@@ -38,7 +41,7 @@ import static org.keycloak.testsuite.util.IOUtil.PROJECT_BUILD_DIRECTORY;
public class AdminClientUtil {
- public static Keycloak createAdminClient() throws Exception {
+ public static Keycloak createAdminClient(boolean ignoreUnknownProperties) throws Exception {
SSLContext ssl = null;
if ("true".equals(System.getProperty("auth.server.ssl.required"))) {
File trustore = new File(PROJECT_BUILD_DIRECTORY, "dependency/keystore/keycloak.truststore");
@@ -47,10 +50,24 @@ public class AdminClientUtil {
System.setProperty("javax.net.ssl.trustStore", trustore.getAbsolutePath());
}
+ ResteasyJackson2Provider jacksonProvider = null;
+
+ // We need to ignore unknown JSON properties e.g. in the adapter configuration representation
+ // during adapter backward compatibility testing
+ if (ignoreUnknownProperties) {
+ jacksonProvider = new ResteasyJackson2Provider();
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ jacksonProvider.setMapper(objectMapper);
+ }
+
return Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
- MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID, null, ssl);
+ MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID, null, ssl, jacksonProvider);
}
+ public static Keycloak createAdminClient() throws Exception {
+ return createAdminClient(false);
+ }
private static SSLContext getSSLContextWithTrustore(File file, String password) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
if (!file.isFile()) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
index 136a145..ae1db35 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -135,7 +135,7 @@ public abstract class AbstractKeycloakTest {
public void beforeAbstractKeycloakTest() throws Exception {
adminClient = testContext.getAdminClient();
if (adminClient == null) {
- adminClient = AdminClientUtil.createAdminClient();
+ adminClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting());
testContext.setAdminClient(adminClient);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
index 49e2261..37941b9 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -21,6 +21,7 @@ import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
+import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.ClientRepresentation;
@@ -34,6 +35,7 @@ import org.keycloak.testsuite.auth.page.account.Applications;
import org.keycloak.testsuite.auth.page.login.OAuthGrant;
import org.keycloak.testsuite.console.page.events.Config;
import org.keycloak.testsuite.console.page.events.LoginEvents;
+import org.keycloak.testsuite.util.RealmBuilder;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebElement;
@@ -379,12 +381,12 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
public void testUpdateToken() {
logInAndInit("standard");
- jsConsoleTestAppPage.setTimeSkewOffset(-33);
+ jsConsoleTestAppPage.setTimeSkew(-33);
setTimeOffset(33);
jsConsoleTestAppPage.refreshTokenIfUnder5s();
- jsConsoleTestAppPage.setTimeSkewOffset(-34);
+ jsConsoleTestAppPage.setTimeSkew(-34);
setTimeOffset(67);
jsConsoleTestAppPage.refreshTokenIfUnder5s();
@@ -455,6 +457,75 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
}
@Test
+ public void spaceInRealmNameTest() {
+ String SPACE_REALM_NAME = "Example realm";
+ adminClient.realm(EXAMPLE).update(RealmBuilder.edit(adminClient.realm(EXAMPLE).toRepresentation()).name(SPACE_REALM_NAME).build());
+
+ jsConsoleTestAppPage.navigateTo();
+ jsConsoleTestAppPage.setInput(SPACE_REALM_NAME);
+ jsConsoleTestAppPage.initWithDifferentRealmName();
+ waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Not Authenticated)");
+ jsConsoleTestAppPage.logIn();
+ waitUntilElement(By.xpath("//body")).is().present();
+ testRealmLoginPage.form().login("user", "password");
+ jsConsoleTestAppPage.setInput(SPACE_REALM_NAME);
+ jsConsoleTestAppPage.initWithDifferentRealmName();
+
+ waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
+ waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Success");
+ }
+
+ @Test
+ public void initializeWithTokenTest() {
+ oauth.realm(EXAMPLE);
+ oauth.clientId("js-console");
+ oauth.redirectUri(appServerContextRootPage + "/js-console");
+ oauth.doLogin("user", "password");
+
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+ String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
+ String refreshToken = oauth.doRefreshTokenRequest(token, "password").getRefreshToken();
+
+ jsConsoleTestAppPage.navigateTo();
+ jsConsoleTestAppPage.setInput(token);
+ jsConsoleTestAppPage.setInput2(refreshToken);
+
+ jsConsoleTestAppPage.initWithBothTokens();
+
+ waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
+
+ jsConsoleTestAppPage.refreshToken();
+ waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
+ }
+
+ @Test
+ public void initializeWithTimeSkew() {
+ setTimeOffset(600);
+ oauth.realm(EXAMPLE);
+ oauth.clientId("js-console");
+ oauth.redirectUri(appServerContextRootPage + "/js-console");
+ oauth.doLogin("user", "password");
+
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+ String token = oauth.doAccessTokenRequest(code, "password").getAccessToken();
+ String refreshToken = oauth.doRefreshTokenRequest(token, "password").getRefreshToken();
+
+ jsConsoleTestAppPage.navigateTo();
+ jsConsoleTestAppPage.setInput(token);
+ jsConsoleTestAppPage.setInput2(refreshToken);
+ jsConsoleTestAppPage.setInput3("-600");
+
+ jsConsoleTestAppPage.initWithTimeSkew();
+
+ waitUntilElement(jsConsoleTestAppPage.getOutputElement()).text().contains("Init Success (Authenticated)");
+
+ jsConsoleTestAppPage.refreshToken();
+ waitUntilElement(jsConsoleTestAppPage.getEventsElement()).text().contains("Auth Refresh Success");
+
+ setTimeOffset(0);
+ }
+
+ @Test
public void reentrancyCallbackTest() {
logInAndInit("standard");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
new file mode 100644
index 0000000..7159635
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractPermissiveModeAdapterTest extends AbstractServletAuthzAdapterTest {
+
+ @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+ public static WebArchive deployment() throws IOException {
+ return exampleDeployment(RESOURCE_SERVER_ID)
+ .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json"), "keycloak.-permissive-authz-service.json");
+ }
+
+ @Test
+ public void testCanAccessWhenPermissive() throws Exception {
+ performTests(() -> {
+ login("jdoe", "jdoe");
+ driver.navigate().to(getResourceServerUrl() + "/enforcing/resource");
+ assertTrue(driver.getTitle().equals("Error"));
+ assertTrue(driver.getPageSource().contains("Not Found"));
+
+ driver.navigate().to(getResourceServerUrl() + "/protected/admin");
+ assertTrue(wasDenied());
+ });
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
index fd27852..d61b077 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java
@@ -16,54 +16,42 @@
*/
package org.keycloak.testsuite.adapter.example.authorization;
+import static org.junit.Assert.assertFalse;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
import org.jboss.arquillian.container.test.api.Deployer;
-import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.test.api.ArquillianResource;
-import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.BeforeClass;
-import org.junit.Test;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
-import org.keycloak.admin.client.resource.RealmResource;
-import org.keycloak.admin.client.resource.UserResource;
-import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
import org.keycloak.testsuite.util.WaitUtils;
-import org.keycloak.util.JsonSerialization;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.keycloak.testsuite.util.IOUtil.loadJson;
-import static org.keycloak.testsuite.util.IOUtil.loadRealm;
-import static org.keycloak.testsuite.util.WaitUtils.pause;
-
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAdapterTest {
- private static final String REALM_NAME = "servlet-authz";
- private static final String RESOURCE_SERVER_ID = "servlet-authz-app";
+ protected static final String REALM_NAME = "servlet-authz";
+ protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
@BeforeClass
public static void enabled() { ProfileAssume.assumePreview(); }
@@ -77,217 +65,29 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json")));
}
- @Deployment(name = RESOURCE_SERVER_ID, managed = false)
- public static WebArchive deployment() throws IOException {
- return exampleDeployment(RESOURCE_SERVER_ID);
- }
-
- @Override
- public void beforeAbstractKeycloakTest() throws Exception {
- super.beforeAbstractKeycloakTest();
- importResourceServerSettings();
- }
-
- @Test
- public void testRegularUserPermissions() throws Exception {
- try {
- this.deployer.deploy(RESOURCE_SERVER_ID);
-
- login("alice", "alice");
- assertFalse(wasDenied());
- assertTrue(hasLink("User Premium"));
- assertTrue(hasLink("Administration"));
- assertTrue(hasText("urn:servlet-authz:page:main:actionForUser"));
- assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin"));
- assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser"));
-
- navigateToDynamicMenuPage();
- assertTrue(hasText("Do user thing"));
- assertFalse(hasText("Do user premium thing"));
- assertFalse(hasText("Do administration thing"));
-
- navigateToUserPremiumPage();
- assertTrue(wasDenied());
-
- navigateToAdminPage();
- assertTrue(wasDenied());
- } finally {
- this.deployer.undeploy(RESOURCE_SERVER_ID);
- }
- }
-
- @Test
- public void testUserPremiumPermissions() throws Exception {
- try {
- this.deployer.deploy(RESOURCE_SERVER_ID);
-
- login("jdoe", "jdoe");
- assertFalse(wasDenied());
- assertTrue(hasLink("User Premium"));
- assertTrue(hasLink("Administration"));
- assertTrue(hasText("urn:servlet-authz:page:main:actionForUser"));
- assertTrue(hasText("urn:servlet-authz:page:main:actionForPremiumUser"));
- assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin"));
-
- navigateToDynamicMenuPage();
- assertTrue(hasText("Do user thing"));
- assertTrue(hasText("Do user premium thing"));
- assertFalse(hasText("Do administration thing"));
-
- navigateToUserPremiumPage();
- assertFalse(wasDenied());
-
- navigateToAdminPage();
- assertTrue(wasDenied());
- } finally {
- this.deployer.undeploy(RESOURCE_SERVER_ID);
- }
+ protected void performTests(ExceptionRunnable assertion) {
+ performTests(() -> importResourceServerSettings(), assertion);
}
- @Test
- public void testAdminPermissions() throws Exception {
+ protected void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
try {
- this.deployer.deploy(RESOURCE_SERVER_ID);
-
- login("admin", "admin");
- assertFalse(wasDenied());
- assertTrue(hasLink("User Premium"));
- assertTrue(hasLink("Administration"));
- assertTrue(hasText("urn:servlet-authz:page:main:actionForUser"));
- assertTrue(hasText("urn:servlet-authz:page:main:actionForAdmin"));
- assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser"));
-
- navigateToDynamicMenuPage();
- assertTrue(hasText("Do user thing"));
- assertTrue(hasText("Do administration thing"));
- assertFalse(hasText("Do user premium thing"));
-
- navigateToUserPremiumPage();
- assertTrue(wasDenied());
-
- navigateToAdminPage();
- assertFalse(wasDenied());
+ beforeDeploy.run();
+ deployer.deploy(RESOURCE_SERVER_ID);
+ assertion.run();
+ } catch (FileNotFoundException cause) {
+ throw new RuntimeException("Failed to import authorization settings", cause);
+ } catch (Exception cause) {
+ throw new RuntimeException("Error while executing tests", cause);
} finally {
- this.deployer.undeploy(RESOURCE_SERVER_ID);
+ deployer.undeploy(RESOURCE_SERVER_ID);
}
}
- @Test
- public void testGrantPremiumAccessToUser() throws Exception {
- try {
- this.deployer.deploy(RESOURCE_SERVER_ID);
-
- login("alice", "alice");
- assertFalse(wasDenied());
-
- navigateToUserPremiumPage();
- assertTrue(wasDenied());
-
- for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
- if ("Premium Resource Permission".equals(policy.getName())) {
- policy.getConfig().put("applyPolicies", "[\"Any User Policy\"]");
- getAuthorizationResource().policies().policy(policy.getId()).update(policy);
- }
- }
-
- login("alice", "alice");
-
- navigateToUserPremiumPage();
- assertFalse(wasDenied());
-
- for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
- if ("Premium Resource Permission".equals(policy.getName())) {
- policy.getConfig().put("applyPolicies", "[\"Only Premium User Policy\"]");
- getAuthorizationResource().policies().policy(policy.getId()).update(policy);
- }
- }
-
- login("alice", "alice");
-
- navigateToUserPremiumPage();
- assertTrue(wasDenied());
-
- PolicyRepresentation onlyAlicePolicy = new PolicyRepresentation();
-
- onlyAlicePolicy.setName("Temporary Premium Access Policy");
- onlyAlicePolicy.setType("user");
- HashMap<String, String> config = new HashMap<>();
- UsersResource usersResource = realmsResouce().realm(REALM_NAME).users();
- List<UserRepresentation> users = usersResource.search("alice", null, null, null, null, null);
-
- assertFalse(users.isEmpty());
-
- config.put("users", JsonSerialization.writeValueAsString(Arrays.asList(users.get(0).getId())));
-
- onlyAlicePolicy.setConfig(config);
- getAuthorizationResource().policies().create(onlyAlicePolicy);
-
- for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
- if ("Premium Resource Permission".equals(policy.getName())) {
- policy.getConfig().put("applyPolicies", "[\"Temporary Premium Access Policy\"]");
- getAuthorizationResource().policies().policy(policy.getId()).update(policy);
- }
- }
-
- login("alice", "alice");
-
- navigateToUserPremiumPage();
- assertFalse(wasDenied());
- } finally {
- this.deployer.undeploy(RESOURCE_SERVER_ID);
- }
- }
-
- @Test
- public void testGrantAdministrativePermissions() throws Exception {
- try {
- this.deployer.deploy(RESOURCE_SERVER_ID);
-
- login("jdoe", "jdoe");
-
- navigateToAdminPage();
- assertTrue(wasDenied());
-
- RealmResource realmResource = realmsResouce().realm(REALM_NAME);
- UsersResource usersResource = realmResource.users();
- List<UserRepresentation> users = usersResource.search("jdoe", null, null, null, null, null);
-
- assertFalse(users.isEmpty());
-
- UserResource userResource = usersResource.get(users.get(0).getId());
-
- RoleRepresentation adminRole = realmResource.roles().get("admin").toRepresentation();
- userResource.roles().realmLevel().add(Arrays.asList(adminRole));
-
- login("jdoe", "jdoe");
-
- navigateToAdminPage();
- assertFalse(wasDenied());
- } finally {
- this.deployer.undeploy(RESOURCE_SERVER_ID);
- }
- }
-
- //KEYCLOAK-3830
- @Test
- public void testAccessPublicResource() throws Exception {
- try {
- this.deployer.deploy(RESOURCE_SERVER_ID);
-
- driver.navigate().to(getResourceServerUrl() + "/public-html.html");
- WaitUtils.waitForPageToLoad(driver);
- assertTrue(hasText("This is public resource that should be accessible without login."));
-
- } finally {
- this.deployer.undeploy(RESOURCE_SERVER_ID);
- }
- }
-
- private boolean hasLink(String text) {
+ protected boolean hasLink(String text) {
return getLink(text) != null;
}
- private boolean hasText(String text) {
+ protected boolean hasText(String text) {
return this.driver.getPageSource().contains(text);
}
@@ -295,11 +95,11 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
}
- private void importResourceServerSettings() throws FileNotFoundException {
+ protected void importResourceServerSettings() throws FileNotFoundException {
getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class));
}
- private AuthorizationResource getAuthorizationResource() throws FileNotFoundException {
+ protected AuthorizationResource getAuthorizationResource() {
return getClientResource(RESOURCE_SERVER_ID).authorization();
}
@@ -317,18 +117,22 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
pause(500);
}
- private void login(String username, String password) throws InterruptedException {
- navigateTo();
- Thread.sleep(2000);
- if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
- Thread.sleep(2000);
- logOut();
+ protected void login(String username, String password) {
+ try {
navigateTo();
- }
+ Thread.sleep(2000);
+ if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
+ Thread.sleep(2000);
+ logOut();
+ navigateTo();
+ }
- Thread.sleep(2000);
+ Thread.sleep(2000);
- this.loginPage.form().login(username, password);
+ this.loginPage.form().login(username, password);
+ } catch (Exception cause) {
+ throw new RuntimeException("Login failed", cause);
+ }
}
private void navigateTo() {
@@ -336,11 +140,11 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
}
- private boolean wasDenied() {
+ protected boolean wasDenied() {
return this.driver.getPageSource().contains("You can not access this resource.");
}
- private URL getResourceServerUrl() {
+ protected URL getResourceServerUrl() {
try {
return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
} catch (MalformedURLException e) {
@@ -348,18 +152,57 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
}
}
- private void navigateToDynamicMenuPage() {
+ protected void navigateToDynamicMenuPage() {
navigateTo();
getLink("Dynamic Menu").click();
}
- private void navigateToUserPremiumPage() {
+ protected void navigateToUserPremiumPage() {
navigateTo();
getLink("User Premium").click();
}
- private void navigateToAdminPage() {
+ protected void navigateToAdminPage() {
navigateTo();
getLink("Administration").click();
}
+
+ protected void updatePermissionPolicies(String permissionName, String... policyNames) {
+ for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
+ if (permissionName.equalsIgnoreCase(policy.getName())) {
+ StringBuilder policies = new StringBuilder("[");
+
+ for (String policyName : policyNames) {
+ if (policies.length() > 1) {
+ policies.append(",");
+ }
+ policies.append("\"").append(policyName).append("\"");
+
+ }
+
+ policies.append("]");
+
+ policy.getConfig().put("applyPolicies", policies.toString());
+ getAuthorizationResource().policies().policy(policy.getId()).update(policy);
+ }
+ }
+ }
+
+ protected void createUserPolicy(String name, String... userNames) {
+ UserPolicyRepresentation policy = new UserPolicyRepresentation();
+
+ policy.setName(name);
+
+ for (String userName : userNames) {
+ policy.addUser(userName);
+ }
+
+ assertFalse(policy.getUsers().isEmpty());
+
+ getAuthorizationResource().policies().users().create(policy);
+ }
+
+ protected interface ExceptionRunnable {
+ void run() throws Exception;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
new file mode 100644
index 0000000..3aef537
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzFunctionalAdapterTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.ResourcesResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.util.WaitUtils;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractServletAuthzAdapterTest {
+
+ @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+ public static WebArchive deployment() throws IOException {
+ return exampleDeployment(RESOURCE_SERVER_ID);
+ }
+
+ @Test
+ public void testCanNotAccessWhenEnforcing() throws Exception {
+ performTests(() -> {
+ importResourceServerSettings();
+ ResourcesResource resources = getAuthorizationResource().resources();
+ ResourceRepresentation resource = resources.findByName("Protected Resource").get(0);
+
+ resource.setUri("/index.jsp");
+
+ resources.resource(resource.getId()).update(resource);
+ }, () -> {
+ login("jdoe", "jdoe");
+ driver.navigate().to(getResourceServerUrl().toString() + "/enforcing/resource");
+ assertTrue(wasDenied());
+ });
+ }
+
+ @Test
+ public void testRegularUserPermissions() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+ assertFalse(wasDenied());
+ assertTrue(hasLink("User Premium"));
+ assertTrue(hasLink("Administration"));
+ assertTrue(hasText("urn:servlet-authz:page:main:actionForUser"));
+ assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin"));
+ assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser"));
+
+ navigateToDynamicMenuPage();
+ assertTrue(hasText("Do user thing"));
+ assertFalse(hasText("Do user premium thing"));
+ assertFalse(hasText("Do administration thing"));
+
+ navigateToUserPremiumPage();
+ assertTrue(wasDenied());
+
+ navigateToAdminPage();
+ assertTrue(wasDenied());
+ });
+ }
+
+ @Test
+ public void testUserPremiumPermissions() throws Exception {
+ performTests(() -> {
+ login("jdoe", "jdoe");
+ assertFalse(wasDenied());
+ assertTrue(hasLink("User Premium"));
+ assertTrue(hasLink("Administration"));
+ assertTrue(hasText("urn:servlet-authz:page:main:actionForUser"));
+ assertTrue(hasText("urn:servlet-authz:page:main:actionForPremiumUser"));
+ assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin"));
+
+ navigateToDynamicMenuPage();
+ assertTrue(hasText("Do user thing"));
+ assertTrue(hasText("Do user premium thing"));
+ assertFalse(hasText("Do administration thing"));
+
+ navigateToUserPremiumPage();
+ assertFalse(wasDenied());
+
+ navigateToAdminPage();
+ assertTrue(wasDenied());
+ });
+ }
+
+ @Test
+ public void testAdminPermissions() throws Exception {
+ performTests(() -> {
+ login("admin", "admin");
+ assertFalse(wasDenied());
+ assertTrue(hasLink("User Premium"));
+ assertTrue(hasLink("Administration"));
+ assertTrue(hasText("urn:servlet-authz:page:main:actionForUser"));
+ assertTrue(hasText("urn:servlet-authz:page:main:actionForAdmin"));
+ assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser"));
+
+ navigateToDynamicMenuPage();
+ assertTrue(hasText("Do user thing"));
+ assertTrue(hasText("Do administration thing"));
+ assertFalse(hasText("Do user premium thing"));
+
+ navigateToUserPremiumPage();
+ assertTrue(wasDenied());
+
+ navigateToAdminPage();
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testGrantPremiumAccessToUser() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+ assertFalse(wasDenied());
+
+ navigateToUserPremiumPage();
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Premium Resource Permission", "Any User Policy");
+
+ login("alice", "alice");
+
+ navigateToUserPremiumPage();
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Premium Resource Permission", "Only Premium User Policy");
+
+ login("alice", "alice");
+
+ navigateToUserPremiumPage();
+ assertTrue(wasDenied());
+
+ createUserPolicy("Temporary Premium Access Policy", "alice");
+
+ updatePermissionPolicies("Premium Resource Permission", "Temporary Premium Access Policy");
+
+ login("alice", "alice");
+
+ navigateToUserPremiumPage();
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testGrantAdministrativePermissions() throws Exception {
+ performTests(() -> {
+ login("jdoe", "jdoe");
+
+ navigateToAdminPage();
+ assertTrue(wasDenied());
+
+ RealmResource realmResource = realmsResouce().realm(REALM_NAME);
+ UsersResource usersResource = realmResource.users();
+ List<UserRepresentation> users = usersResource.search("jdoe", null, null, null, null, null);
+
+ assertFalse(users.isEmpty());
+
+ UserResource userResource = usersResource.get(users.get(0).getId());
+
+ RoleRepresentation adminRole = realmResource.roles().get("admin").toRepresentation();
+ userResource.roles().realmLevel().add(Arrays.asList(adminRole));
+
+ login("jdoe", "jdoe");
+
+ navigateToAdminPage();
+ assertFalse(wasDenied());
+ });
+ }
+
+ //KEYCLOAK-3830
+ @Test
+ public void testAccessPublicResource() throws Exception {
+ performTests(() -> {
+ driver.navigate().to(getResourceServerUrl() + "/public-html.html");
+ WaitUtils.waitForPageToLoad(driver);
+ assertTrue(hasText("This is public resource that should be accessible without login."));
+ });
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
new file mode 100644
index 0000000..aaeee4f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.ResourcePermissionsResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.ProfileAssume;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleAdapterTest {
+
+ protected static final String REALM_NAME = "servlet-policy-enforcer-authz";
+ protected static final String RESOURCE_SERVER_ID = "servlet-policy-enforcer";
+
+ @BeforeClass
+ public static void enabled() { ProfileAssume.assumePreview(); }
+
+ @ArquillianResource
+ private Deployer deployer;
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(
+ loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json")));
+ }
+
+ @Deployment(name = RESOURCE_SERVER_ID, managed = false)
+ public static WebArchive deployment() throws IOException {
+ return exampleDeployment(RESOURCE_SERVER_ID);
+ }
+
+ @Test
+ public void testPattern1() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource/a/b");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 1 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a/b");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 1 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a/b");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern2() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/a/resource-a");
+ assertFalse(wasDenied());
+ navigateTo("/b/resource-a");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 2 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/a/resource-a");
+ assertTrue(wasDenied());
+ navigateTo("/b/resource-a");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/b/resource-a");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern3() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/a/resource-b");
+ assertFalse(wasDenied());
+ navigateTo("/b/resource-b");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/a/resource-b");
+ assertTrue(wasDenied());
+ navigateTo("/b/resource-b");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 3 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/b/resource-b");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/b/resource-a");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/a/resource-b");
+ assertTrue(wasDenied());
+ navigateTo("/b/resource-a");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern4() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource-c");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 4 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource-c");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 4 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource-c");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern5() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource/a/resource-d");
+ assertFalse(wasDenied());
+ navigateTo("/resource/b/resource-d");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 5 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a/resource-d");
+ assertTrue(wasDenied());
+ navigateTo("/resource/b/resource-d");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource/b/resource-d");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern6() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource/a");
+ assertFalse(wasDenied());
+ navigateTo("/resource/b");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 6 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a");
+ assertTrue(wasDenied());
+ navigateTo("/resource/b");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 6 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource/b");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern7() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource/a/f/b");
+ assertFalse(wasDenied());
+ navigateTo("/resource/c/f/d");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 7 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a/f/b");
+ assertTrue(wasDenied());
+ navigateTo("/resource/c/f/d");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 7 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource/c/f/d");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern8() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 8 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 8 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern9() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/file/*.suffix");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 9 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/file/*.suffix");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 9 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/file/*.suffix");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern10() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+
+ navigateTo("/resource/a/i/b/c/d/e");
+ navigateTo("/resource/a/i/b/c/");
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies("Pattern 10 Permission", "Deny Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a/i/b/c/d/e");
+ navigateTo("/resource/a/i/b/c/d");
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies("Pattern 10 Permission", "Default Policy");
+ login("alice", "alice");
+ navigateTo("/resource/a/i/b/c/d");
+ assertFalse(wasDenied());
+ });
+ }
+
+ @Test
+ public void testPattern11UsingResourceInstancePermission() throws Exception {
+ performTests(() -> {
+ login("alice", "alice");
+ navigateTo("/api/v1/resource-a");
+ assertFalse(wasDenied());
+ navigateTo("/api/v1/resource-b");
+ assertFalse(wasDenied());
+
+ ResourceRepresentation resource = new ResourceRepresentation("/api/v1/resource-c");
+
+ resource.setUri(resource.getName());
+
+ getAuthorizationResource().resources().create(resource);
+
+ createResourcePermission(resource.getName() + " permission", resource.getName(), "Default Policy");
+
+ login("alice", "alice");
+ navigateTo(resource.getUri());
+ assertFalse(wasDenied());
+
+ updatePermissionPolicies(resource.getName() + " permission", "Deny Policy");
+
+ login("alice", "alice");
+ navigateTo(resource.getUri());
+ assertTrue(wasDenied());
+
+ updatePermissionPolicies(resource.getName() + " permission", "Default Policy");
+
+ login("alice", "alice");
+ navigateTo(resource.getUri());
+ assertFalse(wasDenied());
+
+ navigateTo("/api/v1");
+ assertTrue(wasDenied());
+ navigateTo("/api/v1/");
+ assertTrue(wasDenied());
+ navigateTo("/api");
+ assertTrue(wasDenied());
+ navigateTo("/api/");
+ assertTrue(wasDenied());
+ });
+ }
+
+ private void navigateTo(String path) {
+ this.driver.navigate().to(getResourceServerUrl() + path);
+ }
+
+ private void performTests(ExceptionRunnable assertion) {
+ performTests(() -> {}, assertion);
+ }
+
+ private void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
+ try {
+ beforeDeploy.run();
+ deployer.deploy(RESOURCE_SERVER_ID);
+ assertion.run();
+ } catch (FileNotFoundException cause) {
+ throw new RuntimeException("Failed to import authorization settings", cause);
+ } catch (Exception cause) {
+ throw new RuntimeException("Error while executing tests", cause);
+ } finally {
+ deployer.undeploy(RESOURCE_SERVER_ID);
+ }
+ }
+
+ private AuthorizationResource getAuthorizationResource() {
+ return getClientResource(RESOURCE_SERVER_ID).authorization();
+ }
+
+ private ClientResource getClientResource(String clientId) {
+ ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
+ ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
+ return clients.get(resourceServer.getId());
+ }
+
+ private void logOut() {
+ navigateTo();
+ By by = By.xpath("//a[text() = 'Sign Out']");
+ WaitUtils.waitUntilElement(by);
+ this.driver.findElement(by).click();
+ pause(500);
+ }
+
+ private void login(String username, String password) {
+ try {
+ navigateTo();
+ if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
+ logOut();
+ navigateTo();
+ }
+ this.loginPage.form().login(username, password);
+ navigateTo();
+ assertFalse(wasDenied());
+ } catch (Exception cause) {
+ throw new RuntimeException("Login failed", cause);
+ }
+ }
+
+ private void navigateTo() {
+ this.driver.navigate().to(getResourceServerUrl());
+ WaitUtils.waitUntilElement(By.xpath("//p[text() = 'Welcome']"));
+ }
+
+ private boolean wasDenied() {
+ return this.driver.getPageSource().contains("You can not access this resource");
+ }
+
+ private URL getResourceServerUrl() {
+ try {
+ return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Could not obtain resource server url.", e);
+ }
+ }
+
+ private void updatePermissionPolicies(String permissionName, String... policyNames) {
+ ResourcePermissionsResource permissions = getAuthorizationResource().permissions().resource();
+ ResourcePermissionRepresentation permission = permissions.findByName(permissionName);
+
+ permission.addPolicy(policyNames);
+
+ permissions.findById(permission.getId()).update(permission);
+ }
+
+ private void createResourcePermission(String name, String resourceName, String... policyNames) {
+ ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+
+ permission.setName(name);
+ permission.addResource(resourceName);
+ permission.addPolicy(policyNames);
+
+ getAuthorizationResource().permissions().resource().create(permission);
+ }
+
+ private interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
index 275a078..f7a9335 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -469,8 +469,10 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd
assertNotNull(version2);
assertNotNull(version2.getVersion());
assertNotNull(version2.getBuildTime());
- assertEquals(version.getVersion(), version2.getVersion());
- assertEquals(version.getBuildTime(), version2.getBuildTime());
+ if (!suiteContext.isAdapterCompatTesting()) {
+ assertEquals(version.getVersion(), version2.getVersion());
+ assertEquals(version.getBuildTime(), version2.getBuildTime());
+ }
client.close();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AuthzCleanupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AuthzCleanupTest.java
index 6f68ae1..4c472fb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AuthzCleanupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AuthzCleanupTest.java
@@ -16,27 +16,31 @@
*/
package org.keycloak.testsuite.admin;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+import java.util.List;
+
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
+import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -51,42 +55,55 @@ public class AuthzCleanupTest extends AbstractKeycloakTest {
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
- RealmRepresentation testRealmRep = new RealmRepresentation();
- testRealmRep.setId(TEST);
- testRealmRep.setRealm(TEST);
- testRealmRep.setEnabled(true);
- testRealms.add(testRealmRep);
+ testRealms.add(RealmBuilder.create().name(TEST)
+ .client(ClientBuilder.create().clientId("myclient")
+ .secret("secret")
+ .authorizationServicesEnabled(true)
+ .redirectUris("http://localhost/myclient")
+ .defaultRoles(
+ "client-role-1",
+ "client-role-2",
+ "Acme administrator",
+ "Acme viewer",
+ "tenant administrator",
+ "tenant viewer",
+ "tenant user"
+ )
+ .build())
+ .build());
}
public static void setup(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName(TEST);
- ClientModel client = session.realms().addClient(realm, "myclient");
- RoleModel role1 = client.addRole("client-role1");
- RoleModel role2 = client.addRole("client-role2");
-
+ session.getContext().setRealm(realm);
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
- ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
- createRolePolicy(authz, resourceServer, role1);
- createRolePolicy(authz, resourceServer, role2);
-
-
+ ClientModel myclient = realm.getClientByClientId("myclient");
+ ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(myclient.getId());
+ createRolePolicy(authz, resourceServer, "client-role-1");
+ createRolePolicy(authz, resourceServer, "client-role-2");
}
- private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
- Policy policy = authz.getStoreFactory().getPolicyStore().create(role.getName(), "role", resourceServer);
+ private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, String roleName) {
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
- String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
- policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
- policy.setLogic(Logic.POSITIVE);
- Map<String, String> config = new HashMap<>();
- config.put("roles", roleValues);
- policy.setConfig(config);
- return policy;
+ representation.setName(roleName);
+ representation.setType("role");
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
+ representation.addRole(roleName, true);
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
}
@Test
public void testCreate() throws Exception {
+ ClientsResource clients = getAdminClient().realms().realm(TEST).clients();
+ ClientRepresentation client = clients.findByClientId("myclient").get(0);
+ ResourceServerRepresentation settings = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/acme-resource-server-cleanup-test.json"), ResourceServerRepresentation.class);
+
+ clients.get(client.getId()).authorization().importSettings(settings);
+
testingClient.server().run(AuthzCleanupTest::setup);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractPermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractPermissionManagementTest.java
new file mode 100644
index 0000000..264a888
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractPermissionManagementTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import org.junit.Before;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.util.AdminClientUtil;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractPermissionManagementTest extends AbstractKeycloakTest {
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(createTestRealm().build());
+ }
+
+ protected RealmBuilder createTestRealm() {
+ return RealmBuilder.create().name("authz-test")
+ .user(UserBuilder.create().username("marta").password("password"))
+ .user(UserBuilder.create().username("kolo").password("password"))
+ .client(ClientBuilder.create().clientId("resource-server-test")
+ .secret("secret")
+ .authorizationServicesEnabled(true)
+ .redirectUris("http://localhost/resource-server-test")
+ .defaultRoles("uma_protection")
+ .directAccessGrants());
+ }
+
+ @Before
+ public void configureAuthorization() throws Exception {
+ createResourcesAndScopes();
+ RealmResource realm = getRealm();
+ createPolicies(realm, getClient(realm));
+ }
+
+ protected void assertRepresentation(AbstractPolicyRepresentation expected, AbstractPolicyRepresentation actual,
+ Supplier<List<ResourceRepresentation>> resources,
+ Supplier<List<ScopeRepresentation>> scopes,
+ Supplier<List<PolicyRepresentation>> policies) {
+ assertNotNull(actual);
+ assertNotNull(actual.getId());
+
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getDescription(), actual.getDescription());
+ assertEquals(expected.getDecisionStrategy(), actual.getDecisionStrategy());
+ assertEquals(expected.getLogic(), actual.getLogic());
+ assertNull(actual.getResources());
+ assertNull(actual.getPolicies());
+ assertNull(actual.getScopes());
+
+ List<PolicyRepresentation> associatedPolicies = policies.get();
+
+ if (expected.getPolicies() != null) {
+ assertEquals(expected.getPolicies().size(), associatedPolicies.size());
+ assertEquals(0, associatedPolicies.stream().map(representation1 -> representation1.getName()).filter(policyName -> !expected.getPolicies().contains(policyName)).count());
+ } else {
+ assertTrue(associatedPolicies.isEmpty());
+ }
+
+ List<ResourceRepresentation> associatedResources = resources.get();
+
+ if (expected.getResources() != null) {
+ assertEquals(expected.getResources().size(), associatedResources.size());
+ assertEquals(0, associatedResources.stream().map(representation1 -> representation1.getName()).filter(resourceName -> !expected.getResources().contains(resourceName)).count());
+ } else {
+ assertTrue(associatedResources.isEmpty());
+ }
+
+ List<ScopeRepresentation> associatedScopes = scopes.get();
+
+ if (expected.getScopes() != null) {
+ assertEquals(expected.getScopes().size(), associatedScopes.size());
+ assertEquals(0, associatedScopes.stream().map(representation1 -> representation1.getName()).filter(scopeName -> !expected.getScopes().contains(scopeName)).count());
+ } else {
+ assertTrue(associatedScopes.isEmpty());
+ }
+
+ expected.setId(actual.getId());
+ }
+
+ private void createResourcesAndScopes() throws IOException {
+ Set<ScopeRepresentation> scopes = new HashSet<>();
+
+ scopes.add(new ScopeRepresentation("read"));
+ scopes.add(new ScopeRepresentation("write"));
+ scopes.add(new ScopeRepresentation("execute"));
+
+ List<ResourceRepresentation> resources = new ArrayList<>();
+
+ resources.add(new ResourceRepresentation("Resource A", scopes));
+ resources.add(new ResourceRepresentation("Resource B", scopes));
+ resources.add(new ResourceRepresentation("Resource C", scopes));
+
+ resources.forEach(resource -> getClient().authorization().resources().create(resource));
+ }
+
+ private void createPolicies(RealmResource realm, ClientResource client) throws IOException {
+ createUserPolicy("Only Marta Policy", realm, client, "marta");
+ createUserPolicy("Only Kolo Policy", realm, client, "kolo");
+ }
+
+ private void createUserPolicy(String name, RealmResource realm, ClientResource client, String username) throws IOException {
+ String userId = realm.users().search(username).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
+
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName(name);
+ representation.addUser(userId);
+
+ client.authorization().policies().users().create(representation);
+ }
+
+ protected ClientResource getClient() {
+ return getClient(getRealm());
+ }
+
+ protected ClientResource getClient(RealmResource realm) {
+ ClientsResource clients = realm.clients();
+ return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
+ }
+
+ protected RealmResource getRealm() {
+ try {
+ return AdminClientUtil.createAdminClient().realm("authz-test");
+ } catch (Exception cause) {
+ throw new RuntimeException("Failed to create admin client", cause);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
new file mode 100644
index 0000000..1deed22
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ResourcePermissionResource;
+import org.keycloak.admin.client.resource.ResourcePermissionsResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcePermissionManagementTest extends AbstractPermissionManagementTest {
+
+ @Test
+ public void testCreateResourcePermission() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Resource A Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateResourceType() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Resource A Type Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.setResourceType("test-resource");
+ representation.addPolicy("Only Marta Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Update Test Resource Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.getResources().remove("Resource A");
+ representation.addResource("Resource B");
+ representation.getPolicies().remove("Only Marta Policy");
+
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+ ResourcePermissionResource permission = permissions.findById(representation.getId());
+
+ permission.update(representation);
+
+ assertRepresentation(representation, permission);
+
+ representation.getResources().clear();
+ representation.setResourceType("changed");
+
+ permission.update(representation);
+
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.setResourceType("test-resource");
+ representation.addPolicy("Only Marta Policy");
+
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+ Response response = permissions.create(representation);
+ ResourcePermissionRepresentation created = response.readEntity(ResourcePermissionRepresentation.class);
+
+ permissions.findById(created.getId()).remove();
+
+ ResourcePermissionResource removed = permissions.findById(created.getId());
+
+ try {
+ removed.toRepresentation();
+ fail("Permission not removed");
+ } catch (NotFoundException ignore) {
+
+ }
+ }
+
+ @Test
+ public void failCreateWithSameName() {
+ AuthorizationResource authorization = getClient().authorization();
+ ResourcePermissionRepresentation permission1 = new ResourcePermissionRepresentation();
+
+ permission1.setName("Conflicting Name Permission");
+ permission1.setResourceType("test-resource");
+ permission1.addPolicy("Only Marta Policy");
+
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+
+ permissions.create(permission1);
+
+ ResourcePermissionRepresentation permission2 = new ResourcePermissionRepresentation();
+
+ permission2.setName(permission1.getName());
+
+ Response response = permissions.create(permission2);
+
+ assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
+ }
+
+ private void assertCreated(AuthorizationResource authorization, ResourcePermissionRepresentation representation) {
+ ResourcePermissionsResource permissions = authorization.permissions().resource();
+ Response response = permissions.create(representation);
+ ResourcePermissionRepresentation created = response.readEntity(ResourcePermissionRepresentation.class);
+ ResourcePermissionResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+
+ private void assertRepresentation(ResourcePermissionRepresentation representation, ResourcePermissionResource permission) {
+ assertRepresentation(representation, permission.toRepresentation(), () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java
new file mode 100644
index 0000000..1b4a701
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/RolePolicyManagementTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+import javax.management.relation.Role;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.admin.client.resource.RolePoliciesResource;
+import org.keycloak.admin.client.resource.RolePolicyResource;
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RolesBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class RolePolicyManagementTest extends AbstractPermissionManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm().roles(
+ RolesBuilder.create()
+ .realmRole(new RoleRepresentation("Role A", "Role A description", false))
+ .realmRole(new RoleRepresentation("Role B", "Role B description", false))
+ .realmRole(new RoleRepresentation("Role C", "Role C description", false))
+ );
+ }
+
+ @Test
+ public void testCreateRealmRolePolicy() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Realm Role Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addRole("Role A", false);
+ representation.addRole("Role B", true);
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateClientRolePolicy() {
+ ClientResource client = getClient();
+ AuthorizationResource authorization = client.authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Realm Client Role Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+
+ RolesResource roles = client.roles();
+
+ roles.create(new RoleRepresentation("Client Role A", "desc", false));
+
+ ClientRepresentation clientRep = client.toRepresentation();
+
+ roles.create(new RoleRepresentation("Client Role B", "desc", false));
+
+ representation.addRole("Client Role A");
+ representation.addClientRole(clientRep.getClientId(), "Client Role B", true);
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Update Test Role Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addRole("Role A", false);
+ representation.addRole("Role B", true);
+ representation.addRole("Role C", false);
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setRoles(representation.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().equals("Resource A")).collect(Collectors.toSet()));
+
+ RolePoliciesResource policies = authorization.policies().roles();
+ RolePolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ for (RolePolicyRepresentation.RoleDefinition roleDefinition : representation.getRoles()) {
+ if (roleDefinition.getId().equals("Role B")) {
+ roleDefinition.setRequired(false);
+ }
+ if (roleDefinition.getId().equals("Role C")) {
+ roleDefinition.setRequired(true);
+ }
+ }
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addRole("Role A", false);
+
+ RolePoliciesResource policies = authorization.policies().roles();
+ Response response = policies.create(representation);
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ RolePolicyResource removed = policies.findById(created.getId());
+
+ try {
+ removed.toRepresentation();
+ fail("Permission not removed");
+ } catch (NotFoundException ignore) {
+
+ }
+ }
+
+ @Test
+ public void testGenericConfig() {
+ AuthorizationResource authorization = getClient().authorization();
+ RolePolicyRepresentation representation = new RolePolicyRepresentation();
+
+ representation.setName("Test Generic Config Permission");
+ representation.addRole("Role A", false);
+
+ RolePoliciesResource policies = authorization.policies().roles();
+ Response response = policies.create(representation);
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+
+ PolicyResource policy = authorization.policies().policy(created.getId());
+ PolicyRepresentation genericConfig = policy.toRepresentation();
+
+ assertNotNull(genericConfig.getConfig());
+ assertNotNull(genericConfig.getConfig().get("roles"));
+
+ RoleRepresentation role = getRealm().roles().get("Role A").toRepresentation();
+
+ assertTrue(genericConfig.getConfig().get("roles").contains(role.getId()));
+ }
+
+ private void assertCreated(AuthorizationResource authorization, RolePolicyRepresentation representation) {
+ RolePoliciesResource permissions = authorization.policies().roles();
+ Response response = permissions.create(representation);
+ RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
+ RolePolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+
+ private void assertRepresentation(RolePolicyRepresentation representation, RolePolicyResource permission) {
+ RolePolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ assertEquals(representation.getRoles().size(), actual.getRoles().size());
+ ClientRepresentation clientRep = getClient().toRepresentation();
+ assertEquals(0, actual.getRoles().stream().filter(actualDefinition -> !representation.getRoles().stream()
+ .filter(roleDefinition -> (getRoleName(actualDefinition.getId()).equals(roleDefinition.getId()) || (clientRep.getClientId() + "/" + getRoleName(actualDefinition.getId())).equals(roleDefinition.getId())) && actualDefinition.isRequired() == roleDefinition.isRequired())
+ .findFirst().isPresent())
+ .count());
+ }
+
+ private String getRoleName(String id) {
+ return getRealm().rolesById().getRole(id).getName();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java
new file mode 100644
index 0000000..a833668
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ScopePermissionManagementTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ScopePermissionResource;
+import org.keycloak.admin.client.resource.ScopePermissionsResource;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopePermissionManagementTest extends AbstractPermissionManagementTest {
+
+ @Test
+ public void testCreateResourceScopePermission() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Resource A Scope Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addScope("read", "execute");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testCreateScopePermission() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Read Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addScope("read", "write");
+ representation.addPolicy("Only Marta Policy");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Update Test Scope Permission");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addResource("Resource A");
+ representation.addScope("read", "execute");
+ representation.addPolicy("Only Marta Policy", "Only Kolo Policy");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.getResources().remove("Resource A");
+ representation.addResource("Resource B");
+ representation.getScopes().remove("execute");
+ representation.getPolicies().remove("Only Marta Policy");
+
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+ ScopePermissionResource permission = permissions.findById(representation.getId());
+
+ permission.update(representation);
+
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addScope("execute");
+ representation.addPolicy("Only Marta Policy");
+
+ assertCreated(authorization, representation);
+
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+
+ permissions.findById(representation.getId()).remove();
+
+ ScopePermissionResource removed = permissions.findById(representation.getId());
+
+ try {
+ removed.toRepresentation();
+ fail("Permission not removed");
+ } catch (NotFoundException ignore) {
+
+ }
+ }
+
+ @Test
+ public void failCreateWithSameName() {
+ AuthorizationResource authorization = getClient().authorization();
+ ScopePermissionRepresentation permission1 = new ScopePermissionRepresentation();
+
+ permission1.setName("Conflicting Name Permission");
+ permission1.addScope("read");
+ permission1.addPolicy("Only Marta Policy");
+
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+
+ permissions.create(permission1);
+
+ ScopePermissionRepresentation permission2 = new ScopePermissionRepresentation();
+
+ permission2.setName(permission1.getName());
+
+ Response response = permissions.create(permission2);
+
+ assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
+ }
+
+ private void assertCreated(AuthorizationResource authorization, ScopePermissionRepresentation representation) {
+ ScopePermissionsResource permissions = authorization.permissions().scope();
+ Response response = permissions.create(representation);
+ ScopePermissionRepresentation created = response.readEntity(ScopePermissionRepresentation.class);
+ ScopePermissionResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+
+ private void assertRepresentation(ScopePermissionRepresentation representation, ScopePermissionResource permission) {
+ assertRepresentation(representation, permission.toRepresentation(), () -> permission.resources(), () -> permission.scopes(), () -> permission.associatedPolicies());
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
new file mode 100644
index 0000000..9a80d68
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/UserPolicyManagementTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin.client.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.PolicyResource;
+import org.keycloak.admin.client.resource.UserPoliciesResource;
+import org.keycloak.admin.client.resource.UserPolicyResource;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserPolicyManagementTest extends AbstractPermissionManagementTest {
+
+ @Override
+ protected RealmBuilder createTestRealm() {
+ return super.createTestRealm()
+ .user(UserBuilder.create().username("User A"))
+ .user(UserBuilder.create().username("User B"))
+ .user(UserBuilder.create().username("User C"));
+ }
+
+ @Test
+ public void testCreateUserPolicy() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Realm User Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addUser("User A");
+ representation.addUser("User B");
+
+ assertCreated(authorization, representation);
+ }
+
+ @Test
+ public void testUpdate() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Update Test User Policy");
+ representation.setDescription("description");
+ representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
+ representation.setLogic(Logic.NEGATIVE);
+ representation.addUser("User A");
+ representation.addUser("User B");
+ representation.addUser("User C");
+
+ assertCreated(authorization, representation);
+
+ representation.setName("changed");
+ representation.setDescription("changed");
+ representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ representation.setLogic(Logic.POSITIVE);
+ representation.setUsers(representation.getUsers().stream().filter(userName -> !userName.equals("User A")).collect(Collectors.toSet()));
+
+ UserPoliciesResource policies = authorization.policies().users();
+ UserPolicyResource permission = policies.findById(representation.getId());
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+
+ representation.setUsers(representation.getUsers().stream().filter(userName -> !userName.equals("User C")).collect(Collectors.toSet()));
+
+ permission.update(representation);
+ assertRepresentation(representation, permission);
+ }
+
+ @Test
+ public void testDelete() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Test Delete Permission");
+ representation.addUser("User A");
+
+ UserPoliciesResource policies = authorization.policies().users();
+ Response response = policies.create(representation);
+ UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
+
+ policies.findById(created.getId()).remove();
+
+ UserPolicyResource removed = policies.findById(created.getId());
+
+ try {
+ removed.toRepresentation();
+ fail("Permission not removed");
+ } catch (NotFoundException ignore) {
+
+ }
+ }
+
+ @Test
+ public void testGenericConfig() {
+ AuthorizationResource authorization = getClient().authorization();
+ UserPolicyRepresentation representation = new UserPolicyRepresentation();
+
+ representation.setName("Test Generic Config Permission");
+ representation.addUser("User A");
+
+ UserPoliciesResource policies = authorization.policies().users();
+ Response response = policies.create(representation);
+ UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
+
+ PolicyResource policy = authorization.policies().policy(created.getId());
+ PolicyRepresentation genericConfig = policy.toRepresentation();
+
+ assertNotNull(genericConfig.getConfig());
+ assertNotNull(genericConfig.getConfig().get("users"));
+
+ UserRepresentation user = getRealm().users().search("User A").get(0);
+
+ assertTrue(genericConfig.getConfig().get("users").contains(user.getId()));
+ }
+
+ private void assertCreated(AuthorizationResource authorization, UserPolicyRepresentation representation) {
+ UserPoliciesResource permissions = authorization.policies().users();
+ Response response = permissions.create(representation);
+ UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
+ UserPolicyResource permission = permissions.findById(created.getId());
+ assertRepresentation(representation, permission);
+ }
+
+ private void assertRepresentation(UserPolicyRepresentation representation, UserPolicyResource permission) {
+ UserPolicyRepresentation actual = permission.toRepresentation();
+ assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
+ assertEquals(representation.getUsers().size(), actual.getUsers().size());
+ assertEquals(0, actual.getUsers().stream().filter(userId -> !representation.getUsers().stream()
+ .filter(userName -> getUserName(userId).equalsIgnoreCase(userName))
+ .findFirst().isPresent())
+ .count());
+ }
+
+ private String getUserName(String id) {
+ return getRealm().users().get(id).toRepresentation().getUsername();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java
index 00aaf91..bcf35fb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java
@@ -47,6 +47,8 @@ import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import java.util.HashMap;
@@ -111,21 +113,16 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
}
private static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
- Policy permission = authz.getStoreFactory().getPolicyStore().create(name, "scope", resourceServer);
- String resources = "[\"" + resource.getId() + "\"]";
- String scopes = "[\"" + scope.getId() + "\"]";
- String applyPolicies = "[\"" + policy.getId() + "\"]";
- Map<String, String> config = new HashMap<>();
- config.put("resources", resources);
- config.put("scopes", scopes);
- config.put("applyPolicies", applyPolicies);
- permission.setConfig(config);
- permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
- permission.setLogic(Logic.POSITIVE);
- permission.addResource(resource);
- permission.addScope(scope);
- permission.addAssociatedPolicy(policy);
- return permission;
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName(name);
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
+ representation.addResource(resource.getName());
+ representation.addScope(scope.getName());
+ representation.addPolicy(policy.getName());
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
}
private static Resource createRoleResource(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
@@ -153,15 +150,18 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
roleName = client.getClientId() ;
}
roleName = "role.policy." + roleName + "." + role.getName();
- Policy policy = authz.getStoreFactory().getPolicyStore().create(roleName, "role", resourceServer);
+ PolicyRepresentation representation = new PolicyRepresentation();
+ representation.setName(roleName);
+ representation.setType("role");
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
- policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
- policy.setLogic(Logic.POSITIVE);
Map<String, String> config = new HashMap<>();
config.put("roles", roleValues);
- policy.setConfig(config);
- return policy;
+ representation.setConfig(config);
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
}
public static void setupUsers(KeycloakSession session) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
new file mode 100644
index 0000000..6db4891
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.authz;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.representation.EntitlementResponse;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.JWSInputException;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.util.AdminClientUtil;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ConflictingScopePermissionTest extends AbstractKeycloakTest {
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(RealmBuilder.create().name("authz-test")
+ .user(UserBuilder.create().username("marta").password("password"))
+ .user(UserBuilder.create().username("kolo").password("password"))
+ .client(ClientBuilder.create().clientId("resource-server-test")
+ .secret("secret")
+ .authorizationServicesEnabled(true)
+ .redirectUris("http://localhost/resource-server-test")
+ .defaultRoles("uma_protection")
+ .directAccessGrants())
+ .build());
+ }
+
+ @Before
+ public void configureAuthorization() throws Exception {
+ createResourcesAndScopes();
+
+ RealmResource realm = getRealm();
+ ClientResource client = getClient(realm);
+
+ createPolicies(realm, client);
+ createPermissions(client);
+ }
+
+ /**
+ * <p>Scope Read on Resource A has two conflicting permissions. One is granting access for Marta and the other for Kolo.
+ *
+ * <p>Scope Read should not be granted for Marta.
+ */
+ @Test
+ public void testMartaCanAccessResourceAWithExecuteAndWrite() {
+ List<Permission> permissions = getEntitlements("marta", "password");
+
+ for (Permission permission : new ArrayList<>(permissions)) {
+ String resourceSetName = permission.getResourceSetName();
+
+ switch (resourceSetName) {
+ case "Resource A":
+ assertEquals(2, permission.getScopes().size());
+ assertTrue(permission.getScopes().contains("execute"));
+ assertTrue(permission.getScopes().contains("write"));
+ permissions.remove(permission);
+ break;
+ case "Resource C":
+ assertEquals(3, permission.getScopes().size());
+ assertTrue(permission.getScopes().contains("execute"));
+ assertTrue(permission.getScopes().contains("write"));
+ assertTrue(permission.getScopes().contains("read"));
+ permissions.remove(permission);
+ break;
+ default:
+ fail("Unexpected permission for resource [" + resourceSetName + "]");
+ }
+ }
+
+ assertTrue(permissions.isEmpty());
+ }
+
+ private List<Permission> getEntitlements(String username, String password) {
+ AuthzClient authzClient = getAuthzClient();
+ EntitlementResponse response = authzClient.entitlement(authzClient.obtainAccessToken(username, password).getToken()).getAll("resource-server-test");
+ AccessToken accessToken;
+
+ try {
+ accessToken = new JWSInput(response.getRpt()).readJsonContent(AccessToken.class);
+ } catch (JWSInputException cause) {
+ throw new RuntimeException("Failed to deserialize RPT", cause);
+ }
+
+ AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+ assertNotNull("RPT does not contain any authorization data", authorization);
+
+ return authorization.getPermissions();
+ }
+
+ private RealmResource getRealm() throws Exception {
+ return AdminClientUtil.createAdminClient().realm("authz-test");
+ }
+
+ private ClientResource getClient(RealmResource realm) {
+ ClientsResource clients = realm.clients();
+ return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
+ }
+
+ private void createPermissions(ClientResource client) throws IOException {
+ createResourcePermission("Resource C Only For Marta Permission", "Resource C", Arrays.asList("Only Marta Policy"), client);
+ createScopePermission("Resource A Scope Read Only For Marta Permission", "Resource A", Arrays.asList("read"), Arrays.asList("Only Marta Policy"), client);
+ createScopePermission("Resource A Scope Read Only For Kolo Permission", "Resource A", Arrays.asList("read"), Arrays.asList("Only Kolo Policy"), client);
+ }
+
+ private void createPolicies(RealmResource realm, ClientResource client) throws IOException {
+ createUserPolicy("Only Marta Policy", realm, client, "marta");
+ createUserPolicy("Only Kolo Policy", realm, client, "kolo");
+ }
+
+ private void createResourcesAndScopes() throws IOException {
+ AuthzClient authzClient = getAuthzClient();
+ Set<ScopeRepresentation> scopes = new HashSet<>();
+
+ scopes.add(new ScopeRepresentation("read"));
+ scopes.add(new ScopeRepresentation("write"));
+ scopes.add(new ScopeRepresentation("execute"));
+
+ List<ResourceRepresentation> resources = new ArrayList<>();
+
+ resources.add(new ResourceRepresentation("Resource A", scopes));
+ resources.add(new ResourceRepresentation("Resource B", scopes));
+ resources.add(new ResourceRepresentation("Resource C", scopes));
+
+ resources.forEach(resource -> authzClient.protection().resource().create(resource));
+ }
+
+ private void createUserPolicy(String name, RealmResource realm, ClientResource client, String username) throws IOException {
+ String userId = realm.users().search(username).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
+
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setName(name);
+ representation.setType("user");
+
+ Map<String, String> config = new HashMap<>();
+
+ config.put("users", JsonSerialization.writeValueAsString(new String[] {userId}));
+
+ representation.setConfig(config);
+
+ client.authorization().policies().create(representation);
+ }
+
+ private void createResourcePermission(String name, String resourceName, List<String> policies, ClientResource client) throws IOException {
+ ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
+
+ representation.setName(name);
+ representation.addResource(resourceName);
+ representation.addPolicy(policies.toArray(new String[policies.size()]));
+
+ client.authorization().permissions().resource().create(representation);
+ }
+
+ private void createScopePermission(String name, String resourceName, List<String> scopes, List<String> policies, ClientResource client) throws IOException {
+ AuthorizationResource authorization = client.authorization();
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName(name);
+
+ if (resourceName != null) {
+ representation.addResource(resourceName);
+ }
+
+ representation.addScope(scopes.toArray(new String[scopes.size()]));
+ representation.addPolicy(scopes.toArray(new String[policies.size()]));
+
+ authorization.permissions().scope().create(representation);
+ }
+
+ private AuthzClient getAuthzClient() {
+ try {
+ return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class));
+ } catch (IOException cause) {
+ throw new RuntimeException("Failed to create authz client", cause);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
index 98d3366..74fcb7b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
@@ -24,9 +24,7 @@ import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
-import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@@ -38,6 +36,8 @@ import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import java.util.HashMap;
@@ -82,34 +82,33 @@ public class PolicyEvaluationCompositeRoleTest extends AbstractKeycloakTest {
}
private static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
- Policy permission = authz.getStoreFactory().getPolicyStore().create(name, "scope", resourceServer);
- String resources = "[\"" + resource.getId() + "\"]";
- String scopes = "[\"" + scope.getId() + "\"]";
- String applyPolicies = "[\"" + policy.getId() + "\"]";
- Map<String, String> config = new HashMap<>();
- config.put("resources", resources);
- config.put("scopes", scopes);
- config.put("applyPolicies", applyPolicies);
- permission.setConfig(config);
- permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
- permission.setLogic(Logic.POSITIVE);
- permission.addResource(resource);
- permission.addScope(scope);
- permission.addAssociatedPolicy(policy);
- return permission;
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName(name);
+ representation.setType("scope");
+ representation.addResource(resource.getName());
+ representation.addScope(scope.getName());
+ representation.addPolicy(policy.getName());
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
}
private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
- Policy policy = authz.getStoreFactory().getPolicyStore().create(role.getName(), "role", resourceServer);
+ PolicyRepresentation representation = new PolicyRepresentation();
+ representation.setName(role.getName());
+ representation.setType("role");
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
- policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
- policy.setLogic(Logic.POSITIVE);
Map<String, String> config = new HashMap<>();
config.put("roles", roleValues);
- policy.setConfig(config);
- return policy;
+ representation.setConfig(config);
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java
index 2882e81..096fb76 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java
@@ -3,18 +3,29 @@ package org.keycloak.testsuite.broker;
import org.jboss.arquillian.graphene.Graphene;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
+import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.social.openshift.OpenshiftV3IdentityProvider;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
-import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.auth.page.login.UpdateAccount;
import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
+import org.keycloak.testsuite.pages.social.AbstractSocialLoginPage;
+import org.keycloak.testsuite.pages.social.FacebookLoginPage;
+import org.keycloak.testsuite.pages.social.GitHubLoginPage;
+import org.keycloak.testsuite.pages.social.GoogleLoginPage;
+import org.keycloak.testsuite.pages.social.LinkedInLoginPage;
+import org.keycloak.testsuite.pages.social.MicrosoftLoginPage;
+import org.keycloak.testsuite.pages.social.StackOverflowLoginPage;
+import org.keycloak.testsuite.pages.social.TwitterLoginPage;
import org.keycloak.testsuite.util.IdentityProviderBuilder;
import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.URLUtils;
+import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
@@ -26,9 +37,18 @@ import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.FACEBOOK;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.LINKEDIN;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.MICROSOFT;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.OPENSHIFT;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.STACKOVERFLOW;
+import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.TWITTER;
/**
- * Created by st on 19.01.17.
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ * @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class SocialLoginTest extends AbstractKeycloakTest {
@@ -38,13 +58,39 @@ public class SocialLoginTest extends AbstractKeycloakTest {
private static Properties config = new Properties();
@Page
- public AccountUpdateProfilePage account;
+ private LoginPage loginPage;
@Page
- public LoginPage loginPage;
+ private UpdateAccount updateAccountPage;
+
+ public enum Provider {
+ GOOGLE("google", GoogleLoginPage.class),
+ FACEBOOK("facebook", FacebookLoginPage.class),
+ GITHUB("github", GitHubLoginPage.class),
+ TWITTER("twitter", TwitterLoginPage.class),
+ LINKEDIN("linkedin", LinkedInLoginPage.class),
+ MICROSOFT("microsoft", MicrosoftLoginPage.class),
+ STACKOVERFLOW("stackoverflow", StackOverflowLoginPage.class),
+ OPENSHIFT("openshift-v3", null);
+
+ private String id;
+ private Class<? extends AbstractSocialLoginPage> pageObjectClazz;
+
+ Provider(String id, Class<? extends AbstractSocialLoginPage> pageObjectClazz) {
+ this.id = id;
+ this.pageObjectClazz = pageObjectClazz;
+ }
- @Page
- public LoginUpdateProfilePage updateProfilePage;
+ public String id() {
+ return id;
+ }
+
+ public Class<? extends AbstractSocialLoginPage> pageObjectClazz() {
+ return pageObjectClazz;
+ }
+ }
+
+ private Provider currentTestProvider;
@BeforeClass
public static void loadConfig() throws Exception {
@@ -52,12 +98,22 @@ public class SocialLoginTest extends AbstractKeycloakTest {
config.load(new FileInputStream(System.getProperty(SOCIAL_CONFIG)));
}
+
+ @Before
+ public void beforeSocialLoginTest() {
+ accountPage.setAuthRealm(REALM);
+ accountPage.navigateTo();
+ currentTestProvider = null;
+ }
@After
public void removeUser() {
List<UserRepresentation> users = adminClient.realm(REALM).users().search(null, null, null);
for (UserRepresentation user : users) {
- adminClient.realm(REALM).users().get(user.getId()).remove();
+ if (user.getServiceAccountClientId() == null) {
+ log.infof("removing test user '%s'", user.getUsername());
+ adminClient.realm(REALM).users().get(user.getId()).remove();
+ }
}
}
@@ -67,21 +123,17 @@ public class SocialLoginTest extends AbstractKeycloakTest {
List<IdentityProviderRepresentation> idps = new LinkedList<>();
rep.setIdentityProviders(idps);
- idps.add(buildIdp("openshift-v3"));
- idps.add(buildIdp("google"));
- idps.add(buildIdp("facebook"));
- idps.add(buildIdp("github"));
- idps.add(buildIdp("twitter"));
- idps.add(buildIdp("linkedin"));
- idps.add(buildIdp("microsoft"));
- idps.add(buildIdp("stackoverflow"));
+ for (Provider provider : Provider.values()) {
+ idps.add(buildIdp(provider));
+ }
testRealms.add(rep);
}
@Test
+ @Ignore
+ // TODO: Fix and revamp this test
public void openshiftLogin() throws Exception {
- account.open(REALM);
loginPage.clickSocial("openshift-v3");
Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("inputUsername")));
@@ -92,169 +144,136 @@ public class SocialLoginTest extends AbstractKeycloakTest {
Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input[name=approve]")));
driver.findElement(By.cssSelector("input[name=approve]")).click();
- assertEquals(config.getProperty("openshift-v3.username", config.getProperty("common.profile.username")), account.getUsername());
+ assertEquals(config.getProperty("openshift-v3.username", config.getProperty("common.profile.username")), accountPage.getUsername());
}
@Test
public void googleLogin() throws InterruptedException {
- account.open(REALM);
-
- loginPage.clickSocial("google");
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("Email")));
-
- driver.findElement(By.id("Email")).sendKeys(config.getProperty("google.username", config.getProperty("common.username")));
- driver.findElement(By.id("next")).click();
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("Passwd")));
-
- driver.findElement(By.id("Passwd")).sendKeys(config.getProperty("google.password", config.getProperty("common.password")));
- driver.findElement(By.id("signIn")).click();
-
- Graphene.waitGui().until(ExpectedConditions.elementToBeClickable(By.id("submit_approve_access")));
-
- driver.findElement(By.id("submit_approve_access")).click();
-
- assertEquals(config.getProperty("google.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("google.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("google.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ currentTestProvider = GOOGLE;
+ performLogin();
+ assertAccount();
}
@Test
- public void faceBookLogin() {
- account.open(REALM);
-
- loginPage.clickSocial("facebook");
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("email")));
- driver.findElement(By.id("email")).sendKeys(config.getProperty("facebook.username", config.getProperty("common.username")));
- driver.findElement(By.id("pass")).sendKeys(config.getProperty("facebook.password", config.getProperty("common.password")));
-
- driver.findElement(By.id("loginbutton")).click();
-
- assertEquals(config.getProperty("facebook.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("facebook.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("facebook.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ public void facebookLogin() {
+ currentTestProvider = FACEBOOK;
+ performLogin();
+ assertAccount();
}
@Test
public void githubLogin() {
- account.open(REALM);
-
- loginPage.clickSocial("github");
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("login_field")));
- driver.findElement(By.id("login_field")).sendKeys(config.getProperty("github.username", config.getProperty("common.username")));
- driver.findElement(By.id("password")).sendKeys(config.getProperty("github.password", config.getProperty("common.password")));
-
- driver.findElement(By.name("commit")).click();
-
- assertEquals(config.getProperty("github.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("github.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("github.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ currentTestProvider = GITHUB;
+ performLogin();
+ assertAccount();
}
@Test
public void twitterLogin() {
- account.open(REALM);
-
- loginPage.clickSocial("twitter");
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("username_or_email")));
- driver.findElement(By.id("username_or_email")).sendKeys(config.getProperty("twitter.username", config.getProperty("common.username")));
- driver.findElement(By.id("password")).sendKeys(config.getProperty("twitter.password", config.getProperty("common.password")));
-
- driver.findElement(By.id("allow")).click();
-
- assertTrue(updateProfilePage.isCurrent());
-
- assertEquals(config.getProperty("twitter.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("twitter.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals("", updateProfilePage.getEmail());
-
- updateProfilePage.update(null, null, "keycloakey@gmail.com");
-
- assertEquals(config.getProperty("twitter.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("twitter.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("twitter.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ currentTestProvider = TWITTER;
+ performLogin();
+ assertUpdateProfile(false, false, true);
+ assertAccount();
}
@Test
public void linkedinLogin() {
- account.open(REALM);
-
- loginPage.clickSocial("linkedin");
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("session_key-oauth2SAuthorizeForm")));
- driver.findElement(By.id("session_key-oauth2SAuthorizeForm")).sendKeys(config.getProperty("linkedin.username", config.getProperty("common.username")));
- driver.findElement(By.id("session_password-oauth2SAuthorizeForm")).sendKeys(config.getProperty("linkedin.password", config.getProperty("common.password")));
-
- driver.findElement(By.name("authorize")).click();
-
- assertEquals(config.getProperty("linkedin.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("linkedin.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("linkedin.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ currentTestProvider = LINKEDIN;
+ performLogin();
+ assertAccount();
}
@Test
public void microsoftLogin() {
- account.open(REALM);
-
- loginPage.clickSocial("microsoft");
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.name("loginfmt")));
- driver.findElement(By.name("loginfmt")).sendKeys(config.getProperty("microsoft.username", config.getProperty("common.username")));
- driver.findElement(By.xpath("//input[@value='Next']")).click();
-
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.name("passwd")));
- driver.findElement(By.name("passwd")).sendKeys(config.getProperty("microsoft.password", config.getProperty("common.password")));
- driver.findElement(By.xpath("//input[@value='Sign in']")).click();
-
- assertEquals(config.getProperty("microsoft.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("microsoft.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("microsoft.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ currentTestProvider = MICROSOFT;
+ performLogin();
+ assertAccount();
}
@Test
public void stackoverflowLogin() {
- account.open(REALM);
+ currentTestProvider = STACKOVERFLOW;
+ performLogin();
+ assertUpdateProfile(false, false, true);
+ assertAccount();
+ }
- loginPage.clickSocial("stackoverflow");
+ private IdentityProviderRepresentation buildIdp(Provider provider) {
+ IdentityProviderRepresentation idp = IdentityProviderBuilder.create().alias(provider.id()).providerId(provider.id()).build();
+ idp.setEnabled(true);
+ idp.getConfig().put("clientId", getConfig(provider, "clientId"));
+ idp.getConfig().put("clientSecret", getConfig(provider, "clientSecret"));
+ if (provider == STACKOVERFLOW) {
+ idp.getConfig().put("key", getConfig(provider, "clientKey"));
+ }
+ if (provider == OPENSHIFT) {
+ idp.getConfig().put("baseUrl", config.getProperty(provider.id() + ".baseUrl", OpenshiftV3IdentityProvider.BASE_URL));
+ }
+ return idp;
+ }
- Graphene.waitModel().until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a[@title='log in with Stack_Exchange']")));
- driver.findElement(By.xpath("//a[@title='log in with Stack_Exchange']")).click();
+ private String getConfig(Provider provider, String key) {
+ return config.getProperty(provider.id() + "." + key, config.getProperty("common." + key));
+ }
- driver.switchTo().frame(driver.findElement(By.id("affiliate-signin-iframe")));
+ private String getConfig(String key) {
+ return getConfig(currentTestProvider, key);
+ }
- Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.name("email")));
- driver.findElement(By.name("email")).sendKeys(config.getProperty("stackoverflow.username", config.getProperty("common.username")));
- driver.findElement(By.name("password")).sendKeys(config.getProperty("stackoverflow.password", config.getProperty("common.password")));
+ private void performLogin() {
+ loginPage.clickSocial(currentTestProvider.id());
- driver.findElement(By.xpath("//input[@value='Sign In']")).click();
+ // Just to be sure there's no redirect in progress
+ WaitUtils.pause(3000);
+ WaitUtils.waitForPageToLoad(driver);
- assertEquals(config.getProperty("stackoverflow.profile.firstName", config.getProperty("common.profile.firstName")), updateProfilePage.getFirstName());
- assertEquals(config.getProperty("stackoverflow.profile.lastName", config.getProperty("common.profile.lastName")), updateProfilePage.getLastName());
- assertEquals("", updateProfilePage.getEmail());
+ // Only when there's not active session for the social provider, i.e. login is required
+ if (URLUtils.currentUrlDoesntStartWith(driver, getAuthServerRoot().toASCIIString())) {
+ log.infof("current URL: %s", driver.getCurrentUrl());
+ log.infof("performing log in to '%s' ...", currentTestProvider.id());
+ AbstractSocialLoginPage loginPage = Graphene.createPageFragment(currentTestProvider.pageObjectClazz(), driver.findElement(By.tagName("html")));
+ loginPage.login(getConfig("username"), getConfig("password"));
+ }
+ else {
+ log.infof("already logged in to '%s'; skipping the login process", currentTestProvider.id());
+ }
+ }
- updateProfilePage.update(null, null, "keycloakey@gmail.com");
+ private void assertAccount() {
+ assertTrue(URLUtils.currentUrlStartWith(driver, accountPage.toString())); // Sometimes after login the URL ends with /# or similar
- assertEquals(config.getProperty("stackoverflow.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName());
- assertEquals(config.getProperty("stackoverflow.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName());
- assertEquals(config.getProperty("stackoverflow.profile.email", config.getProperty("common.profile.email")), account.getEmail());
+ assertEquals(getConfig("profile.firstName"), accountPage.getFirstName());
+ assertEquals(getConfig("profile.lastName"), accountPage.getLastName());
+ assertEquals(getConfig("profile.email"), accountPage.getEmail());
}
- private IdentityProviderRepresentation buildIdp(String id) {
- IdentityProviderRepresentation idp = IdentityProviderBuilder.create().alias(id).providerId(id).build();
- idp.setEnabled(true);
- idp.getConfig().put("clientId", config.getProperty(id + ".clientId"));
- idp.getConfig().put("clientSecret", config.getProperty(id + ".clientSecret"));
- if (id.equals("stackoverflow")) {
- idp.getConfig().put("key", config.getProperty(id + ".clientKey"));
+ private void assertUpdateProfile(boolean firstName, boolean lastName, boolean email) {
+ assertTrue(URLUtils.currentUrlDoesntStartWith(driver, accountPage.toString()));
+
+ if (firstName) {
+ assertTrue(updateAccountPage.fields().getFirstName().isEmpty());
+ updateAccountPage.fields().setFirstName(getConfig("profile.firstName"));
}
- if (id.equals("openshift-v3")) {
- idp.getConfig().put("baseUrl", config.getProperty(id + ".baseUrl", OpenshiftV3IdentityProvider.BASE_URL));
+ else {
+ assertEquals(getConfig("profile.firstName"), updateAccountPage.fields().getFirstName());
}
- return idp;
- }
+ if (lastName) {
+ assertTrue(updateAccountPage.fields().getLastName().isEmpty());
+ updateAccountPage.fields().setLastName(getConfig("profile.lastName"));
+ }
+ else {
+ assertEquals(getConfig("profile.lastName"), updateAccountPage.fields().getLastName());
+ }
+
+ if (email) {
+ assertTrue(updateAccountPage.fields().getEmail().isEmpty());
+ updateAccountPage.fields().setEmail(getConfig("profile.email"));
+ }
+ else {
+ assertEquals(getConfig("profile.email"), updateAccountPage.fields().getEmail());
+ }
+
+ updateAccountPage.submit();
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java
index 215bf15..958c8a9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java
@@ -16,9 +16,14 @@
*/
package org.keycloak.testsuite.forms;
+import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.IOUtils;
import org.jboss.arquillian.graphene.page.Page;
-import org.junit.*;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
import org.keycloak.events.Details;
@@ -40,6 +45,7 @@ import org.keycloak.testsuite.util.UserBuilder;
import javax.ws.rs.core.Response;
import java.io.IOException;
+import java.util.Map;
/**
* Tests for {@link org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticator}
@@ -56,8 +62,12 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
private AuthenticationFlowRepresentation flow;
+ public static final String EXECUTION_ID = "scriptAuth";
+
@BeforeClass
- public static void enabled() { ProfileAssume.assumePreview(); }
+ public static void enabled() {
+ ProfileAssume.assumePreview();
+ }
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
@@ -99,8 +109,6 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
.builtIn(false)
.build();
- String scriptAuth = "scriptAuth";
-
Response createFlowResponse = testRealm().flows().createFlow(scriptBrowserFlow);
Assert.assertEquals(201, createFlowResponse.getStatus());
@@ -119,7 +127,7 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
.build();
AuthenticationExecutionRepresentation authScriptExecution = ExecutionBuilder.create()
- .id(scriptAuth)
+ .id(EXECUTION_ID)
.parentFlow(this.flow.getId())
.requirement(AuthenticationExecutionModel.Requirement.REQUIRED.name())
.authenticator(ScriptBasedAuthenticatorFactory.PROVIDER_ID)
@@ -127,12 +135,11 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
Response addExecutionResponse = testRealm().flows().addExecution(usernamePasswordFormExecution);
Assert.assertEquals(201, addExecutionResponse.getStatus());
+ addExecutionResponse.close();
addExecutionResponse = testRealm().flows().addExecution(authScriptExecution);
Assert.assertEquals(201, addExecutionResponse.getStatus());
-
- Response newExecutionConfigResponse = testRealm().flows().newExecutionConfig(scriptAuth, createScriptAuthConfig(scriptAuth, "authenticator-example.js", "/scripts/authenticator-example.js", "simple script based authenticator"));
- Assert.assertEquals(201, newExecutionConfigResponse.getStatus());
+ addExecutionResponse.close();
testContext.setInitialized(true);
}
@@ -142,6 +149,7 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
*/
@Test
public void loginShouldWorkWithScriptAuthenticator() {
+ addConfigFromFile("/scripts/authenticator-example.js");
loginPage.open();
@@ -155,20 +163,70 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
*/
@Test
public void loginShouldFailWithScriptAuthenticator() {
+ addConfigFromFile("/scripts/authenticator-example.js");
loginPage.open();
loginPage.login("fail", "password");
- events.expect(EventType.LOGIN_ERROR).user((String)null).error(Errors.USER_NOT_FOUND).assertEvent();
+ events.expect(EventType.LOGIN_ERROR).user((String) null).error(Errors.USER_NOT_FOUND).assertEvent();
}
- private AuthenticatorConfigRepresentation createScriptAuthConfig(String alias, String scriptName, String scriptCodePath, String scriptDescription) throws IOException {
+ /**
+ * KEYCLOAK-4505
+ */
+ @Test
+ public void scriptWithClientSession() {
+ addConfigFromFile("/scripts/client-session-test.js", ImmutableMap.of(
+ "realm", "test",
+ "clientId", "test-app",
+ "authMethod", "openid-connect"));
+
+ loginPage.open();
- AuthenticatorConfigRepresentation configRep = new AuthenticatorConfigRepresentation();
+ loginPage.login("user", "password");
+ events.expectLogin().user("user").detail(Details.USERNAME, "user").assertEvent();
+ }
+
+ private void addConfigFromFile(String filename) {
+ addConfigFromFile(filename, null);
+ }
+
+ private void addConfigFromFile(String filename, Map<String, String> parameters) {
+
+ String alias = filename.substring(filename.lastIndexOf("/") + 1);
+ String script = loadFile(filename, parameters);
+
+ Response newExecutionConfigResponse = testRealm().flows().
+ newExecutionConfig(EXECUTION_ID, createScriptAuthConfig(EXECUTION_ID, alias, script, "script based authenticator"));
+ newExecutionConfigResponse.close();
+
+ Assert.assertEquals(201, newExecutionConfigResponse.getStatus());
+ }
+
+ private String loadFile(String filename, Map<String, String> parameters) {
+ String script = null;
+ try {
+ script = IOUtils.toString(getClass().getResourceAsStream(filename));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (parameters != null) {
+ for (Map.Entry<String, String> entry : parameters.entrySet()) {
+ script = script.replaceAll("\\$\\{" + entry.getKey() + "}", entry.getValue());
+ }
+ }
+
+ return script;
+ }
+
+ private AuthenticatorConfigRepresentation createScriptAuthConfig(String alias, String scriptName, String script, String scriptDescription) {
+
+ AuthenticatorConfigRepresentation configRep = new AuthenticatorConfigRepresentation();
configRep.setAlias(alias);
- configRep.getConfig().put("scriptCode", IOUtils.toString(getClass().getResourceAsStream(scriptCodePath)));
+ configRep.getConfig().put("scriptCode", script);
configRep.getConfig().put("scriptName", scriptName);
configRep.getConfig().put("scriptDescription", scriptDescription);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
index 6174842..b799833 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
@@ -95,7 +95,7 @@ public class EmailTest extends AbstractI18NTest {
MimeMessage message = greenMail.getReceivedMessages()[0];
- Assert.assertEquals("Passwort zurückzusetzen", message.getSubject());
+ Assert.assertEquals("Passwort zurücksetzen", message.getSubject());
// Revert
changeUserLocale("en");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
index 921e3db..c0ca7f1 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
@@ -43,9 +43,11 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.account.AccountTest;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
import org.keycloak.testsuite.auth.page.AuthRealm;
+import org.keycloak.testsuite.pages.AccountApplicationsPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.ClientManager;
@@ -58,6 +60,7 @@ import org.keycloak.util.TokenUtil;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import javax.ws.rs.NotFoundException;
@@ -79,6 +82,10 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
@Page
protected LoginPage loginPage;
+
+ @Page
+ protected AccountApplicationsPage applicationsPage;
+
@Rule
public AssertEvents events = new AssertEvents(this);
@@ -482,4 +489,65 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
RealmRepresentation testRealm = offlineTokenAdmin.realm("test").toRepresentation();
Assert.assertNotNull(testRealm);
}
+
+
+ // KEYCLOAK-4525
+ @Test
+ public void offlineTokenRemoveClientWithTokens() throws Exception {
+ // Create new client
+ RealmResource appRealm = adminClient.realm("test");
+
+ ClientRepresentation clientRep = ClientBuilder.create().clientId("offline-client-2")
+ .id(KeycloakModelUtils.generateId())
+ .directAccessGrants()
+ .secret("secret1").build();
+
+ appRealm.clients().create(clientRep);
+
+ // Direct grant login requesting offline token
+ oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+ oauth.clientId("offline-client-2");
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "test-user@localhost", "password");
+ Assert.assertNull(tokenResponse.getErrorDescription());
+ AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
+ String offlineTokenString = tokenResponse.getRefreshToken();
+ RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
+
+ events.expectLogin()
+ .client("offline-client-2")
+ .user(userId)
+ .session(token.getSessionState())
+ .detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
+ .detail(Details.TOKEN_ID, token.getId())
+ .detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
+ .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_OFFLINE)
+ .detail(Details.USERNAME, "test-user@localhost")
+ .removeDetail(Details.CODE_ID)
+ .removeDetail(Details.REDIRECT_URI)
+ .removeDetail(Details.CONSENT)
+ .assertEvent();
+
+ // Go to account mgmt applications page
+ applicationsPage.open();
+ loginPage.login("test-user@localhost", "password");
+ events.expectLogin().client("account").detail(Details.REDIRECT_URI, AccountTest.ACCOUNT_REDIRECT + "?path=applications").assertEvent();
+ Assert.assertTrue(applicationsPage.isCurrent());
+ Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
+ Assert.assertTrue(apps.containsKey("offline-client-2"));
+ Assert.assertEquals("Offline Token", apps.get("offline-client-2").getAdditionalGrants().get(0));
+
+ // Now remove the client
+ ClientResource offlineTokenClient2 = ApiUtil.findClientByClientId(appRealm, "offline-client-2" );
+ offlineTokenClient2.remove();
+
+ // Go to applications page and see offline-client not anymore
+ applicationsPage.open();
+ apps = applicationsPage.getApplications();
+ Assert.assertFalse(apps.containsKey("offline-client-2"));
+
+ // Login as admin and see consents of user
+ UserResource user = ApiUtil.findUserByUsernameId(appRealm, "test-user@localhost");
+ List<Map<String, Object>> consents = user.getConsents();
+ Assert.assertTrue(consents.isEmpty());
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
index 7530275..df49edb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
@@ -159,4 +159,9 @@ public class ClientBuilder {
rep.setRootUrl(rootUrl);
return this;
}
+
+ public ClientBuilder authorizationServicesEnabled(boolean enable) {
+ rep.setAuthorizationServicesEnabled(true);
+ return this;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json
new file mode 100644
index 0000000..3c1f55f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/acme-resource-server-cleanup-test.json
@@ -0,0 +1,195 @@
+{
+ "allowRemoteResourceManagement": false,
+ "policyEnforcementMode": "ENFORCING",
+ "resources": [
+ {
+ "name": "Administration resource",
+ "uri": "/admin/*",
+ "type": "http://acme.com/admin",
+ "scopes": [
+ {
+ "name": "urn:acme.com:scopes:admin:manage"
+ },
+ {
+ "name": "urn:acme.com:scopes:admin:view"
+ }
+ ],
+ "typedScopes": []
+ },
+ {
+ "name": "Role resource",
+ "uri": "/{REALM}/roles",
+ "type": "http://acme.com/roles",
+ "scopes": [
+ {
+ "name": "urn:acme.com:scopes:role:view"
+ }
+ ],
+ "typedScopes": []
+ },
+ {
+ "name": "User profile resource",
+ "uri": "/{REALM}/userprofiles/*",
+ "type": "http://acme.com/userprofiles",
+ "scopes": [
+ {
+ "name": "urn:acme.com:scopes:userprofile:manage"
+ },
+ {
+ "name": "urn:acme.com:scopes:userprofile:view"
+ }
+ ],
+ "typedScopes": []
+ },
+ {
+ "name": "Account resource",
+ "uri": "/{REALM}/account/*",
+ "type": "http://acme.com/account",
+ "scopes": [
+ {
+ "name": "urn:acme.com:scopes:account:manage"
+ }
+ ],
+ "typedScopes": []
+ }
+ ],
+ "policies": [
+ {
+ "name": "Acme admin policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"Acme administrator\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Acme viewer policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"Acme viewer\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Tenant user policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"tenant user\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Tenant administrator policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"tenant administrator\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Tenant viewer policy",
+ "type": "role",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "roles": "[{\"id\":\"tenant viewer\",\"required\":true}]"
+ }
+ },
+ {
+ "name": "Any user policy",
+ "description": "Defines that only users from well known clients are allowed to access",
+ "type": "aggregate",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "applyPolicies": "[\"Tenant user policy\",\"Acme admin policy\",\"Acme viewer policy\",\"Tenant viewer policy\",\"Tenant administrator policy\"]"
+ }
+ },
+ {
+ "name": "Super tenant admin permission",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "scopes": "[\"urn:acme.com:scopes:admin:manage\"]",
+ "applyPolicies": "[\"Acme admin policy\"]"
+ }
+ },
+ {
+ "name": "Super tenant admin read permission",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "scopes": "[\"urn:acme.com:scopes:admin:view\"]",
+ "applyPolicies": "[\"Acme admin policy\",\"Acme viewer policy\"]"
+ }
+ },
+ {
+ "name": "View tenant role permission",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "resources": "[\"Role resource\"]",
+ "scopes": "[\"urn:acme.com:scopes:role:view\"]",
+ "applyPolicies": "[\"Any user policy\"]"
+ }
+ },
+ {
+ "name": "Manage account permission",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "resources": "[\"Account resource\"]",
+ "scopes": "[\"urn:acme.com:scopes:account:manage\"]",
+ "applyPolicies": "[\"Any user policy\"]"
+ }
+ },
+ {
+ "name": "Manage user profile permission",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "scopes": "[\"urn:acme.com:scopes:userprofile:manage\"]",
+ "applyPolicies": "[\"Acme admin policy\",\"Tenant administrator policy\"]"
+ }
+ },
+ {
+ "name": "View user profile permission",
+ "type": "scope",
+ "logic": "POSITIVE",
+ "decisionStrategy": "AFFIRMATIVE",
+ "config": {
+ "scopes": "[\"urn:acme.com:scopes:userprofile:view\"]",
+ "applyPolicies": "[\"Acme admin policy\",\"Acme viewer policy\",\"Tenant viewer policy\",\"Tenant administrator policy\"]"
+ }
+ }
+ ],
+ "scopes": [
+ {
+ "name": "urn:acme.com:scopes:admin:manage"
+ },
+ {
+ "name": "urn:acme.com:scopes:admin:view"
+ },
+ {
+ "name": "urn:acme.com:scopes:role:view"
+ },
+ {
+ "name": "urn:acme.com:scopes:account:manage"
+ },
+ {
+ "name": "urn:acme.com:scopes:userprofile:view"
+ },
+ {
+ "name": "urn:acme.com:scopes:userprofile:manage"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak.json
new file mode 100644
index 0000000..7308c71
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak.json
@@ -0,0 +1,8 @@
+{
+ "realm": "authz-test",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "resource" : "resource-server-test",
+ "credentials": {
+ "secret": "secret"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/scripts/client-session-test.js b/testsuite/integration-arquillian/tests/base/src/test/resources/scripts/client-session-test.js
new file mode 100644
index 0000000..07a07a1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/scripts/client-session-test.js
@@ -0,0 +1,21 @@
+AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
+
+function authenticate(context) {
+
+ if (clientSession.getRealm().getName() != "${realm}") {
+ context.failure(AuthenticationFlowError.INVALID_CLIENT_SESSION);
+ return;
+ }
+
+ if (clientSession.getClient().getClientId() != "${clientId}") {
+ context.failure(AuthenticationFlowError.UNKNOWN_CLIENT);
+ return;
+ }
+
+ if (clientSession.getAuthMethod() != "${authMethod}") {
+ context.failure(AuthenticationFlowError.INVALID_CLIENT_SESSION);
+ return;
+ }
+
+ context.success();
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/pom.xml
index 2fd4867..b53010c 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/pom.xml
@@ -34,5 +34,20 @@
<properties>
<app.server>eap</app.server>
</properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.wildfly.extras.creaper</groupId>
+ <artifactId>creaper-core</artifactId>
+ <scope>test</scope>
+ <version>1.5.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly.core</groupId>
+ <artifactId>wildfly-cli</artifactId>
+ <scope>test</scope>
+ <version>2.2.0.Final</version>
+ </dependency>
+ </dependencies>
</project>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/cluster/EAPSAMLAdapterClusterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/cluster/EAPSAMLAdapterClusterTest.java
new file mode 100644
index 0000000..ef5059b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/cluster/EAPSAMLAdapterClusterTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.cluster;
+
+import org.keycloak.testsuite.adapter.page.EmployeeServletDistributable;
+import org.keycloak.testsuite.arquillian.annotation.*;
+
+import java.io.*;
+
+import org.keycloak.testsuite.adapter.servlet.cluster.AbstractSAMLAdapterClusterTest;
+import org.keycloak.testsuite.adapter.servlet.SendUsernameServlet;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.wildfly.extras.creaper.core.*;
+import org.wildfly.extras.creaper.core.online.*;
+import org.wildfly.extras.creaper.core.online.operations.*;
+
+
+/**
+ *
+ * @author hmlnarik
+ */
+@AppServerContainer("app-server-eap")
+public class EAPSAMLAdapterClusterTest extends AbstractSAMLAdapterClusterTest {
+
+ @TargetsContainer(value = "app-server-eap-" + NODE_1_NAME)
+ @Deployment(name = EmployeeServletDistributable.DEPLOYMENT_NAME, managed = false)
+ protected static WebArchive employee() {
+ return samlServletDeployment(EmployeeServletDistributable.DEPLOYMENT_NAME, EmployeeServletDistributable.DEPLOYMENT_NAME + "/WEB-INF/web.xml", SendUsernameServlet.class);
+ }
+
+ @TargetsContainer(value = "app-server-eap-" + NODE_2_NAME)
+ @Deployment(name = EmployeeServletDistributable.DEPLOYMENT_NAME + "_2", managed = false)
+ protected static WebArchive employee2() {
+ return employee();
+ }
+
+ @Override
+ protected void prepareWorkerNode(Integer managementPort) throws IOException, CliException, NumberFormatException {
+ log.infov("Preparing worker node ({0})", managementPort);
+
+ OnlineManagementClient clientWorkerNodeClient = ManagementClient.online(OnlineOptions
+ .standalone()
+ .hostAndPort("localhost", managementPort)
+ .build());
+ Operations op = new Operations(clientWorkerNodeClient);
+
+ Batch b = new Batch();
+ Address tcppingStack = Address
+ .subsystem("jgroups")
+ .and("stack", "tcpping");
+ b.add(tcppingStack);
+ b.add(tcppingStack.and("transport", "TCP"), Values.of("socket-binding", "jgroups-tcp"));
+ b.add(tcppingStack.and("protocol", "TCPPING"));
+ b.add(tcppingStack.and("protocol", "TCPPING").and("property", "initial_hosts"), Values.of("value", "localhost[" + (7600 + PORT_OFFSET_NODE_1) + "],localhost[" + (7600 + PORT_OFFSET_NODE_2) + "]"));
+ b.add(tcppingStack.and("protocol", "TCPPING").and("property", "port_range"), Values.of("value", "0"));
+ b.add(tcppingStack.and("protocol", "TCPPING").and("property", "num_initial_members"), Values.of("value", "2"));
+ b.add(tcppingStack.and("protocol", "TCPPING").and("property", "timeout"), Values.of("value", "3000"));
+ b.add(tcppingStack.and("protocol", "MERGE3"));
+ b.add(tcppingStack.and("protocol", "FD_SOCK"), Values.of("socket-binding", "jgroups-tcp-fd"));
+ b.add(tcppingStack.and("protocol", "FD"));
+ b.add(tcppingStack.and("protocol", "VERIFY_SUSPECT"));
+ b.add(tcppingStack.and("protocol", "pbcast.NAKACK2"));
+ b.add(tcppingStack.and("protocol", "UNICAST3"));
+ b.add(tcppingStack.and("protocol", "pbcast.STABLE"));
+ b.add(tcppingStack.and("protocol", "pbcast.GMS"));
+ b.add(tcppingStack.and("protocol", "MFC"));
+ b.add(tcppingStack.and("protocol", "FRAG2"));
+ b.writeAttribute(Address.subsystem("jgroups").and("channel", "ee"), "stack", "tcpping");
+ op.batch(b);
+
+ op.add(Address.extension("org.keycloak.keycloak-saml-adapter-subsystem"), Values.of("module", "org.keycloak.keycloak-saml-adapter-subsystem"));
+ op.add(Address.subsystem("keycloak-saml"));
+
+ clientWorkerNodeClient.execute("reload");
+
+ log.infov("Worker node ({0}) Prepared", managementPort);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/resources/adapter-test/keycloak-saml/employee-distributable/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/resources/adapter-test/keycloak-saml/employee-distributable/WEB-INF/web.xml
new file mode 100644
index 0000000..b57928f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/resources/adapter-test/keycloak-saml/employee-distributable/WEB-INF/web.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <distributable/>
+
+ <absolute-ordering/>
+
+ <module-name>%CONTEXT_PATH%</module-name>
+
+ <servlet-mapping>
+ <servlet-name>javax.ws.rs.core.Application</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <error-page>
+ <location>/error.html</location>
+ </error-page>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Application</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>manager</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK-SAML</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>manager</role-name>
+ </security-role>
+
+ <context-param>
+ <param-name>keycloak.sessionIdMapperUpdater.classes</param-name>
+ <param-value>org.keycloak.adapters.saml.wildfly.infinispan.InfinispanSessionCacheIdMapperUpdater</param-value>
+ </context-param>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6PermissiveModeAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6PermissiveModeAdapterTest.java
new file mode 100644
index 0000000..c300b51
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6PermissiveModeAdapterTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@RunAsClient
+@AppServerContainer("app-server-eap6")
+public class EAP6PermissiveModeAdapterTest extends AbstractPermissiveModeAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6ServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6ServletAuthzAdapterTest.java
index 5833b29..f7ac21a 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6ServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAP6ServletAuthzAdapterTest.java
@@ -25,6 +25,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
*/
@RunAsClient
@AppServerContainer("app-server-eap6")
-public class EAP6ServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest {
+public class EAP6ServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPermissiveModeAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPermissiveModeAdapterTest.java
new file mode 100644
index 0000000..8c6e0d3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPermissiveModeAdapterTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@RunAsClient
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyPermissiveModeAdapterTest extends AbstractPermissiveModeAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
index 6ff0e1f..13a444f 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java
@@ -17,7 +17,6 @@
package org.keycloak.testsuite.adapter.example.authorization;
import org.jboss.arquillian.container.test.api.RunAsClient;
-import org.keycloak.testsuite.adapter.example.authorization.AbstractServletAuthzAdapterTest;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
/**
@@ -27,6 +26,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
@RunAsClient
@AppServerContainer("app-server-wildfly")
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
-public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest {
+public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java
new file mode 100644
index 0000000..8ad7eb1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletPolicyEnforcerAdapterTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.adapter.example.authorization;
+
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@RunAsClient
+@AppServerContainer("app-server-wildfly")
+//@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyServletPolicyEnforcerAdapterTest extends AbstractServletPolicyEnforcerTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index 9e13566..75a68ea 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -352,6 +352,12 @@
</artifactItem>
<artifactItem>
<groupId>org.keycloak.testsuite</groupId>
+ <artifactId>servlet-policy-enforcer</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
<artifactId>integration-arquillian-test-apps-cors-angular-product</artifactId>
<version>${project.version}</version>
<type>war</type>
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/Authorization.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/Authorization.java
new file mode 100644
index 0000000..5510d7e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/Authorization.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.clients.Client;
+import org.keycloak.testsuite.console.page.clients.authorization.permission.Permissions;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.Policies;
+import org.keycloak.testsuite.console.page.clients.authorization.resource.Resources;
+import org.keycloak.testsuite.console.page.clients.authorization.scope.Scopes;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Authorization extends Client {
+
+ @FindBy(id = "authz-tabs")
+ protected AuthorizationTabLinks authorizationTabLinks;
+
+ @Page
+ private AuthorizationSettingsForm authorizationSettingsForm;
+
+ @Page
+ private Resources resources;
+
+ @Page
+ private Scopes scopes;
+
+ @Page
+ private Permissions permissions;
+
+ @Page
+ private Policies policies;
+
+ public AuthorizationSettingsForm settings() {
+ return authorizationSettingsForm;
+ }
+
+ public AuthorizationTab authorizationTabs() {
+ return new AuthorizationTab(authorizationTabLinks);
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/authz/resource-server";
+ }
+
+ public class AuthorizationTab {
+
+ private final AuthorizationTabLinks links;
+
+ public AuthorizationTab(AuthorizationTabLinks links) {
+ this.links = links;
+ }
+
+ public Resources resources() {
+ links.resources();
+ return resources;
+ }
+
+ public Scopes scopes() {
+ links.scopes();
+ return scopes;
+ }
+
+ public Permissions permissions() {
+ links.permissions();
+ return permissions;
+ }
+
+ public Policies policies() {
+ links.policies();
+ return policies;
+ }
+ }
+
+ public class AuthorizationTabLinks {
+
+ @Root
+ private WebElement root;
+
+ @FindBy(linkText = "Settings")
+ private WebElement settingsLink;
+
+ @FindBy(linkText = "Resources")
+ private WebElement resourcesLink;
+
+ @FindBy(linkText = "Authorization Scopes")
+ private WebElement scopesLink;
+
+ @FindBy(linkText = "Permissions")
+ private WebElement permissionsLink;
+
+ @FindBy(linkText = "Policies")
+ private WebElement policiesLink;
+
+ public void settings() {
+ settingsLink.click();
+ }
+
+ public void resources() {
+ resourcesLink.click();
+ }
+
+ private void scopes() {
+ scopesLink.click();
+ }
+
+ private void permissions() {
+ permissionsLink.click();
+ }
+
+ private void policies() {
+ policiesLink.click();
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/AuthorizationSettingsForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/AuthorizationSettingsForm.java
new file mode 100644
index 0000000..bf6a52b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/AuthorizationSettingsForm.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization;
+
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationSettingsForm extends Form {
+
+ @FindBy(id = "server.policyEnforcementMode")
+ private Select enforcementMode;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='server.allowRemoteResourceManagement']]")
+ private OnOffSwitch allowRemoteResourceManagement;
+
+ public void setEnforcementMode(PolicyEnforcerConfig.EnforcementMode mode) {
+ enforcementMode.selectByValue(mode.name());
+ }
+
+ public PolicyEnforcerConfig.EnforcementMode getEnforcementMode() {
+ return PolicyEnforcerConfig.EnforcementMode.valueOf(enforcementMode.getFirstSelectedOption().getAttribute("value"));
+ }
+
+ public void setAllowRemoteResourceManagement(boolean enable) {
+ allowRemoteResourceManagement.setOn(enable);
+ }
+
+ public boolean isAllowRemoteResourceManagement() {
+ return allowRemoteResourceManagement.isOn();
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java
new file mode 100644
index 0000000..c29ab76
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/Permissions.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.permission;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Permissions extends Form {
+
+ @FindBy(css = "table[class*='table']")
+ private PermissionsTable table;
+
+ public PermissionsTable permissions() {
+ return table;
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/PermissionsTable.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/PermissionsTable.java
new file mode 100644
index 0000000..71b0139
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/permission/PermissionsTable.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.permission;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionsTable extends DataTable {
+
+ public PolicyRepresentation findByName(String name) {
+ search(name);
+ List<PolicyRepresentation> result = getTableRows();
+ if (result.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == result.size();
+ return result.get(0);
+ }
+ }
+
+ public boolean contains(String name) {
+ for (PolicyRepresentation representation : getTableRows()) {
+ if (name.equals(representation.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<PolicyRepresentation> getTableRows() {
+ List<PolicyRepresentation> rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ PolicyRepresentation representation = toRepresentation(row);
+ if (representation != null) {
+ rows.add(representation);
+ }
+ }
+ return rows;
+ }
+
+ public PolicyRepresentation toRepresentation(WebElement row) {
+ PolicyRepresentation representation = null;
+ List<WebElement> tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ representation = new PolicyRepresentation();
+ representation.setName(tds.get(0).getText());
+ representation.setDescription(tds.get(1).getText());
+ representation.setType(tds.get(2).getText());
+ }
+ return representation;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java
new file mode 100644
index 0000000..36a198d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/Policies.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.policy;
+
+import org.keycloak.testsuite.console.page.clients.authorization.permission.PermissionsTable;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Policies extends Form {
+
+ @FindBy(css = "table[class*='table']")
+ private PermissionsTable table;
+
+ public PermissionsTable policies() {
+ return table;
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PoliciesTable.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PoliciesTable.java
new file mode 100644
index 0000000..50b91da
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/policy/PoliciesTable.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.policy;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PoliciesTable extends DataTable {
+
+ public PolicyRepresentation findByName(String name) {
+ search(name);
+ List<PolicyRepresentation> result = getTableRows();
+ if (result.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == result.size();
+ return result.get(0);
+ }
+ }
+
+ public boolean contains(String name) {
+ for (PolicyRepresentation representation : getTableRows()) {
+ if (name.equals(representation.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<PolicyRepresentation> getTableRows() {
+ List<PolicyRepresentation> rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ PolicyRepresentation representation = toRepresentation(row);
+ if (representation != null) {
+ rows.add(representation);
+ }
+ }
+ return rows;
+ }
+
+ public PolicyRepresentation toRepresentation(WebElement row) {
+ PolicyRepresentation representation = null;
+ List<WebElement> tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ representation = new PolicyRepresentation();
+ representation.setName(tds.get(0).getText());
+ representation.setDescription(tds.get(1).getText());
+ representation.setType(tds.get(2).getText());
+ }
+ return representation;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resource.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resource.java
new file mode 100644
index 0000000..aae4992
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.resource;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Resource {
+
+ @Page
+ private ResourceForm form;
+
+ public ResourceForm form() {
+ return form;
+ }
+
+ public ResourceRepresentation toRepresentation() {
+ return form.toRepresentation();
+ }
+
+ public void update(ResourceRepresentation expected) {
+ form().populate(expected);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java
new file mode 100644
index 0000000..8f4a66f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.resource;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceForm extends Form {
+
+ @FindBy(id = "name")
+ private WebElement name;
+
+ @FindBy(id = "type")
+ private WebElement type;
+
+ @FindBy(id = "uri")
+ private WebElement uri;
+
+ @FindBy(id = "iconUri")
+ private WebElement iconUri;
+
+ @FindBy(id = "resource.owner.name")
+ private WebElement owner;
+
+ @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+ private WebElement deleteButton;
+
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+ private WebElement confirmDelete;
+
+ @FindBy(id = "s2id_scopes")
+ private ScopesInput scopesInput;
+
+ public void populate(ResourceRepresentation expected) {
+ setInputValue(name, expected.getName());
+ setInputValue(type, expected.getType());
+ setInputValue(uri, expected.getUri());
+ setInputValue(iconUri, expected.getIconUri());
+
+ Set<ScopeRepresentation> scopes = expected.getScopes();
+
+ for (ScopeRepresentation scope : scopes) {
+ scopesInput.select(scope.getName());
+ }
+
+ Set<ScopeRepresentation> selection = scopesInput.getSelected();
+
+ for (ScopeRepresentation selected : selection) {
+ boolean isSelected = false;
+
+ for (ScopeRepresentation scope : scopes) {
+ if (selected.getName().equals(scope.getName())) {
+ isSelected = true;
+ break;
+ }
+ }
+
+ if (!isSelected) {
+ scopesInput.unSelect(selected.getName(), driver);
+ }
+ }
+
+ save();
+ }
+
+ public void delete() {
+ deleteButton.click();
+ confirmDelete.click();
+ }
+
+ public ResourceRepresentation toRepresentation() {
+ ResourceRepresentation representation = new ResourceRepresentation();
+
+ representation.setName(getInputValue(name));
+ representation.setType(getInputValue(type));
+ representation.setUri(getInputValue(uri));
+ representation.setIconUri(getInputValue(iconUri));
+ representation.setScopes(scopesInput.getSelected());
+
+ return representation;
+ }
+
+ public class ScopesInput {
+
+ @Root
+ private WebElement root;
+
+ @FindBy(xpath = "//input[contains(@class,'select2-input')]")
+ private WebElement search;
+
+ @FindBy(xpath = "//div[contains(@class,'select2-result-label')]")
+ private List<WebElement> result;
+
+ @FindBy(xpath = "//li[contains(@class,'select2-search-choice')]")
+ private List<WebElement> selection;
+
+ public void select(String name) {
+ setInputValue(search, name);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ for (WebElement result : result) {
+ if (result.getText().equalsIgnoreCase(name)) {
+ result.click();
+ return;
+ }
+ }
+ }
+
+ public Set<ScopeRepresentation> getSelected() {
+ HashSet<ScopeRepresentation> values = new HashSet<>();
+
+ for (WebElement selected : selection) {
+ values.add(new ScopeRepresentation(selected.findElements(By.tagName("div")).get(0).getText()));
+ }
+
+ return values;
+ }
+
+ public void unSelect(String name, WebDriver driver) {
+ for (WebElement selected : selection) {
+ if (name.equals(selected.findElements(By.tagName("div")).get(0).getText())) {
+ WebElement element = selected.findElement(By.xpath("//a[contains(@class,'select2-search-choice-close')]"));
+ JavascriptExecutor executor = (JavascriptExecutor) driver;
+ executor.executeScript("arguments[0].click();", element);
+ WaitUtils.pause(1000);
+ return;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resources.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resources.java
new file mode 100644
index 0000000..280af3f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/Resources.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.resource;
+
+import static org.openqa.selenium.By.tagName;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Resources extends Form {
+
+ @FindBy(css = "table[class*='table']")
+ private ResourcesTable table;
+
+ @FindBy(linkText = "Create")
+ private WebElement create;
+
+ @Page
+ private Resource resource;
+
+ public ResourcesTable resources() {
+ return table;
+ }
+
+ public void create(ResourceRepresentation representation) {
+ create.click();
+ resource.form().populate(representation);
+ }
+
+ public void update(String name, ResourceRepresentation representation) {
+ for (WebElement row : resources().rows()) {
+ ResourceRepresentation actual = resources().toRepresentation(row);
+ if (actual.getName().equalsIgnoreCase(name)) {
+ row.findElements(tagName("a")).get(0).click();
+ WaitUtils.waitForPageToLoad(driver);
+ resource.form().populate(representation);
+ return;
+ }
+ }
+ }
+
+ public void delete(String name) {
+ for (WebElement row : resources().rows()) {
+ ResourceRepresentation actual = resources().toRepresentation(row);
+ if (actual.getName().equalsIgnoreCase(name)) {
+ row.findElements(tagName("a")).get(0).click();
+ WaitUtils.waitForPageToLoad(driver);
+ resource.form().delete();
+ return;
+ }
+ }
+ }
+
+ public Resource name(String name) {
+ for (WebElement row : resources().rows()) {
+ ResourceRepresentation actual = resources().toRepresentation(row);
+ if (actual.getName().equalsIgnoreCase(name)) {
+ row.findElements(tagName("a")).get(0).click();
+ WaitUtils.waitForPageToLoad(driver);
+ return resource;
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourcesTable.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourcesTable.java
new file mode 100644
index 0000000..f5dc5a7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourcesTable.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.resource;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourcesTable extends DataTable {
+
+ public ResourceRepresentation findByName(String name) {
+ search(name);
+ List<ResourceRepresentation> result = getTableRows();
+ if (result.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == result.size();
+ return result.get(0);
+ }
+ }
+
+ public boolean contains(String name) {
+ for (ResourceRepresentation representation : getTableRows()) {
+ if (name.equals(representation.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<ResourceRepresentation> getTableRows() {
+ List<ResourceRepresentation> rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ ResourceRepresentation representation = toRepresentation(row);
+ if (representation != null) {
+ rows.add(representation);
+ }
+ }
+ return rows;
+ }
+
+ public ResourceRepresentation toRepresentation(WebElement row) {
+ ResourceRepresentation representation = null;
+ List<WebElement> tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ representation = new ResourceRepresentation();
+ representation.setName(tds.get(0).getText());
+ representation.setType(tds.get(1).getText());
+ representation.setUri(tds.get(2).getText());
+ ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
+ owner.setName(tds.get(3).getText());
+ representation.setOwner(owner);
+ }
+ return representation;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java
new file mode 100644
index 0000000..5137125
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.scope;
+
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Scope {
+
+ @Page
+ private ScopeForm form;
+
+ public ScopeForm form() {
+ return form;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java
new file mode 100644
index 0000000..ed01a2b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.scope;
+
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeForm extends Form {
+
+ @FindBy(id = "name")
+ private WebElement name;
+
+ @FindBy(id = "iconUri")
+ private WebElement iconUri;
+
+ @FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
+ private WebElement deleteButton;
+
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
+ private WebElement confirmDelete;
+
+ public void populate(ScopeRepresentation expected) {
+ setInputValue(name, expected.getName());
+ setInputValue(iconUri, expected.getIconUri());
+ save();
+ }
+
+ public void delete() {
+ deleteButton.click();
+ confirmDelete.click();
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java
new file mode 100644
index 0000000..a59869c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.scope;
+
+import static org.openqa.selenium.By.tagName;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class Scopes extends Form {
+
+ @FindBy(css = "table[class*='table']")
+ private ScopesTable table;
+
+ @FindBy(linkText = "Create")
+ private WebElement create;
+
+ @Page
+ private Scope scope;
+
+ public ScopesTable scopes() {
+ return table;
+ }
+
+ public void create(ScopeRepresentation representation) {
+ create.click();
+ scope.form().populate(representation);
+ }
+
+ public void update(String name, ScopeRepresentation representation) {
+ for (WebElement row : scopes().rows()) {
+ ScopeRepresentation actual = scopes().toRepresentation(row);
+ if (actual.getName().equalsIgnoreCase(name)) {
+ row.findElements(tagName("a")).get(0).click();
+ scope.form().populate(representation);
+ }
+ }
+ }
+
+ public void delete(String name) {
+ for (WebElement row : scopes().rows()) {
+ ScopeRepresentation actual = scopes().toRepresentation(row);
+ if (actual.getName().equalsIgnoreCase(name)) {
+ row.findElements(tagName("a")).get(0).click();
+ scope.form().delete();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopesTable.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopesTable.java
new file mode 100644
index 0000000..3372cd3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopesTable.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.page.clients.authorization.scope;
+
+import static org.openqa.selenium.By.tagName;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopesTable extends DataTable {
+
+ public ScopeRepresentation findByName(String name) {
+ search(name);
+ List<ScopeRepresentation> result = getTableRows();
+ if (result.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == result.size();
+ return result.get(0);
+ }
+ }
+
+ public boolean contains(String name) {
+ for (ScopeRepresentation representation : getTableRows()) {
+ if (name.equals(representation.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<ScopeRepresentation> getTableRows() {
+ List<ScopeRepresentation> rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ ScopeRepresentation representation = toRepresentation(row);
+ if (representation != null) {
+ rows.add(representation);
+ }
+ }
+ return rows;
+ }
+
+ public ScopeRepresentation toRepresentation(WebElement row) {
+ ScopeRepresentation representation = null;
+ List<WebElement> tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ representation = new ScopeRepresentation();
+ representation.setName(tds.get(0).getText());
+ }
+ return representation;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
index a6ce3e5..39999ba 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
@@ -76,6 +76,8 @@ public class Client extends Clients {
private WebElement installationLink;
@FindBy(linkText = "Service Account Roles")
private WebElement serviceAccountRoles;
+ @FindBy(linkText = "Authorization")
+ private WebElement authorizationLink;
public void settings() {
settingsLink.click();
@@ -104,6 +106,10 @@ public class Client extends Clients {
public void installation() {
installationLink.click();
}
+
+ public void authorization() {
+ authorizationLink.click();
+ }
public boolean isServiceAccountRolesDisplayed() {
try {
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java
index a1d225a..57f789e 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java
@@ -67,6 +67,12 @@ public class ClientSettingsForm extends CreateClientForm {
@FindBy(xpath = ".//button[contains(@data-ng-click, 'deleteWebOrigin')]")
private List<WebElement> deleteWebOriginIcons;
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='authorizationServicesEnabled']]")
+ private OnOffSwitch authorizationSettingsEnabledSwitch;
+
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Disable Authorization Settings']")
+ private WebElement confirmDisableAuthorizationSettingsButton;
+
public enum OidcAccessType {
BEARER_ONLY("bearer-only"), PUBLIC("public"), CONFIDENTIAL("confidential");
@@ -212,6 +218,18 @@ public class ClientSettingsForm extends CreateClientForm {
serviceAccountsEnabledSwitch.setOn(serviceAccountsEnabled);
}
+ public void setAuthorizationSettingsEnabled(boolean enabled) {
+ authorizationSettingsEnabledSwitch.setOn(enabled);
+ }
+
+ public boolean isAuthorizationSettingsEnabled() {
+ return authorizationSettingsEnabledSwitch.isOn();
+ }
+
+ public void confirmDisableAuthorizationSettings() {
+ confirmDisableAuthorizationSettingsButton.click();
+ }
+
public class SAMLClientSettingsForm extends Form {
public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java
new file mode 100644
index 0000000..5bb5449
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/AbstractAuthorizationSettingsTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.authorization;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.auth.page.login.Login.OIDC;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.testsuite.console.clients.AbstractClientTest;
+import org.keycloak.testsuite.console.page.clients.authorization.Authorization;
+import org.keycloak.testsuite.console.page.clients.settings.ClientSettings;
+import org.openqa.selenium.By;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractAuthorizationSettingsTest extends AbstractClientTest {
+
+ @Page
+ protected ClientSettings clientSettingsPage;
+
+ @Page
+ protected Authorization authorizationPage;
+
+ protected ClientRepresentation newClient;
+
+ @Before
+ public void configureTest() {
+ this.newClient = createResourceServer();
+ }
+
+ private ClientRepresentation createResourceServer() {
+ ClientRepresentation newClient = createClientRep("oidc-confidetial", OIDC);
+
+ createClient(newClient);
+
+ newClient.setRedirectUris(TEST_REDIRECT_URIs);
+ newClient.setAuthorizationServicesEnabled(true);
+
+ clientSettingsPage.form().setRedirectUris(TEST_REDIRECT_URIs);
+ clientSettingsPage.form().setAuthorizationSettingsEnabled(true);
+ clientSettingsPage.form().save();
+ assertAlertSuccess();
+
+ ClientRepresentation found = findClientByClientId(newClient.getClientId());
+ assertNotNull("Client " + newClient.getClientId() + " was not found.", found);
+
+ newClient.setPublicClient(false);
+ newClient.setServiceAccountsEnabled(true);
+
+ assertClientSettingsEqual(newClient, found);
+ assertTrue(clientSettingsPage.tabs().getTabs().findElement(By.linkText("Authorization")).isDisplayed());
+
+ clientSettingsPage.setId(found.getId());
+ clientSettingsPage.navigateTo();
+ authorizationPage.setId(found.getId());
+
+ clientSettingsPage.tabs().authorization();
+ assertTrue(authorizationPage.isCurrent());
+
+ return newClient;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DefaultAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DefaultAuthorizationSettingsTest.java
new file mode 100644
index 0000000..f4b5b68
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DefaultAuthorizationSettingsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.AuthorizationSettingsForm;
+import org.keycloak.testsuite.console.page.clients.authorization.permission.Permissions;
+import org.keycloak.testsuite.console.page.clients.authorization.policy.Policies;
+import org.keycloak.testsuite.console.page.clients.authorization.resource.Resources;
+import org.keycloak.testsuite.console.page.clients.authorization.scope.Scopes;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DefaultAuthorizationSettingsTest extends AbstractAuthorizationSettingsTest {
+
+ @Test
+ public void testDefaultSettings() {
+ AuthorizationSettingsForm settings = authorizationPage.settings();
+
+ assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING, settings.getEnforcementMode());
+ assertEquals(false, settings.isAllowRemoteResourceManagement());
+
+ Resources resources = authorizationPage.authorizationTabs().resources();
+ ResourceRepresentation resource = resources.resources().findByName("Default Resource");
+
+ assertNotNull(resource);
+ assertEquals("urn:oidc-confidetial:resources:default", resource.getType());
+ assertEquals("/*", resource.getUri());
+ assertEquals(newClient.getClientId(), resource.getOwner().getName());
+
+ Scopes scopes = authorizationPage.authorizationTabs().scopes();
+
+ assertTrue(scopes.scopes().getTableRows().isEmpty());
+
+ Permissions permissions = authorizationPage.authorizationTabs().permissions();
+ PolicyRepresentation permission = permissions.permissions().findByName("Default Permission");
+
+ assertNotNull(permission);
+ assertEquals("resource", permission.getType());
+
+ Policies policies = authorizationPage.authorizationTabs().policies();
+ PolicyRepresentation policy = policies.policies().findByName("Default Policy");
+
+ assertNotNull(policy);
+ assertEquals("js", policy.getType());
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java
new file mode 100644
index 0000000..3e3359e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/DisableAuthorizationSettingsTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.authorization;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class DisableAuthorizationSettingsTest extends AbstractAuthorizationSettingsTest {
+
+ @Test
+ public void testDisableAuthorization() throws InterruptedException {
+ clientSettingsPage.navigateTo();
+ clientSettingsPage.form().setAuthorizationSettingsEnabled(false);
+ clientSettingsPage.form().confirmDisableAuthorizationSettings();
+ Thread.sleep(1000);
+ clientSettingsPage.form().save();
+ assertAlertSuccess();
+
+ clientSettingsPage.navigateTo();
+ assertFalse(clientSettingsPage.form().isAuthorizationSettingsEnabled());
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java
new file mode 100644
index 0000000..75a479a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.resource.Resource;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ResourceManagementTest extends AbstractAuthorizationSettingsTest {
+
+ @Before
+ @Override
+ public void configureTest() {
+ super.configureTest();
+ for (String scopeName : Arrays.asList("Scope A", "Scope B", "Scope C")) {
+ authorizationPage.navigateTo();
+ authorizationPage.authorizationTabs().scopes().create(new ScopeRepresentation(scopeName));
+ }
+ authorizationPage.navigateTo();
+ }
+
+ @Test
+ public void testUpdate() {
+ ResourceRepresentation expected = createResource();
+ String previousName = expected.getName();
+
+ expected.setName("changed");
+ expected.setType("changed");
+ expected.setUri("changed");
+ expected.setScopes(Arrays.asList("Scope A", "Scope B", "Scope C").stream().map(name -> new ScopeRepresentation(name)).collect(Collectors.toSet()));
+
+ authorizationPage.navigateTo();
+ Resource resource = authorizationPage.authorizationTabs().resources().name(previousName);
+ resource.update(expected);
+ assertAlertSuccess();
+ assertResource(expected);
+
+ expected.setScopes(expected.getScopes().stream().filter(scope -> scope.getName().equals("Scope C")).collect(Collectors.toSet()));
+
+ authorizationPage.navigateTo();
+ authorizationPage.authorizationTabs().resources().update(expected.getName(), expected);
+ assertAlertSuccess();
+ assertResource(expected);
+ }
+
+ @Test
+ public void testDelete() {
+ ResourceRepresentation expected = createResource();
+ authorizationPage.navigateTo();
+ authorizationPage.authorizationTabs().resources().delete(expected.getName());
+ authorizationPage.navigateTo();
+ assertNull(authorizationPage.authorizationTabs().resources().resources().findByName(expected.getName()));
+ }
+
+ private ResourceRepresentation createResource() {
+ ResourceRepresentation expected = new ResourceRepresentation();
+
+ expected.setName("Test Resource");
+ expected.setType("Test Type");
+ expected.setUri("/test/resource");
+
+ authorizationPage.authorizationTabs().resources().create(expected);
+ assertAlertSuccess();
+
+ return expected;
+ }
+
+ private void assertResource(ResourceRepresentation expected) {
+ authorizationPage.navigateTo();
+ ResourceRepresentation actual = authorizationPage.authorizationTabs().resources().resources().findByName(expected.getName());
+
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getType(), actual.getType());
+ assertEquals(expected.getUri(), actual.getUri());
+ assertEquals(expected.getIconUri(), actual.getIconUri());
+
+ ResourceRepresentation resource = authorizationPage.authorizationTabs().resources().name(expected.getName()).toRepresentation();
+ Set<ScopeRepresentation> associatedScopes = resource.getScopes();
+
+ if (expected.getScopes() != null) {
+ assertNotNull(associatedScopes);
+ assertEquals(expected.getScopes().size(), associatedScopes.size());
+ assertEquals(0, resource.getScopes().stream().filter(actualScope -> !expected.getScopes().stream()
+ .filter(expectedScope -> actualScope.getName().equals(expectedScope.getName()))
+ .findFirst().isPresent())
+ .count());
+ } else {
+ assertTrue(associatedScopes.isEmpty());
+ }
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java
new file mode 100644
index 0000000..84a5c42
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.console.authorization;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ScopeManagementTest extends AbstractAuthorizationSettingsTest {
+
+ @Test
+ public void testUpdate() {
+ ScopeRepresentation expected = createScope();
+ String previousName = expected.getName();
+
+ expected.setName("changed");
+
+ authorizationPage.navigateTo();
+ authorizationPage.authorizationTabs().scopes().update(previousName, expected);
+ assertAlertSuccess();
+ assertScope(expected);
+ }
+
+ @Test
+ public void testDelete() {
+ ScopeRepresentation expected = createScope();
+ authorizationPage.navigateTo();
+ authorizationPage.authorizationTabs().scopes().delete(expected.getName());
+ authorizationPage.navigateTo();
+ assertNull(authorizationPage.authorizationTabs().scopes().scopes().findByName(expected.getName()));
+ }
+
+ private ScopeRepresentation createScope() {
+ ScopeRepresentation expected = new ScopeRepresentation();
+
+ expected.setName("Test Scope");
+
+ authorizationPage.authorizationTabs().scopes().create(expected);
+ assertAlertSuccess();
+
+ return expected;
+ }
+
+ private void assertScope(ScopeRepresentation expected) {
+ authorizationPage.navigateTo();
+ ScopeRepresentation actual = authorizationPage.authorizationTabs().scopes().scopes().findByName(expected.getName());
+
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getIconUri(), actual.getIconUri());
+ }
+}
diff --git a/themes/src/main/resources/theme/base/admin/index.ftl b/themes/src/main/resources/theme/base/admin/index.ftl
index 82150bc..7717904 100755
--- a/themes/src/main/resources/theme/base/admin/index.ftl
+++ b/themes/src/main/resources/theme/base/admin/index.ftl
@@ -17,6 +17,7 @@
<script type="text/javascript">
var authUrl = '${authUrl}';
+ var consoleBaseUrl = '${consoleBaseUrl}';
var resourceUrl = '${resourceUrl}';
var masterRealm = '${masterRealm}';
</script>
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index ad0b5e9..c4a134b 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1,10 +1,5 @@
'use strict';
-var consoleBaseUrl = window.location.href;
-consoleBaseUrl = consoleBaseUrl.substring(0, consoleBaseUrl.indexOf("/console"));
-consoleBaseUrl = consoleBaseUrl + "/console";
-var configUrl = consoleBaseUrl + "/config";
-
var auth = {};
var resourceBundle;
var locale = 'en';
@@ -14,11 +9,11 @@ var resourceRequests = 0;
var loadingTimer = -1;
angular.element(document).ready(function () {
- var keycloakAuth = new Keycloak(configUrl);
+ var keycloakAuth = new Keycloak(consoleBaseUrl + 'config');
function whoAmI(success, error) {
var req = new XMLHttpRequest();
- req.open('GET', consoleBaseUrl + "/whoami", true);
+ req.open('GET', consoleBaseUrl + 'whoami', true);
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Authorization', 'bearer ' + keycloakAuth.token);
@@ -38,7 +33,7 @@ angular.element(document).ready(function () {
function loadResourceBundle(success, error) {
var req = new XMLHttpRequest();
- req.open('GET', consoleBaseUrl + '/messages.json?lang=' + locale, true);
+ req.open('GET', consoleBaseUrl + 'messages.json?lang=' + locale, true);
req.setRequestHeader('Accept', 'application/json');
req.onreadystatechange = function () {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index e40fcec..ed5620c 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -646,7 +646,7 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
};
});
-module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
+module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPermission, PolicyProvider, client) {
$scope.realm = realm;
$scope.client = client;
$scope.policyProviders = [];
@@ -654,7 +654,6 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
$scope.query = {
realm: realm.realm,
client : client.id,
- permission: true,
max : 20,
first : 0
};
@@ -705,7 +704,7 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
$scope.searchQuery = function() {
$scope.searchLoaded = false;
- ResourceServerPolicy.query($scope.query, function(data) {
+ ResourceServerPermission.query($scope.query, function(data) {
$scope.policies = data;
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
@@ -723,7 +722,7 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
policy.details = {loaded: false};
- ResourceServerPolicy.associatedPolicies({
+ ResourceServerPermission.associatedPolicies({
realm : $route.current.params.realm,
client : client.id,
id : policy.id
@@ -758,7 +757,7 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http
policy = $scope.policy;
}
- $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/resolveModules'
+ $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/provider/resolveModules'
, policy).success(function(data) {
$scope.drools.moduleNames = data;
$scope.resolveSessions();
@@ -766,7 +765,7 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http
}
$scope.resolveSessions = function() {
- $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/resolveSessions'
+ $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/provider/resolveSessions'
, $scope.policy).success(function(data) {
$scope.drools.moduleSessions = data;
});
@@ -789,7 +788,7 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http
}, realm, client, $scope);
});
-module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $route, $location, realm, client, PolicyController, ResourceServerPolicy, ResourceServerResource) {
+module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $route, $location, realm, client, PolicyController, ResourceServerPermission, ResourceServerResource) {
PolicyController.onInit({
getPolicyType : function() {
return "resource";
@@ -848,7 +847,7 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
max : 20,
first : 0
};
- ResourceServerPolicy.query($scope.query, function(response) {
+ ResourceServerPermission.searchPolicies($scope.query, function(response) {
data.results = response;
query.callback(data);
});
@@ -860,59 +859,89 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
};
$scope.applyToResourceType = function() {
- if ($scope.policy.config.default) {
- $scope.policy.config.resources = [];
+ if ($scope.applyToResourceTypeFlag) {
+ $scope.selectedResource = null;
} else {
- $scope.policy.config.defaultResourceType = null;
+ $scope.policy.resourceType = null;
}
}
},
onInitUpdate : function(policy) {
- policy.config.default = eval(policy.config.default);
- policy.config.resources = {};
- ResourceServerPolicy.resources({
- realm : $route.current.params.realm,
- client : client.id,
- id : policy.id
- }, function(resources) {
- resources[0].text = resources[0].name;
- $scope.policy.config.resources = resources[0];
- });
+ if (!policy.resourceType) {
+ $scope.selectedResource = {};
+ ResourceServerPermission.resources({
+ realm: $route.current.params.realm,
+ client: client.id,
+ id: policy.id
+ }, function (resources) {
+ resources[0].text = resources[0].name;
+ $scope.selectedResource = resources[0];
+ var copy = angular.copy($scope.selectedResource);
+ $scope.$watch('selectedResource', function() {
+ if (!angular.equals($scope.selectedResource, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
+ });
+ } else {
+ $scope.applyToResourceTypeFlag = true;
+ }
- policy.config.applyPolicies = [];
- ResourceServerPolicy.associatedPolicies({
+ $scope.selectedPolicies = [];
+ ResourceServerPermission.associatedPolicies({
realm : $route.current.params.realm,
client : client.id,
id : policy.id
}, function(policies) {
for (i = 0; i < policies.length; i++) {
policies[i].text = policies[i].name;
- $scope.policy.config.applyPolicies.push(policies[i]);
+ $scope.selectedPolicies.push(policies[i]);
+ var copy = angular.copy($scope.selectedPolicies);
+ $scope.$watch('selectedPolicies', function() {
+ if (!angular.equals($scope.selectedPolicies, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
}
});
},
onUpdate : function() {
- if ($scope.policy.config.resources && $scope.policy.config.resources._id) {
- $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ if ($scope.selectedResource && $scope.selectedResource._id) {
+ $scope.policy.resources = [];
+ $scope.policy.resources.push($scope.selectedResource._id);
} else {
- delete $scope.policy.config.resources
+ delete $scope.policy.resources
}
var policies = [];
- for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
- policies.push($scope.policy.config.applyPolicies[i].id);
+ for (i = 0; i < $scope.selectedPolicies.length; i++) {
+ policies.push($scope.selectedPolicies[i].id);
}
- $scope.policy.config.applyPolicies = JSON.stringify(policies);
+ $scope.policy.policies = policies;
+ delete $scope.policy.config;
},
onInitCreate : function(newPolicy) {
newPolicy.decisionStrategy = 'UNANIMOUS';
- newPolicy.config = {};
- newPolicy.config.resources = null;
+ $scope.selectedResource = null;
+ var copy = angular.copy($scope.selectedResource);
+ $scope.$watch('selectedResource', function() {
+ if (!angular.equals($scope.selectedResource, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
+
+ $scope.selectedPolicies = null;
+ var copy = angular.copy($scope.selectedPolicies);
+ $scope.$watch('selectedPolicies', function() {
+ if (!angular.equals($scope.selectedPolicies, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
var resourceId = $location.search()['rsrid'];
@@ -923,25 +952,27 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
rsrid : resourceId
}, function(data) {
data.text = data.name;
- $scope.policy.config.resources = data;
+ $scope.selectedResource = data;
});
}
},
onCreate : function() {
- if ($scope.policy.config.resources && $scope.policy.config.resources._id) {
- $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ if ($scope.selectedResource && $scope.selectedResource._id) {
+ $scope.policy.resources = [];
+ $scope.policy.resources.push($scope.selectedResource._id);
} else {
- delete $scope.policy.config.resources
+ delete $scope.policy.resources
}
var policies = [];
- for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
- policies.push($scope.policy.config.applyPolicies[i].id);
+ for (i = 0; i < $scope.selectedPolicies.length; i++) {
+ policies.push($scope.selectedPolicies[i].id);
}
- $scope.policy.config.applyPolicies = JSON.stringify(policies);
+ $scope.policy.policies = policies;
+ delete $scope.policy.config;
}
}, realm, client, $scope);
});
@@ -1046,14 +1077,14 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
};
$scope.selectResource = function() {
- $scope.policy.config.scopes = null;
- if ($scope.policy.config.resources) {
+ $scope.selectedScopes = null;
+ if ($scope.selectedResource) {
ResourceServerResource.scopes({
realm: $route.current.params.realm,
client: client.id,
- rsrid: $scope.policy.config.resources._id
+ rsrid: $scope.selectedResource._id
}, function (data) {
- $scope.policy.config.resources.scopes = data;
+ $scope.resourceScopes = data;
});
}
}
@@ -1079,88 +1110,118 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
deep: false
}, function (resource) {
resource[0].text = resource[0].name;
- $scope.policy.config.resources = resource[0];
+ $scope.selectedResource = resource[0];
+ var copy = angular.copy($scope.selectedResource);
+ $scope.$watch('selectedResource', function() {
+ if (!angular.equals($scope.selectedResource, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
ResourceServerResource.scopes({
realm: $route.current.params.realm,
client: client.id,
rsrid: resource[0]._id
}, function (scopes) {
- $scope.policy.config.resources.scopes = scopes;
- });
- ResourceServerPolicy.scopes({
- realm: $route.current.params.realm,
- client: client.id,
- id: policy.id
- }, function (scopes) {
- $scope.policy.config.scopes = [];
- for (i = 0; i < scopes.length; i++) {
- $scope.policy.config.scopes.push(scopes[i].id);
- }
+ $scope.resourceScopes = scopes;
+ ResourceServerPolicy.scopes({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : policy.id
+ }, function(scopes) {
+ $scope.selectedScopes = [];
+ for (i = 0; i < scopes.length; i++) {
+ scopes[i].text = scopes[i].name;
+ $scope.selectedScopes.push(scopes[i].id);
+ }
+ var copy = angular.copy($scope.selectedScopes);
+ $scope.$watch('selectedScopes', function() {
+ if (!angular.equals($scope.selectedScopes, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
+ });
});
});
});
}
} else {
- $scope.policy.config.resources = null;
+ $scope.selectedResource = null;
+ var copy = angular.copy($scope.selectedResource);
+ $scope.$watch('selectedResource', function() {
+ if (!angular.equals($scope.selectedResource, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
ResourceServerPolicy.scopes({
realm : $route.current.params.realm,
client : client.id,
id : policy.id
}, function(scopes) {
- $scope.policy.config.scopes = [];
+ $scope.selectedScopes = [];
for (i = 0; i < scopes.length; i++) {
scopes[i].text = scopes[i].name;
- $scope.policy.config.scopes.push(scopes[i]);
+ $scope.selectedScopes.push(scopes[i]);
}
+ var copy = angular.copy($scope.selectedScopes);
+ $scope.$watch('selectedScopes', function() {
+ if (!angular.equals($scope.selectedScopes, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
});
}
});
- policy.config.applyPolicies = [];
ResourceServerPolicy.associatedPolicies({
realm : $route.current.params.realm,
client : client.id,
id : policy.id
}, function(policies) {
+ $scope.selectedPolicies = [];
for (i = 0; i < policies.length; i++) {
policies[i].text = policies[i].name;
- $scope.policy.config.applyPolicies.push(policies[i]);
+ $scope.selectedPolicies.push(policies[i]);
}
+ var copy = angular.copy($scope.selectedPolicies);
+ $scope.$watch('selectedPolicies', function() {
+ if (!angular.equals($scope.selectedPolicies, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
});
},
onUpdate : function() {
- if ($scope.policy.config.resources != null) {
- $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ if ($scope.selectedResource != null) {
+ $scope.policy.resources = [$scope.selectedResource._id];
} else {
- $scope.policy.config.resources = JSON.stringify([""]);
+ delete $scope.policy.resources;
}
var scopes = [];
- for (i = 0; i < $scope.policy.config.scopes.length; i++) {
- if ($scope.policy.config.scopes[i].id) {
- scopes.push($scope.policy.config.scopes[i].id);
+ for (i = 0; i < $scope.selectedScopes.length; i++) {
+ if ($scope.selectedScopes[i].id) {
+ scopes.push($scope.selectedScopes[i].id);
} else {
- scopes.push($scope.policy.config.scopes[i]);
+ scopes.push($scope.selectedScopes[i]);
}
}
- $scope.policy.config.scopes = JSON.stringify(scopes);
+ $scope.policy.scopes = scopes;
var policies = [];
- for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
- policies.push($scope.policy.config.applyPolicies[i].id);
+ for (i = 0; i < $scope.selectedPolicies.length; i++) {
+ policies.push($scope.selectedPolicies[i].id);
}
- $scope.policy.config.applyPolicies = JSON.stringify(policies);
+ $scope.policy.policies = policies;
+ delete $scope.policy.config;
},
onInitCreate : function(newPolicy) {
newPolicy.decisionStrategy = 'UNANIMOUS';
- newPolicy.config = {};
- newPolicy.config.resources = null;
var scopeId = $location.search()['scpid'];
@@ -1171,38 +1232,39 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
id: scopeId,
}, function (data) {
data.text = data.name;
- if (!$scope.policy.config.scopes) {
- $scope.policy.config.scopes = [];
+ if (!$scope.policy.scopes) {
+ $scope.selectedScopes = [];
}
- $scope.policy.config.scopes.push(data);
+ $scope.selectedScopes.push(data);
});
}
},
onCreate : function() {
- if ($scope.policy.config.resources != null) {
- $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]);
+ if ($scope.selectedResource != null) {
+ $scope.policy.resources = [$scope.selectedResource._id];
}
var scopes = [];
- for (i = 0; i < $scope.policy.config.scopes.length; i++) {
- if ($scope.policy.config.scopes[i].id) {
- scopes.push($scope.policy.config.scopes[i].id);
+ for (i = 0; i < $scope.selectedScopes.length; i++) {
+ if ($scope.selectedScopes[i].id) {
+ scopes.push($scope.selectedScopes[i].id);
} else {
- scopes.push($scope.policy.config.scopes[i]);
+ scopes.push($scope.selectedScopes[i]);
}
}
- $scope.policy.config.scopes = JSON.stringify(scopes);
+ $scope.policy.scopes = scopes;
var policies = [];
- for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) {
- policies.push($scope.policy.config.applyPolicies[i].id);
+ for (i = 0; i < $scope.selectedPolicies.length; i++) {
+ policies.push($scope.selectedPolicies[i].id);
}
- $scope.policy.config.applyPolicies = JSON.stringify(policies);
+ $scope.policy.policies = policies;
+ delete $scope.policy.config;
}
}, realm, client, $scope);
});
@@ -1264,8 +1326,8 @@ module.controller('ResourceServerPolicyUserDetailCtrl', function($scope, $route,
onInitUpdate : function(policy) {
var selectedUsers = [];
- if (policy.config.users) {
- var users = eval(policy.config.users);
+ if (policy.users) {
+ var users = policy.users;
for (i = 0; i < users.length; i++) {
User.get({realm: $route.current.params.realm, userId: users[i]}, function(data) {
@@ -1289,7 +1351,8 @@ module.controller('ResourceServerPolicyUserDetailCtrl', function($scope, $route,
users.push($scope.selectedUsers[i].id);
}
- $scope.policy.config.users = JSON.stringify(users);
+ $scope.policy.users = users;
+ delete $scope.policy.config;
},
onCreate : function() {
@@ -1299,7 +1362,8 @@ module.controller('ResourceServerPolicyUserDetailCtrl', function($scope, $route,
users.push($scope.selectedUsers[i].id);
}
- $scope.policy.config.users = JSON.stringify(users);
+ $scope.policy.users = users;
+ delete $scope.policy.config;
}
}, realm, client, $scope);
});
@@ -1483,8 +1547,8 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
onInitUpdate : function(policy) {
var selectedRoles = [];
- if (policy.config.roles) {
- var roles = eval(policy.config.roles);
+ if (policy.roles) {
+ var roles = policy.roles;
for (i = 0; i < roles.length; i++) {
RoleById.get({realm: $route.current.params.realm, role: roles[i].id}, function(data) {
@@ -1524,7 +1588,7 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
roles.push(role);
}
- $scope.policy.config.roles = JSON.stringify(roles);
+ $scope.policy.roles = roles;
},
onCreate : function() {
@@ -1539,7 +1603,7 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
roles.push(role);
}
- $scope.policy.config.roles = JSON.stringify(roles);
+ $scope.policy.roles = roles;
}
}, realm, client, $scope);
@@ -1743,7 +1807,7 @@ module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $r
}, realm, client, $scope);
});
-module.service("PolicyController", function($http, $route, $location, ResourceServer, ResourceServerPolicy, AuthzDialog, Notifications) {
+module.service("PolicyController", function($http, $route, $location, ResourceServer, ResourceServerPolicy, ResourceServerPermission, AuthzDialog, Notifications) {
var PolicyController = {};
@@ -1754,6 +1818,12 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
}
}
+ var service = ResourceServerPolicy;
+
+ if (delegate.isPermission()) {
+ service = ResourceServerPermission;
+ }
+
$scope.realm = realm;
$scope.client = client;
@@ -1799,7 +1869,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
if (delegate.onCreate) {
delegate.onCreate();
}
- ResourceServerPolicy.save({realm : realm.realm, client : client.id}, $scope.policy, function(data) {
+ service.save({realm : realm.realm, client : client.id, type: $scope.policy.type}, $scope.policy, function(data) {
if (delegate.isPermission()) {
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + $scope.policy.type + "/" + data.id);
Notifications.success("The permission has been created.");
@@ -1819,9 +1889,10 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
}
}
} else {
- ResourceServerPolicy.get({
+ service.get({
realm: realm.realm,
client : client.id,
+ type: delegate.getPolicyType(),
id: $route.current.params.id
}, function(data) {
$scope.originalPolicy = data;
@@ -1845,7 +1916,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
if (delegate.onUpdate) {
delegate.onUpdate();
}
- ResourceServerPolicy.update({realm : realm.realm, client : client.id, id : $scope.policy.id}, $scope.policy, function() {
+ service.update({realm : realm.realm, client : client.id, type: $scope.policy.type, id : $scope.policy.id}, $scope.policy, function() {
$route.reload();
if (delegate.isPermission()) {
Notifications.success("The permission has been updated.");
@@ -1871,7 +1942,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
$scope.remove = function() {
var msg = "";
- ResourceServerPolicy.dependentPolicies({
+ service.dependentPolicies({
realm : $route.current.params.realm,
client : client.id,
id : $scope.policy.id
@@ -1887,7 +1958,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
}
AuthzDialog.confirmDeleteWithMsg($scope.policy.name, "Policy", msg, function() {
- ResourceServerPolicy.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
+ service.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
if (delegate.isPermission()) {
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission");
Notifications.success("The permission has been deleted.");
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
index 1c4f584..92219bb 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
@@ -36,10 +36,11 @@ module.factory('ResourceServerScope', function($resource) {
});
module.factory('ResourceServerPolicy', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id', {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:type/:id', {
realm : '@realm',
client: '@client',
- id : '@id'
+ id : '@id',
+ type: '@type'
}, {
'update' : {method : 'PUT'},
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'},
@@ -50,6 +51,23 @@ module.factory('ResourceServerPolicy', function($resource) {
});
});
+module.factory('ResourceServerPermission', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/:type/:id', {
+ realm : '@realm',
+ client: '@client',
+ type: '@type',
+ id : '@id'
+ }, {
+ 'update' : {method : 'PUT'},
+ 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/search', method : 'GET'},
+ 'searchPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy', method : 'GET', isArray: true},
+ 'associatedPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/associatedPolicies', method : 'GET', isArray: true},
+ 'dependentPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/dependentPolicies', method : 'GET', isArray: true},
+ 'scopes' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/:id/scopes', method : 'GET', isArray: true},
+ 'resources' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/:id/resources', method : 'GET', isArray: true}
+ });
+});
+
module.factory('PolicyProvider', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/providers', {
realm : '@realm',
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index b56bcc9..bcb0d0c 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -468,12 +468,14 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt
value = null;
} else {
id = policyToken.substring(0, policyToken.indexOf('('));
- value = policyToken.substring(policyToken.indexOf('(') + 1, policyToken.indexOf(')')).trim();
+ value = policyToken.substring(policyToken.indexOf('(') + 1, policyToken.lastIndexOf(')')).trim();
}
for (var j = 0; j < serverInfo.passwordPolicies.length; j++) {
if (serverInfo.passwordPolicies[j].id == id) {
- var p = serverInfo.passwordPolicies[j];
+ // clone
+ var p = JSON.parse(JSON.stringify(serverInfo.passwordPolicies[j]));
+
p.value = value && value || p.defaultValue;
policies.push(p);
}
@@ -502,6 +504,7 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt
$scope.serverInfo = serverInfo;
$scope.changed = false;
+ console.log(JSON.stringify(parse(realm.passwordPolicy)));
$scope.policy = parse(realm.passwordPolicy);
var oldCopy = angular.copy($scope.policy);
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
index 10e3170..7a5ba6d 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html
@@ -29,25 +29,25 @@
<kc-tooltip>{{:: 'authz-permission-description.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="policy.config.default">{{:: 'authz-permission-resource-apply-to-resource-type' | translate}}</label>
+ <label class="col-md-2 control-label" for="applyToResourceTypeFlag">{{:: 'authz-permission-resource-apply-to-resource-type' | translate}}</label>
<div class="col-md-6">
- <input ng-model="policy.config.default" id="policy.config.default" onoffswitch data-ng-click="applyToResourceType()"/>
+ <input ng-model="applyToResourceTypeFlag" id="applyToResourceTypeFlag" onoffswitch data-ng-click="applyToResourceType()"/>
</div>
<kc-tooltip>{{:: 'authz-permission-resource-apply-to-resource-type.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group clearfix" data-ng-hide="policy.config.default">
+ <div class="form-group clearfix" data-ng-hide="applyToResourceTypeFlag">
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <input type="hidden" ui-select2="resourcesUiSelect" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!policy.config.default"/>
+ <input type="hidden" ui-select2="resourcesUiSelect" id="reqActions" data-ng-model="selectedResource" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!applyToResourceTypeFlag"/>
</div>
<kc-tooltip>{{:: 'authz-permission-resource-resource.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group clearfix" data-ng-show="policy.config.default">
- <label class="col-md-2 control-label" for="policy.config.defaultResourceType">{{:: 'authz-resource-type' | translate}} <span class="required">*</span></label>
+ <div class="form-group clearfix" data-ng-show="applyToResourceTypeFlag">
+ <label class="col-md-2 control-label" for="policy.resourceType">{{:: 'authz-resource-type' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <input class="form-control" type="text" id="policy.config.defaultResourceType" name="policy.config.defaultResourceType" data-ng-model="policy.config.defaultResourceType" data-ng-required="policy.config.default">
+ <input class="form-control" type="text" id="policy.resourceType" name="policy.resourceType" data-ng-model="policy.resourceType" data-ng-required="applyToResourceTypeFlag">
</div>
<kc-tooltip>{{:: 'authz-permission-resource-type.tooltip' | translate}}</kc-tooltip>
@@ -56,7 +56,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
+ <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
</div>
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
index 90a3dc6..5da3a12 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html
@@ -32,27 +32,28 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resource' | translate}}</label>
<div class="col-md-6">
- <input type="hidden" ui-select2="resourcesUiSelect" data-ng-change="selectResource()" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-any-resource' | translate}}..." />
+ <input type="hidden" ui-select2="resourcesUiSelect" data-ng-change="selectResource()" id="reqActions" data-ng-model="selectedResource" data-placeholder="{{:: 'authz-any-resource' | translate}}..." />
</div>
<kc-tooltip>{{:: 'authz-permission-scope-resource.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group clearfix" data-ng-show="policy.config.resources">
+ <div class="form-group clearfix" data-ng-show="selectedResource">
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
<select ui-select2 id="reqActions2"
- data-ng-model="policy.config.scopes"
+ data-ng-model="selectedScopes"
data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple
- data-ng-required="policy.config.resources != null"
- data-ng-options="scope.id as scope.name for scope in policy.config.resources.scopes track by scope.id"/>
+ data-ng-required="selectedResource != null">
+ <option ng-repeat="scope in resourceScopes" value="{{scope.id}}">{{scope.name}}</option>
+ </select>
</div>
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group clearfix" data-ng-show="!policy.config.resources">
+ <div class="form-group clearfix" data-ng-show="!selectedResource">
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="policy.config.scopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple data-ng-required="policy.config.resources == null" />
+ <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="selectedScopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple data-ng-required="selectedResource == null" />
</div>
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
</div>
@@ -60,7 +61,7 @@
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
- <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
+ <input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="selectedPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
</div>
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
index 7187c22..e3bda1e 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
@@ -44,10 +44,10 @@
<kc-tooltip>{{:: 'authz-resource-uri.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}}</label>
+ <label class="col-md-2 control-label" for="scopes">{{:: 'authz-scopes' | translate}}</label>
<div class="col-md-6">
- <input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="resource.scopes" data-placeholder="{{:: 'authz-select-scope' | translate}}..." multiple/>
+ <input type="hidden" ui-select2="scopesUiSelect" id="scopes" data-ng-model="resource.scopes" data-placeholder="{{:: 'authz-select-scope' | translate}}..." multiple/>
</div>
<kc-tooltip>{{:: 'authz-resource-scopes.tooltip' | translate}}</kc-tooltip>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
index 6bfadfe..a505bbc 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
@@ -25,7 +25,7 @@
<div class="form-group">
<label class="col-md-2 control-label" for="name">{{:: 'authz-icon-uri' | translate}} </label>
<div class="col-sm-6">
- <input class="form-control" type="text" id="name" name="name" data-ng-model="scope.iconUri" autofocus>
+ <input class="form-control" type="text" id="iconUri" name="name" data-ng-model="scope.iconUri" autofocus>
</div>
<kc-tooltip>{{:: 'authz-icon-uri.tooltip' | translate}}</kc-tooltip>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html
index 4b24d3f..0491364 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/authz/kc-tabs-resource-server.html
@@ -2,7 +2,7 @@
<kc-tabs-client></kc-tabs-client>
- <ul class="nav nav-tabs nav-tabs-pf" data-ng-hide="create && !path[4]" style="margin-left: 15px">
+ <ul id="authz-tabs" class="nav nav-tabs nav-tabs-pf" data-ng-hide="create && !path[4]" style="margin-left: 15px">
<li ng-class="{active: !path[6]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/">{{:: 'settings' | translate}}</a></li>
<li ng-class="{active: path[6] == 'resource'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource">{{:: 'authz-resources' | translate}}</a></li>
<li ng-class="{active: path[6] == 'scope'}" data-ng-hide="create"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope">{{:: 'authz-authz-scopes' | translate}}</a></li>
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties
index 0217932..f323249 100644
--- a/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties
@@ -6,12 +6,13 @@ doAdd=Hinzuf\u00FCgen
doSignOut=Abmelden
editAccountHtmlTitle=Benutzerkonto bearbeiten
-federatedIdentitiesHtmlTitle=Federated Identities
+federatedIdentitiesHtmlTitle=F\u00F6derierte Identit\u00E4ten
accountLogHtmlTitle=Benutzerkonto Log
changePasswordHtmlTitle=Passwort \u00C4ndern
sessionsHtmlTitle=Sitzungen
accountManagementTitle=Keycloak Benutzerkontoverwaltung
authenticatorTitle=Authenticator
+applicationsHtmlTitle=Applikationen
authenticatorCode=One-time Code
email=E-Mail
@@ -50,7 +51,12 @@ role_manage-clients=Clients verwalten
role_manage-events=Events verwalten
role_view-profile=Profile ansehen
role_manage-account=Profile verwalten
+role_read-token=Token lesen
+role_offline-access=Offline-Zugriff
client_account=Konto
+client_realm-management=Realm-Management
+client_broker=Broker
+
requiredFields=Erforderliche Felder
allFieldsRequired=Alle Felder sind erforderlich
@@ -70,11 +76,22 @@ expires=Ablaufdatum
applications=Applikationen
account=Benutzerkonto
-federatedIdentity=Federated Identity
+federatedIdentity=F\u00F6derierte Identit\u00E4t
authenticator=Authenticator
sessions=Sitzungen
log=Log
+application=Applikation
+availablePermissions=verf\u00FCgbare Berechtigungen
+grantedPermissions=gew\u00E4hrte Berechtigungen
+grantedPersonalInfo=gew\u00E4hrte pers\u00F6nliche Informationen
+additionalGrants=zus\u00E4tzliche Berechtigungen
+action=Aktion
+inResource=in
+fullAccess=Vollzugriff
+offlineToken=Offline-Token
+revoke=Berechtigung widerrufen
+
configureAuthenticators=Authenticatoren konfigurieren
mobile=Mobile
totpStep1=Installieren Sie <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> oder <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> auf Ihrem Smartphone.
@@ -94,12 +111,17 @@ invalidPasswordConfirmMessage=Die Passwortbest\u00E4tigung ist nicht identisch.
invalidTotpMessage=Ung\u00FCltiger One-time Code.
invalidEmailMessage=Ung\u00FCltige E-Mail Adresse.
+usernameExistsMessage=Der Benutzername existiert bereits.
+emailExistsMessage=Die E-Mail-Adresse existiert bereits.
+
readOnlyUserMessage=Sie k\u00F6nnen dieses Benutzerkonto nicht \u00E4ndern, da es schreibgesch\u00FCtzt ist.
readOnlyPasswordMessage=Sie k\u00F6nnen dieses Passwort nicht \u00E4ndern, da es schreibgesch\u00FCtzt ist.
successTotpMessage=Mobile Authentifizierung eingerichtet.
successTotpRemovedMessage=Mobile Authentifizierung entfernt.
+successGrantRevokedMessage=Berechtigung erfolgreich widerrufen.
+
accountUpdatedMessage=Ihr Benutzerkonto wurde aktualisiert.
accountPasswordUpdatedMessage=Ihr Passwort wurde aktualisiert.
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_fr.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_fr.properties
index 99e6d70..3cdc016 100644
--- a/themes/src/main/resources-community/theme/base/account/messages/messages_fr.properties
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_fr.properties
@@ -8,7 +8,7 @@ doRemove=Supprimer
doAdd=Ajouter
doSignOut=D\u00e9connexion
-editAccountHtmlTitle=Edition du compte
+editAccountHtmlTitle=\u00c9dition du compte
federatedIdentitiesHtmlTitle=Identit\u00e9s f\u00e9d\u00e9r\u00e9es
accountLogHtmlTitle=Acc\u00e8s au compte
changePasswordHtmlTitle=Changer de mot de passe
@@ -51,11 +51,11 @@ role_manage-users=G\u00e9rer les utilisateurs
role_manage-applications=G\u00e9rer les applications
role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9s
role_manage-clients=G\u00e9rer les clients
-role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
-role_view-profile=Voir le profile
+role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
+role_view-profile=Voir le profil
role_manage-account=G\u00e9rer le compte
role_read-token=Lire le jeton d''authentification
-role_offline-access=Acc\u00e9s hors-ligne
+role_offline-access=Acc\u00e8s hors-ligne
client_account=Compte
client_security-admin-console=Console d''administration de la s\u00e9curit\u00e9
client_admin-cli=Admin CLI
@@ -87,20 +87,20 @@ sessions=Sessions
log=Connexion
application=Application
-availablePermissions=Permissions Disponibles
+availablePermissions=Permissions disponibles
grantedPermissions=Permissions accord\u00e9es
grantedPersonalInfo=Informations personnelles accord\u00e9es
additionalGrants=Droits additionnels
action=Action
inResource=dans
-fullAccess=Acc\u00e9s complet
+fullAccess=Acc\u00e8s complet
offlineToken=Jeton d''authentification hors-ligne
revoke=R\u00e9voquer un droit
configureAuthenticators=Authentifications configur\u00e9es.
mobile=T\u00e9l\u00e9phone mobile
totpStep1=Installez <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
-totpStep2=Ouvrez l''application et scanner le code barre ou entrez la cl\u00e9.
+totpStep2=Ouvrez l''application et scannez le code-barres ou entrez la clef.
totpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
missingUsernameMessage=Veuillez entrer votre nom d''utilisateur.
@@ -111,7 +111,7 @@ missingEmailMessage=Veuillez entrer votre courriel.
missingPasswordMessage=Veuillez entrer votre mot de passe.
notMatchPasswordMessage=Les mots de passe ne sont pas identiques
-missingTotpMessage=Veuillez entrer le code authentification.
+missingTotpMessage=Veuillez entrer le code d''authentification.
invalidPasswordExistingMessage=Mot de passe existant invalide.
invalidPasswordConfirmMessage=Le mot de passe de confirmation ne correspond pas.
invalidTotpMessage=Le code d''authentification est invalide.
@@ -141,7 +141,7 @@ identityProviderAlreadyLinkedMessage=Le fournisseur d''identit\u00e9 retourn\u00
accountDisabledMessage=Ce compte est d\u00e9sactiv\u00e9, veuillez contacter votre administrateur.
-accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, veuillez contacter votre administrateur ou r\u00e9essayez plus tard..
+accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, veuillez contacter votre administrateur ou r\u00e9essayez plus tard.
invalidPasswordMinLengthMessage=Mot de passe invalide: longueur minimale {0}.
invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.
invalidPasswordMinDigitsMessage=Mot de passe invalide: doit contenir au moins {0} chiffre(s).
@@ -149,4 +149,4 @@ invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide: doit contenir au
invalidPasswordMinSpecialCharsMessage=Mot de passe invalide: doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
invalidPasswordNotUsernameMessage=Mot de passe invalide: ne doit pas \u00eatre identique au nom d''utilisateur.
invalidPasswordRegexPatternMessage=Mot de passe invalide: ne valide pas l''expression rationnelle.
-invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
\ No newline at end of file
+invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas \u00eatre \u00e9gal aux {0} derniers mots de passe.
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_it.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_it.properties
index d22096e..611bed1 100755
--- a/themes/src/main/resources-community/theme/base/account/messages/messages_it.properties
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_it.properties
@@ -1,17 +1,18 @@
doSave=Salva
doCancel=Annulla
-doLogOutAllSessions=Effettua Log out in tutte le sessioni
+doLogOutAllSessions=Effettua il blog out da tutte le sessioni
doRemove=Elimina
doAdd=Aggiungi
-doSignOut=Sign Out
+doSignOut=Esci
-editAccountHtmlTitle=Gestisci Account
+editAccountHtmlTitle=Modifica Account
federatedIdentitiesHtmlTitle=Federated Identities
accountLogHtmlTitle=Account Log
changePasswordHtmlTitle=Cambia Password
sessionsHtmlTitle=Sessioni
accountManagementTitle=Keycloak Account Management
authenticatorTitle=Authenticator
+applicationsHtmlTitle=Applicazioni
authenticatorCode=Codice One-time
email=Email
@@ -26,12 +27,12 @@ passwordNew=Nuova Password
username=Username
address=Indirizzo
street=Via
-locality=Citta'' o Localita''
+locality=Citt\u00e0 o Localit\u00e0
region=Stato, Provincia, o Regione
-postal_code=Cap
+postal_code=CAP
country=Paese
emailVerified=Email verificata
-gssDelegationCredential=credenziali gss delegation
+gssDelegationCredential=Credenziali GSS Delegation
role_admin=Admin
role_realm-admin=Realm Admin
@@ -46,10 +47,19 @@ role_manage-realm=Gestisci realm
role_manage-users=Gestisci utenti
role_manage-applications=Gestisci applicazioni
role_manage-identity-providers=Gestisci identity provider
-role_manage-clients=Gestisci client
+role_manage-clients=Gestisci i client
role_manage-events=Gestisci eventi
role_view-profile=Visualizza profilo
role_manage-account=Gestisci account
+role_read-token=Leggi token
+role_offline-access=Accesso offline
+role_uma_authorization=Ottieni permessi
+client_account=Account
+client_security-admin-console=Security Admin Console
+client_admin-cli=Admin CLI
+client_realm-management=Gestione Realm
+client_broker=Broker
+
requiredFields=Campi obbligatori
allFieldsRequired=Tutti campi obbligatori
@@ -61,10 +71,10 @@ date=Data
event=Evento
ip=IP
client=Client
-clients=Client
+clients=Clients
details=Dettagli
started=Iniziato
-lastAccess=Ultimo Accesso
+lastAccess=Ultimo accesso
expires=Scade
applications=Applicazioni
@@ -74,49 +84,70 @@ authenticator=Authenticator
sessions=Sessioni
log=Log
-configureAuthenticators=Authenticator configurati
+application=Applicazione
+availablePermissions=Permessi disponibili
+grantedPermissions=Permessi concessi
+grantedPersonalInfo=Informazioni Personali concesse
+additionalGrants=Concessioni addizionali
+action=Azione
+inResource=in
+fullAccess=Accesso completo
+offlineToken=Token offline
+revoke=Revoca concessione
+
+configureAuthenticators=Configura Authenticators
mobile=Mobile
totpStep1=Installa <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> sul tuo dispositivo mobile.
totpStep2=Apri l''applicazione e scansiona il barcode o scrivi la chiave.
totpStep3=Scrivi il codice one-time fornito dall''applicazione e clicca Salva per completare il setup.
+missingUsernameMessage=Inserisci la username.
missingFirstNameMessage=Inserisci il nome.
-invalidEmailMessage=Invalid email address.
+invalidEmailMessage=Indirizzo email non valido.
missingLastNameMessage=Inserisci il cognome.
missingEmailMessage=Inserisci l''indirizzo email.
missingPasswordMessage=Inserisci la password.
-notMatchPasswordMessage=La password non coincide.
+notMatchPasswordMessage=Le password non corrispondono.
missingTotpMessage=Inserisci il codice di autenticazione.
invalidPasswordExistingMessage=Password esistente non valida.
invalidPasswordConfirmMessage=La password di conferma non coincide.
invalidTotpMessage=Codice di autenticazione non valido.
-readOnlyUserMessage=Non puoi aggiornare il tuo account dal momento che e'' in sola lettura.
-readOnlyPasswordMessage=Non puoi aggiornare la tua password dal momento che e'' in sola lettura.
+usernameExistsMessage=Username gi\u00e0 esistente.
+emailExistsMessage=Email gi\u00e0 esistente.
+
+readOnlyUserMessage=Non puoi aggiornare il tuo account dal momento che \u00e8 in modalit\u00e0 sola lettura.
+readOnlyPasswordMessage=Non puoi aggiornare il tuo account dal momento che \u00e8 in modalit\u00e0 sola lettura.
successTotpMessage=Mobile authenticator configurato.
-successTotpRemovedMessage=Mobile authenticator rliminato.
+successTotpRemovedMessage=Mobile authenticator eliminato.
+
+successGrantRevokedMessage=Concessione revocata correttamente.
-accountUpdatedMessage=Il tuo account e'' stato aggiornato.
-accountPasswordUpdatedMessage=La tua password e'' stata aggiornata.
+accountUpdatedMessage=Il tuo account \u00e8 stato aggiornato.
+accountPasswordUpdatedMessage=La tua password \u00e8 stata aggiornata.
missingIdentityProviderMessage=Identity provider non specificata.
invalidFederatedIdentityActionMessage=Azione non valida o mancante.
-identityProviderNotFoundMessage=L''identity provider specificato non e'' stato trovato.
-federatedIdentityLinkNotActiveMessage=Questo identity non e'' piu'' attivo.
-federatedIdentityRemovingLastProviderMessage=Non puoi rimuovere l''ultimo federated identity dal momento che non hai piu'' la password.
-identityProviderRedirectErrorMessage=Fallito il redirect all''identity provider.
-identityProviderRemovedMessage=Identity provider eliminato con successo.
+identityProviderNotFoundMessage=L''identity provider specificato non \u00e8 stato trovato.
+federatedIdentityLinkNotActiveMessage=Questo identity non \u00e8 pi\u00f9 attivo.
+federatedIdentityRemovingLastProviderMessage=Non puoi rimuovere l''ultimo federated identity dal momento che non hai pi\u00f9 la password.
+identityProviderRedirectErrorMessage=Il reindirizzamento all''identity provider \u00e8 fallito.
+identityProviderRemovedMessage=Identity provider eliminato correttamente.
+identityProviderAlreadyLinkedMessage=Federated identity ritornata da {0} \u00e8 gi\u00e0 collegata ad un altro utente.
+staleCodeAccountMessage=La pagina \u00e8 scaduta. Riprova di nuovo.
+consentDenied=Permesso negato.
accountDisabledMessage=Account disabilitato, contatta l''amministratore.
-accountTemporarilyDisabledMessage=L''account e'' temporaneamente disabilitato, contatta l''admin o riprova piu'' tardi.
+accountTemporarilyDisabledMessage=L''account \u00e8 temporaneamente disabilitato, contatta l''amministratore o riprova pi\u00f9 tardi.
invalidPasswordMinLengthMessage=Password non valida: lunghezza minima {0}.
invalidPasswordMinLowerCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri minuscoli.
invalidPasswordMinDigitsMessage=Password non valida: deve contenere almeno {0} numeri.
invalidPasswordMinUpperCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri maiuscoli.
invalidPasswordMinSpecialCharsMessage=Password non valida: deve contenere almeno {0} caratteri speciali.
-invalidPasswordNotUsernameMessage=Password non valida: non deve essere uguale allo username.
-invalidPasswordRegexPatternMessage=Password non valida: fallito il match con una o piu'' espressioni regolari.
-invalidPasswordHistoryMessage=Password non valida: non deve ssere uguale ad una delle ultime {0} password.
\ No newline at end of file
+invalidPasswordNotUsernameMessage=Password non valida: non deve essere uguale alla username.
+invalidPasswordRegexPatternMessage=Password non valida: fallito il match con una o pi\u00f9 espressioni regolari.
+invalidPasswordHistoryMessage=Password non valida: non deve essere uguale a nessuna delle ultime {0} password.
+invalidPasswordGenericMessage=Password non valida: la nuova password non rispetta le indicazioni previste.
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_ru.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_ru.properties
index fd5f9a9..7d47a55 100644
--- a/themes/src/main/resources-community/theme/base/account/messages/messages_ru.properties
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_ru.properties
@@ -1,151 +1,155 @@
-doSave=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C
-doCancel=\u041E\u0442\u043C\u0435\u043D\u0430
-doLogOutAllSessions=\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u0432\u0441\u0435\u0445 \u0441\u0435\u0441\u0441\u0438\u0439
-doRemove=\u0423\u0434\u0430\u043B\u0438\u0442\u044C
-doAdd=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C
-doSignOut=\u0412\u044B\u0445\u043E\u0434
-
-editAccountHtmlTitle=\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438
-federatedIdentitiesHtmlTitle=\u0424\u0435\u0434\u0435\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u044B
-accountLogHtmlTitle=\u041B\u043E\u0433 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438
-changePasswordHtmlTitle=\u0421\u043C\u0435\u043D\u0430 \u043F\u0430\u0440\u043E\u043B\u044F
-sessionsHtmlTitle=\u0421\u0435\u0441\u0441\u0438\u0438
-accountManagementTitle=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E
-authenticatorTitle=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440
-applicationsHtmlTitle=\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F
-
-authenticatorCode=\u041E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043A\u043E\u0434
-email=\u042D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u0430\u044F \u043F\u043E\u0447\u0442\u0430
-firstName=\u0418\u043C\u044F
-givenName=\u0412\u044B\u0434\u0430\u043D\u043D\u043E\u0435 \u0438\u043C\u044F
-fullName=\u041F\u043E\u043B\u043D\u043E\u0435 \u0438\u043C\u044F
-lastName=\u0424\u0430\u043C\u0438\u043B\u0438\u044F
-familyName=\u0424\u0430\u043C\u0438\u043B\u0438\u044F
-password=\u041F\u0430\u0440\u043E\u043B\u044C
-passwordConfirm=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F
-passwordNew=\u041D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C
-username=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-address=\u0410\u0434\u0440\u0435\u0441
-street=\u0423\u043B\u0438\u0446\u0430
-locality=\u0413\u043E\u0440\u043E\u0434
-region=\u0420\u0435\u0433\u0438\u043E\u043D
-postal_code=\u041F\u043E\u0447\u0442\u043E\u0432\u044B\u0439 \u0438\u043D\u0434\u0435\u043A\u0441
-country=\u0421\u0442\u0440\u0430\u043D\u0430
-emailVerified=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u043E\u0447\u0442\u043E\u0432\u043E\u0433\u043E \u0430\u0434\u0440\u0435\u0441\u0430
-gssDelegationCredential=\u0414\u0435\u043B\u0435\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 \u0447\u0435\u0440\u0435\u0437 GSS
-
-role_admin=\u0410\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440
-role_realm-admin=\u0410\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440 realm
-role_create-realm=\u0421\u043E\u0437\u0434\u0430\u0442\u044C realm
-role_view-realm=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 realm
-role_view-users=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-role_view-applications=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439
-role_view-clients=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-role_view-events=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u0441\u043E\u0431\u044B\u0442\u0438\u0439
-role_view-identity-providers=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u0432 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439
-role_manage-realm=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 realm
-role_manage-users=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\u043C\u0438
-role_manage-applications=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u043C\u0438
-role_manage-identity-providers=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043C\u0438 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439
-role_manage-clients=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430\u043C\u0438
-role_manage-events=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F\u043C\u0438
-role_view-profile=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u0440\u043E\u0444\u0438\u043B\u044F
-role_manage-account=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E
-role_read-token=\u0427\u0442\u0435\u043D\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u0430
-role_offline-access=\u0414\u043E\u0441\u0442\u0443\u043F \u043E\u0444\u0444\u043B\u0430\u0439\u043D
-client_account=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C
-client_security-admin-console=\u041A\u043E\u043D\u0441\u043E\u043B\u044C \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438
-client_admin-cli=\u041A\u043E\u043C\u0430\u043D\u0434\u043D\u044B\u0439 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430
-client_realm-management=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 Realm
-client_broker=\u0411\u0440\u043E\u043A\u0435\u0440
-
-
-requiredFields=\u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u043E\u043B\u044F
-allFieldsRequired=\u0412\u0441\u0435 \u043F\u043E\u043B\u044F \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435
-
-backToApplication=« \u041D\u0430\u0437\u0430\u0434 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435
-backTo=\u041D\u0430\u0437\u0430\u0434 \u0432 {0}
-
-date=\u0414\u0430\u0442\u0430
-event=\u0421\u043E\u0431\u044B\u0442\u0438\u0435
+# encoding: utf-8
+doSave=Сохранить
+doCancel=Отмена
+doLogOutAllSessions=Выйти из всех сессий
+doRemove=Удалить
+doAdd=Добавить
+doSignOut=Выход
+
+editAccountHtmlTitle=Изменение учетной записи
+federatedIdentitiesHtmlTitle=Федеративные идентификаторы
+accountLogHtmlTitle=Лог учетной записи
+changePasswordHtmlTitle=Смена пароля
+sessionsHtmlTitle=Сессии
+accountManagementTitle=Управление учетной записью
+authenticatorTitle=Аутентификатор
+applicationsHtmlTitle=Приложения
+
+authenticatorCode=Одноразовый код
+email=E-mail
+firstName=Имя
+givenName=Имя
+fullName=Полное имя
+lastName=Фамилия
+familyName=Фамилия
+password=Пароль
+passwordConfirm=Подтверждение пароля
+passwordNew=Новый пароль
+username=Имя пользователя
+address=Адрес
+street=Улица
+locality=Город
+region=Регион
+postal_code=Почтовый индекс
+country=Страна
+emailVerified=E-mail подтвержден
+gssDelegationCredential=Делегирование учетных данных через GSS
+
+role_admin=Администратор
+role_realm-admin=Администратор realm
+role_create-realm=Создать realm
+role_view-realm=Просмотр realm
+role_view-users=Просмотр пользователей
+role_view-applications=Просмотр приложений
+role_view-clients=Просмотр клиентов
+role_view-events=Просмотр событий
+role_view-identity-providers=Просмотр провайдеров учетных записей
+role_manage-realm=Управление realm
+role_manage-users=Управление пользователями
+role_manage-applications=Управление приложениями
+role_manage-identity-providers=Управление провайдерами учетных записей
+role_manage-clients=Управление клиентами
+role_manage-events=Управление событиями
+role_view-profile=Просмотр профиля
+role_manage-account=Управление учетной записью
+role_read-token=Чтение токена
+role_offline-access=Доступ оффлайн
+role_uma_authorization=Получение разрешений
+client_account=Учетная запись
+client_security-admin-console=Консоль администратора безопасности
+client_admin-cli=Командный интерфейс администратора
+client_realm-management=Управление Realm
+client_broker=Брокер
+
+
+requiredFields=Обязательные поля
+allFieldsRequired=Все поля обязательны
+
+backToApplication=« Назад в приложение
+backTo=Назад в {0}
+
+date=Дата
+event=Событие
ip=IP
-client=\u041A\u043B\u0438\u0435\u043D\u0442
-clients=\u041A\u043B\u0438\u0435\u043D\u0442\u044B
-details=\u0414\u0435\u0442\u0430\u043B\u0438
-started=\u041D\u0430\u0447\u0430\u0442\u0430
-lastAccess=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0434\u043E\u0441\u0442\u0443\u043F
-expires=\u0418\u0441\u0442\u0435\u043A\u0430\u0435\u0442
-applications=\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F
-
-account=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C
-federatedIdentity=\u0424\u0435\u0434\u0435\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440
-authenticator=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440
-sessions=\u0421\u0435\u0441\u0441\u0438\u0438
-log=\u041B\u043E\u0433
-
-application=\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435
-availablePermissions=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u044F
-grantedPermissions=\u0421\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u044F
-grantedPersonalInfo=\u0421\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u0430\u044F \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F
-additionalGrants=\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u044F
-action=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435
-inResource=\u0432
-fullAccess=\u041F\u043E\u043B\u043D\u044B\u0439 \u0434\u043E\u0441\u0442\u0443\u043F
-offlineToken=\u041E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D
-revoke=\u041E\u0442\u043E\u0437\u0432\u0430\u0442\u044C \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u0435
-
-configureAuthenticators=\u0421\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u044B
-mobile=\u041C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435
-totpStep1=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> \u0438\u043B\u0438 Google Authenticator. \u041E\u0431\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u043D\u0430 <a href="https://play.google.com">Google Play</a> \u0438 \u0432 Apple App Store.
-totpStep2=\u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0438 \u043F\u0440\u043E\u0441\u043A\u0430\u043D\u0438\u0440\u0443\u0439\u0442\u0435 \u0431\u0430\u0440\u043A\u043E\u0434, \u043B\u0438\u0431\u043E \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043B\u044E\u0447.
-totpStep3=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043A\u043E\u0434, \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C, \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438.
-
-missingUsernameMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-missingFirstNameMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043C\u044F.
-invalidEmailMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u044B\u0439 \u0430\u0434\u0440\u0435\u0441.
-missingLastNameMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0444\u0430\u043C\u0438\u043B\u0438\u044E.
-missingEmailMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u044D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u044B\u0439 \u0430\u0434\u0440\u0435\u0441.
-missingPasswordMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C.
-notMatchPasswordMessage=\u041F\u0430\u0440\u043E\u043B\u0438 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u044E\u0442.
-
-missingTotpMessage=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0434 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430.
-invalidPasswordExistingMessage=\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0439 \u043F\u0430\u0440\u043E\u043B\u044C \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439.
-invalidPasswordConfirmMessage=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442.
-invalidTotpMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043A\u043E\u0434 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430.
-
-usernameExistsMessage=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.
-emailExistsMessage=\u042D\u043B\u0435\u043A\u0442\u0440\u043E\u043D\u043D\u044B\u0439 \u0430\u0434\u0440\u0435\u0441 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.
-
-readOnlyUserMessage=\u0412\u044B \u043D\u0435 \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438, \u0442.\u043A. \u043E\u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0447\u0442\u0435\u043D\u0438\u044F.
-readOnlyPasswordMessage=\u0412\u044B \u043D\u0435 \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438, \u0442.\u043A. \u043E\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0447\u0442\u0435\u043D\u0438\u044F.
-
-successTotpMessage=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0432 \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u043C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D.
-successTotpRemovedMessage=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0432 \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u043C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 \u0443\u0434\u0430\u043B\u0435\u043D.
-
-successGrantRevokedMessage=\u0421\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u0435 \u043E\u0442\u043E\u0437\u0432\u0430\u043D\u043E \u0443\u0441\u043F\u0435\u0448\u043D\u043E.
-
-accountUpdatedMessage=\u0412\u0430\u0448\u0430 \u0443\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430.
-accountPasswordUpdatedMessage=\u0412\u0430\u0448\u0430 \u043F\u0430\u0440\u043E\u043B\u044C \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D.
-
-missingIdentityProviderMessage=\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u043D\u0435 \u0437\u0430\u0434\u0430\u043D.
-invalidFederatedIdentityActionMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0438\u043B\u0438 \u043D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435.
-identityProviderNotFoundMessage=\u0417\u0430\u0434\u0430\u043D\u043D\u044B\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.
-federatedIdentityLinkNotActiveMessage=\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0430\u043A\u0442\u0438\u0432\u0435\u043D.
-federatedIdentityRemovingLastProviderMessage=\u0412\u044B \u043D\u0435 \u043C\u043E\u0436\u0435\u0442\u0435 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0444\u0435\u0434\u0435\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440, \u0442.\u043A. \u0412\u044B \u043D\u0435 \u0438\u043C\u0435\u0435\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044F.
-identityProviderRedirectErrorMessage=\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0432 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-identityProviderRemovedMessage=\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u0443\u0434\u0430\u043B\u0435\u043D.
-identityProviderAlreadyLinkedMessage=\u0424\u0435\u0434\u0435\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440, \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u043D\u044B\u0439 {0} \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D \u043A \u0434\u0440\u0443\u0433\u043E\u043C\u0443 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E.
-staleCodeAccountMessage=\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437.
-consentDenied=\u0412 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u0438 \u043E\u0442\u043A\u0430\u0437\u0430\u043D\u043E.
-
-accountDisabledMessage=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430, \u043E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u043A \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0443.
-
-accountTemporarilyDisabledMessage=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430, \u043E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u043A \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0443 \u0438\u043B\u0438 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043F\u043E\u0437\u0436\u0435.
-invalidPasswordMinLengthMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430 {0}.
-invalidPasswordMinLowerCaseCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u0438\u043C\u0432\u043E\u043B\u044B \u0432 \u043D\u0438\u0436\u043D\u0435\u043C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinDigitsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0} \u0446\u0438\u0444\u0440(\u044B).
-invalidPasswordMinUpperCaseCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0} \u0441\u0438\u043C\u0432\u043E\u043B\u0430(\u043E\u0432) \u0432 \u0432\u0435\u0440\u0445\u043D\u0435\u043C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435.
-invalidPasswordMinSpecialCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0} \u0441\u043F\u0435\u0446\u0441\u0438\u043C\u0432\u043E\u043B\u0430(\u043E\u0432).
-invalidPasswordNotUsernameMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043F\u0430\u0440\u043E\u043B\u044C \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0442\u044C \u0441 \u0438\u043C\u0435\u043D\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-invalidPasswordRegexPatternMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043F\u0430\u0440\u043E\u043B\u044C \u043D\u0435 \u0443\u0434\u043E\u0432\u043B\u0435\u0442\u0432\u043E\u0440\u044F\u0435\u0442 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E\u043C\u0443 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u044E.
-invalidPasswordHistoryMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043F\u0430\u0440\u043E\u043B\u044C \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0442\u044C \u0441 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C(\u0438) {0} \u043F\u0430\u0440\u043E\u043B\u044F\u043C\u0438.
\ No newline at end of file
+client=Клиент
+clients=Клиенты
+details=Детали
+started=Начата
+lastAccess=Последний доступ
+expires=Истекает
+applications=Приложения
+
+account=Учетная запись
+federatedIdentity=Федеративный идентификатор
+authenticator=Аутентификатор
+sessions=Сессии
+log=Журнал
+
+application=Приложение
+availablePermissions=Доступные разрешения
+grantedPermissions=Согласованные разрешения
+grantedPersonalInfo=Согласованная персональная информация
+additionalGrants=Дополнительные согласования
+action=Действие
+inResource=в
+fullAccess=Полный доступ
+offlineToken=Оффлайн токен
+revoke=Отозвать согласование
+
+configureAuthenticators=Сконфигурированные аутентификаторы
+mobile=Мобильное приложение
+totpStep1=Установите <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> или Google Authenticator. Оба приложения доступны на <a href="https://play.google.com">Google Play</a> и в Apple App Store.
+totpStep2=Откройте приложение и просканируйте баркод, либо введите ключ.
+totpStep3=Введите одноразовый код, выданный приложением, и нажмите сохранить для завершения установки.
+
+missingUsernameMessage=Введите имя пользователя.
+missingFirstNameMessage=Введите имя.
+invalidEmailMessage=Введите корректный E-mail.
+missingLastNameMessage=Введите фамилию.
+missingEmailMessage=Введите E-mail.
+missingPasswordMessage=Введите пароль.
+notMatchPasswordMessage=Пароли не совпадают.
+
+missingTotpMessage=Введите код аутентификатора.
+invalidPasswordExistingMessage=Существующий пароль неверный.
+invalidPasswordConfirmMessage=Подтверждение пароля не совпадает.
+invalidTotpMessage=Неверный код аутентификатора.
+
+usernameExistsMessage=Имя пользователя уже существует.
+emailExistsMessage=E-mail уже существует.
+
+readOnlyUserMessage=Вы не можете обновить информацию вашей учетной записи, т.к. она доступна только для чтения.
+readOnlyPasswordMessage=Вы не можете обновить пароль вашей учетной записи, т.к. он доступен только для чтения.
+
+successTotpMessage=Аутентификатор в мобильном приложении сконфигурирован.
+successTotpRemovedMessage=Аутентификатор в мобильном приложении удален.
+
+successGrantRevokedMessage=Согласование отозвано успешно.
+
+accountUpdatedMessage=Ваша учетная запись обновлена.
+accountPasswordUpdatedMessage=Ваша пароль обновлен.
+
+missingIdentityProviderMessage=Провайдер учетных записей не задан.
+invalidFederatedIdentityActionMessage=Некорректное или недопустимое действие.
+identityProviderNotFoundMessage=Заданный провайдер учетных записей не найден.
+federatedIdentityLinkNotActiveMessage=Идентификатор больше не активен.
+federatedIdentityRemovingLastProviderMessage=Вы не можете удалить последний федеративный идентификатор, т.к. Вы не имеете пароля.
+identityProviderRedirectErrorMessage=Ошибка перенаправления в провайдер учетных записей.
+identityProviderRemovedMessage=Провайдер учетных записей успешно удален.
+identityProviderAlreadyLinkedMessage=Федеративный идентификатор, возвращенный {0} уже используется другим пользователем.
+staleCodeAccountMessage=Страница устарела. Попробуйте еще раз.
+consentDenied=В согласовании отказано.
+
+accountDisabledMessage=Учетная запись заблокирована, обратитесь к администратору.
+
+accountTemporarilyDisabledMessage=Учетная запись временно заблокирована, обратитесь к администратору или попробуйте позже.
+invalidPasswordMinLengthMessage=Некорректный пароль: длина пароля должна быть не менее {0} символа(ов).
+invalidPasswordMinLowerCaseCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в нижнем регистре.
+invalidPasswordMinDigitsMessage=Некорректный пароль: пароль должен содержать не менее {0} цифр(ы).
+invalidPasswordMinUpperCaseCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в верхнем регистре.
+invalidPasswordMinSpecialCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} спецсимвола(ов).
+invalidPasswordNotUsernameMessage=Некорректный пароль: пароль не должен совпадать с именем пользователя.
+invalidPasswordRegexPatternMessage=Некорректный пароль: пароль не удовлетворяет регулярному выражению.
+invalidPasswordHistoryMessage=Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролями.
+invalidPasswordGenericMessage=Некорректный пароль: новый пароль не соответствует правилам пароля.
+
diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_sv.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_sv.properties
index f939b52..8c83abb 100755
--- a/themes/src/main/resources-community/theme/base/account/messages/messages_sv.properties
+++ b/themes/src/main/resources-community/theme/base/account/messages/messages_sv.properties
@@ -94,7 +94,7 @@ revoke=Upphäv Tillstånd
configureAuthenticators=Ändrade Autentiserare
mobile=Mobil
-totpStep1=Installera <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> eller Google Authenticator på din enhet. Båda applikationerna finns tillgängliga på <a href="https://play.google.com">Google Play</a> och Apple App Store.
+totpStep1=Installera <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> eller Google Authenticator på din enhet. Båda applikationerna finns tillgängliga på <a href="https://play.google.com">Google Play</a> och Apple App Store.
totpStep2=Öppna applikationen och skanna streckkoden eller skriv i nyckeLn.
totpStep3=Fyll i engångskoden som tillhandahålls av applikationen och klicka på Spara för att avsluta inställningarna.
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_fr.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_fr.properties
index 976ba69..525dceb 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_fr.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_fr.properties
@@ -14,11 +14,11 @@ clients=Clients
clear=Effacer
selectOne=Select One...
-manage=Gérer
+manage=G\u00e9rer
authentication=Authentification
user-federation=Regroupement Utilisateur
user-storage=Stockage Utilisateur
-events=Ev\u00e8nements
+events=\u00c9v\u00e8nements
realm-settings=Configurations du domaine
configure=Configurer
select-realm=Choisir un domaine
@@ -31,35 +31,35 @@ endpoints=Endpoints
# Realm settings
realm-detail.enabled.tooltip=Les utilisateurs et les clients peuvent acc\u00e9der au domaine si celui-ci est actif
-realm-detail.oidc-endpoints.tooltip=Affiche les configurations du endpoint OpenID Connect
+realm-detail.oidc-endpoints.tooltip=Affiche les configurations de l''endpoint OpenID Connect
registrationAllowed=Enregistrement d''utilisateur
registrationAllowed.tooltip=Activer/d\u00e9sactiver la page d''enregistrement. Un lien pour l''enregistrement sera visible sur la page de connexion.
registrationEmailAsUsername=Courriel comme nom d''utilisateur
-registrationEmailAsUsername.tooltip=Si actif le champ du nom de l''utilisateur est cach\u00e9 pendant l''enregistrement, le courriel est utilis\u00e9 comme nom d''utilisateur.
-editUsernameAllowed=Editez le nom de l''utilisateur
+registrationEmailAsUsername.tooltip=Si actif, le champ du nom de l''utilisateur est cach\u00e9 pendant l''enregistrement ; le courriel est utilis\u00e9 comme nom d''utilisateur.
+editUsernameAllowed=\u00c9ditez le nom de l''utilisateur
editUsernameAllowed.tooltip=Si actif, le champ du nom de l''utilisateur est modifiable.
resetPasswordAllowed=Mot de passe oubli\u00e9
-resetPasswordAllowed.tooltip=Afficher un lien sur la page de connexion pour les utilisateurs ayant oubli\u00e9s leurs accr\u00e9ditations.
+resetPasswordAllowed.tooltip=Affiche un lien sur la page de connexion pour les utilisateurs ayant oubli\u00e9 leurs accr\u00e9ditations.
rememberMe=Se souvenir de moi
-rememberMe.tooltip=Afficher la case \u00e0 cocher sur la page de connexion pour permettre aux utilisateurs de rester connecter entre deux red\u00e9marrages de leur navigateur jusqu''\u00e0 expiration de la session.
-verifyEmail=Verifier le courriel
+rememberMe.tooltip=Affiche une case \u00e0 cocher sur la page de connexion pour permettre aux utilisateurs de rester connect\u00e9s entre deux red\u00e9marrages de leur navigateur, jusqu''\u00e0 expiration de la session.
+verifyEmail=V\u00e9rification du courriel
verifyEmail.tooltip=Force l''utilisateur \u00e0 v\u00e9rifier son courriel lors de la premi\u00e8re connexion.
loginWithEmailAllowed=Authentification avec courriel
-loginWithEmailAllowed.tooltip=Autoriser l''utilisateur \u00e0 s''authentifier avec son adresse de courriel.
+loginWithEmailAllowed.tooltip=Autorise l''utilisateur \u00e0 s''authentifier avec son adresse de courriel.
duplicateEmailsAllowed=Doublon courriel
-duplicateEmailsAllowed.tooltip=Autoriser plusieurs utilisateurs \u00e0 avoir la m\u00eame adresse de courriel. Changer cette configuration va vider le cache. Il est recommand\u00e9 de mettre \u00e0 jour manuellement les contraintes sur le courriel dans la base de donn\u00e0e apr\u00e8s la d\u00e9sactivation du support des doublons.
+duplicateEmailsAllowed.tooltip=Autorise plusieurs utilisateurs \u00e0 avoir la m\u00eame adresse de courriel. Changer cette configuration va vider le cache. Il est recommand\u00e9 de mettre \u00e0 jour manuellement les contraintes sur le courriel dans la base de donn\u00e9es apr\u00e8s la d\u00e9sactivation du support des doublons.
sslRequired=SSL requis
sslRequired.option.all=toutes les requ\u00eates
sslRequired.option.external=les requ\u00eates externes
sslRequired.option.none=aucun
-sslRequired.tooltip=Si le HTTPS est requis ? ''aucun'' signifie que le HTTPS n''est requis pour aucune adresse IP cliente. ''les requ\u00eates externes'' signifie que localhost et les adresses IP priv\u00e9es peuvent acc\u00e9der sans HTTPS. ''toutes les requ\u00eates'' signifie que le protocole HTTPS est obligatoire pour toutes les adresses IP.
+sslRequired.tooltip=Niveau d''exigence HTTPS \: ''aucun'' signifie que le HTTPS n''est requis pour aucune adresse IP cliente. ''les requ\u00eates externes'' signifie que localhost et les adresses IP priv\u00e9es peuvent acc\u00e9der sans HTTPS. ''toutes les requ\u00eates'' signifie que le protocole HTTPS est obligatoire pour toutes les adresses IP.
publicKey=Clef publique
gen-new-keys=Cr\u00e9ation de nouvelle clef
certificate=Certificat
host=H\u00f4te
-smtp-host=SMTP h\u00f4te
+smtp-host=H\u00f4te SMTP
port=Port
-smtp-port=SMTP Port (par defaut 25)
+smtp-port=Port SMTP (25 par d\u00e9faut)
from=De
sender-email-addr=Courriel de l''exp\u00e9diteur
enable-ssl=Activer SSL/TLS
@@ -75,17 +75,17 @@ login-theme.tooltip=S\u00e9lectionnez le th\u00e8me pour les pages de connexion,
account-theme=Th\u00e8me du compte
account-theme.tooltip=S\u00e9lectionnez le th\u00e8me pour la gestion des comptes.
admin-console-theme=Th\u00e8me de la console d''administration
-select-theme-admin-console=S\u00e9lectionnez le theme e la console d''administration.
+select-theme-admin-console=S\u00e9lectionnez le th\u00e8me de la console d''administration.
email-theme=Th\u00e8me pour le courriel
-select-theme-email=S\u00e9lectionnez le th\u00e8me les courriels envoy\u00e9es par le serveur.
-i18n-enabled=Internationalisation activ\u00e9
+select-theme-email=S\u00e9lectionnez le th\u00e8me pour les courriels envoy\u00e9es par le serveur.
+i18n-enabled=Internationalisation activ\u00e9e
supported-locales=Locales support\u00e9es
-supported-locales.placeholder=Entrer la locale et valider
+supported-locales.placeholder=Entrez la locale et validez
default-locale=Locale par d\u00e9faut
realm-cache-enabled=Cache du domaine activ\u00e9
realm-cache-enabled.tooltip=Activer/D\u00e9sactiver le cache pour le domaine, client et donn\u00e9es.
user-cache-enabled=Cache utilisateur activ\u00e9
-user-cache-enabled.tooltip=Activer/D\u00e9sactiver le cache utilisateur, et le cache de relation entre utilisateur et roles.
+user-cache-enabled.tooltip=Activer/D\u00e9sactiver le cache utilisateur, et le cache de relation entre utilisateurs et r\u00f4les.
sso-session-idle=Sessions SSO inactives
seconds=Secondes
minutes=Minutes
@@ -95,26 +95,26 @@ sso-session-max=Maximum de sessions SSO
sso-session-idle.tooltip=Temps d''inactivit\u00e9 autoris\u00e9 avant expiration de la session. Les jetons et les sessions navigateurs sont invalid\u00e9es quand la session expire.
sso-session-max.tooltip=Dur\u00e9e maximale avant que la session n''expire. Les jetons et les sessions navigateurs sont invalid\u00e9es quand la session expire.
access-token-lifespan=Dur\u00e9e de vie du jeton d''acc\u00e8s
-access-token-lifespan.tooltip=Dur\u00e9e maximale avant que le jeton d''acc\u00e8s n''expire. Cette valeur devrait etre relativement plus petite que la dur\u00e9e d''inactivit\u00e9 (timeout) du SSO .
+access-token-lifespan.tooltip=Dur\u00e9e maximale avant que le jeton d''acc\u00e8s n''expire. Cette valeur devrait \u00eatre relativement plus petite que la dur\u00e9e d''inactivit\u00e9 (timeout) du SSO.
client-login-timeout=Dur\u00e9e d''inactivit\u00e9 de connexion (timeout)
-client-login-timeout.tooltip=Dur\u00e9e maximale qu''a un client pour finir le protocole du jeton d''acc\u00e8s. Devrait etre de l''ordre de la minute (1 min).
+client-login-timeout.tooltip=Dur\u00e9e maximale qu''a un client pour finir le protocole du jeton d''acc\u00e8s. Devrait \u00eatre de l''ordre de la minute (1 min).
login-timeout=Dur\u00e9e d''inactivit\u00e9 de connexion
-login-timeout.tooltip=Dur\u00e9e maximale autoris\u00e9e pour finaliser la connexion. Devrait \u00eatre relativement long une 30 minutes voire plus.
+login-timeout.tooltip=Dur\u00e9e maximale autoris\u00e9e pour finaliser la connexion. Devrait \u00eatre relativement long \: 30 minutes, voire plus.
login-action-timeout=Dur\u00e9e d''inactivit\u00e9 des actions de connexions
-login-action-timeout.tooltip=Dur\u00e9e maximale qu''a un utilisateur pour finir ses actions concernants la mise \u00e0 jour de son mot de passe ou bien de la configuration du mot de passe \u00e0 usage unique (totp). Devrait \u00eatre relativement long 5 minutes voire plus.
+login-action-timeout.tooltip=Dur\u00e9e maximale qu''a un utilisateur pour finir ses actions concernant la mise \u00e0 jour de son mot de passe ou bien de la configuration du mot de passe \u00e0 usage unique (TOTP). Devrait \u00eatre relativement long \: 5 minutes, voire plus.
headers=En-t\u00eates
brute-force-detection=D\u00e9tection des attaques par force brute
x-frame-options=X-Frame-Options
click-label-for-info=Cliquer sur le label pour plus d''information. Les valeurs par d\u00e9faut \u00e9vitent que les pages soient incluses dans des iframes \u00e9trang\u00e8res.
content-sec-policy=Content-Security-Policy
-max-login-failures=Nombre maximale d''erreur de connexion
-max-login-failures.tooltip=Combien d''erreur avant le temps d''attente.
+max-login-failures=Nombre maximal d''erreurs de connexion
+max-login-failures.tooltip=Nombre d''erreurs avant de d\u00e9clencher le temps d''attente.
wait-increment=Temps d''attente
-wait-increment.tooltip=Quand le seuil des erreurs est atteint,combien de temps l''utilisateur est-il bloqu\u00e9 ?
-quick-login-check-millis=Nombre de milli-secondes entre deux connexions
+wait-increment.tooltip=Quand le seuil des erreurs est atteint, combien de temps l''utilisateur est-il bloqu\u00e9 ?
+quick-login-check-millis=Nombre de millisecondes entre deux connexions
quick-login-check-millis.tooltip=Si une erreur apparait trop rapidement, bloquer le compte utilisateur.
min-quick-login-wait=Dur\u00e9e minimale d''attente entre deux connexions
-min-quick-login-wait.tooltip=Dur\u00e9e d''attente demand\u00e9e apr\u00e8s une erreur entre deux connexion.
+min-quick-login-wait.tooltip=Dur\u00e9e d''attente demand\u00e9e apr\u00e8s une erreur entre deux connexions.
max-wait=Dur\u00e9e maximale d''attente
max-wait.tooltip=Dur\u00e9e maximale de blocage du compte utilisateur
failure-reset-time=Dur\u00e9e de remise \u00e0 z\u00e9ro des erreurs
@@ -125,7 +125,7 @@ realm-tab-email=Courriels
realm-tab-themes=Th\u00e8mes
realm-tab-cache=Cache
realm-tab-tokens=Jetons
-realm-tab-security-defenses=Security Defenses
+realm-tab-security-defenses=Mesures de s\u00e9curit\u00e9
realm-tab-general=G\u00e9n\u00e9ral
add-realm=Ajouter un domaine
@@ -139,4 +139,4 @@ not-before=Pas avant
not-before.tooltip=R\u00e9voquer tous les jetons demand\u00e9s avant cette date.
set-to-now=Mettre \u00e0 maintenant
push=Appuyer
-push.tooltip=Pour tout les clients ayant une URL d''administration, les notifier de la politique de r\u00e9vocation.
+push.tooltip=Pour tous les clients ayant une URL d''administration, les notifier de la politique de r\u00e9vocation.
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties
index 6a59cc9..08f1fb8 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties
@@ -1,915 +1,1273 @@
-consoleTitle=\u041A\u043E\u043D\u0441\u043E\u043B\u044C \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 Keycloak
-
+# encoding: utf-8
+consoleTitle=Консоль администратора Keycloak
# Common messages
-enabled=\u0412\u043A\u043B\u044E\u0447\u0435\u043D\u043E
-name=\u0418\u043C\u044F
-displayName=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435
-displayNameHtml=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u0432 HTML
-save=\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C
-cancel=\u041E\u0442\u043C\u0435\u043D\u0430
-onText=\u0412\u041A\u041B
-offText=\u0412\u042B\u041A
-client=\u041A\u043B\u0438\u0435\u043D\u0442
-clients=\u041A\u043B\u0438\u0435\u043D\u0442\u044B
-clear=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C
-selectOne=\u0412\u044B\u0431\u0440\u0430\u0442\u044C...
-
-true=\u0414\u0430
-false=\u041D\u0435\u0442
+enabled=Включено
+hidden=Скрыто
+link-only-column=Только ссылки
+name=Имя
+displayName=Отображаемое название
+displayNameHtml=Отображаемое название в HTML
+save=Сохранить
+cancel=Отмена
+onText=ВКЛ
+offText=ВЫК
+client=Клиент
+clients=Клиенты
+clear=Очистить
+selectOne=Выбрать...
+
+true=Да
+false=Нет
+endpoints=Конечные точки
# Realm settings
-realm-detail.enabled.tooltip=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u044B \u043C\u043E\u0433\u0443\u0442 \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u043A Realm \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-registrationAllowed=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-registrationAllowed.tooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438. \u0421\u0441\u044B\u043B\u043A\u0430 \u0434\u043B\u044F \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043A\u0436\u0435 \u043F\u043E\u043A\u0430\u0437\u0430\u043D\u0430 \u043D\u0430 \u0444\u043E\u0440\u043C\u0435 \u0432\u0445\u043E\u0434\u0430.
-registrationEmailAsUsername=Email \u043A\u0430\u043A \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-registrationEmailAsUsername.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0442\u043E \u043D\u0430 \u0444\u043E\u0440\u043C\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043F\u043E\u043B\u0435 \u0438\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0431\u0443\u0434\u0435\u0442 \u0441\u043A\u0440\u044B\u0442\u043E \u0438 \u0432 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0438\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0434\u043B\u044F \u043D\u043E\u0432\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F email.
-editUsernameAllowed=\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u0443\u0435\u043C\u043E\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-editUsernameAllowed.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E,\u0442\u043E \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043C\u043E\u0436\u043D\u043E \u0431\u0443\u0434\u0435\u0442 \u043E\u0442\u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C, \u0438\u043D\u0430\u0447\u0435 \u043E\u043D\u043E \u0431\u0443\u0434\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u043C \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0447\u0442\u0435\u043D\u0438\u044F.
-resetPasswordAllowed=\u0417\u0430\u0431\u044B\u043B\u0438 \u043F\u0430\u0440\u043E\u043B\u044C
-resetPasswordAllowed.tooltip=\u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435 \u0432\u0445\u043E\u0434\u0430 \u0434\u043B\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F, \u043F\u043E \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0443 \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0441\u043C\u043E\u0436\u0435\u0442 \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0432\u043E\u0438 \u0434\u0430\u043D\u043D\u044B\u0435 \u0434\u043B\u044F \u0432\u0445\u043E\u0434\u0430.
-rememberMe=\u0417\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u044C \u043C\u0435\u043D\u044F
-rememberMe.tooltip=\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0447\u0435\u043A\u0431\u043E\u043A\u0441 \u043D\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435 \u0432\u0445\u043E\u0434\u0430, \u0447\u0442\u043E\u0431\u044B \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u0437\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u044C \u0432\u0445\u043E\u0434 \u0432 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C \u0432 \u0441\u043B\u0443\u0447\u0430\u0435 \u0435\u0441\u043B\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043D\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442.
-verifyEmail=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 email
-verifyEmail.tooltip=\u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0443 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u0441\u0432\u043E\u0439 email \u043F\u0440\u0438 \u043F\u0435\u0440\u0432\u043E\u043C \u0432\u0445\u043E\u0434\u0435 \u0432 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C.
-sslRequired=\u0422\u0440\u0435\u0431\u0443\u0435\u0442 SSL
-sslRequired.option.all=\u0432\u0441\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u044B
-sslRequired.option.external=\u0432\u043D\u0435\u0448\u043D\u0438\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u044B
-sslRequired.option.none=\u043D\u0435\u0442
-sslRequired.tooltip=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043B\u0438 HTTPS? '\u043D\u0435\u0442' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E HTTPS \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F \u043B\u044E\u0431\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0441 \u043B\u044E\u0431\u044B\u043C IP \u0430\u0434\u0440\u0435\u0441\u043E\u043C. '\u0412\u043D\u0435\u0448\u043D\u0438\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u044B' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E localhost \u0438 \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u044B\u0435 IP \u0430\u0434\u0440\u0435\u0441\u0430 \u043C\u043E\u0433\u0443\u0442 \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0431\u0435\u0437 HTTPS. '\u0412\u0441\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u044B' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E HTTPS \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F \u0432\u0441\u0435\u0445 IP \u0430\u0434\u0440\u0435\u0441\u043E\u0432.
-publicKey=\u041F\u0443\u0431\u043B\u0438\u0447\u043D\u044B\u0439 \u043A\u043B\u044E\u0447
-privateKey=\u041F\u0440\u0438\u0432\u0430\u0442\u043D\u044B\u0439 \u043A\u043B\u044E\u0447
-gen-new-keys=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u043A\u043B\u044E\u0447
-certificate=\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442
-host=\u0425\u043E\u0441\u0442
-smtp-host=SMTP \u0445\u043E\u0441\u0442
-port=\u041F\u043E\u0440\u0442
-smtp-port=SMTP \u043F\u043E\u0440\u0442 (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 25)
-from=\u041E\u0442
-sender-email-addr=Email \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u0435\u043B\u044F
-enable-ssl=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C SSL
-enable-start-tls=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C StartTLS
-enable-auth=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E
-username=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-login-username=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0434\u043B\u044F \u0432\u0445\u043E\u0434\u0430
-password=\u041F\u0430\u0440\u043E\u043B\u044C
-login-password=\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F \u0432\u0445\u043E\u0434\u0430
-login-theme=\u0422\u0435\u043C\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0432\u0445\u043E\u0434\u0430
-login-theme.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0435\u043C\u0443 \u0434\u043B\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446 \u0432\u0445\u043E\u0434\u0430, \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0433\u043E \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u043E\u0433\u043E \u043F\u0430\u0440\u043E\u043B\u044F (TOTP), \u0432\u044B\u0434\u0430\u0447\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0439, \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0430\u0440\u043E\u043B\u044F.
-account-theme=\u0422\u0435\u043C\u0430 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438
-account-theme.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0435\u043C\u0443 \u0434\u043B\u044F \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-admin-console-theme=\u0422\u0435\u043C\u0430 \u043A\u043E\u043D\u0441\u043E\u043B\u0438 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430
-select-theme-admin-console=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0435\u043C\u0443 \u0434\u043B\u044F \u043A\u043E\u043D\u0441\u043E\u043B\u0438 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430.
-email-theme=\u0422\u0435\u043C\u0430 \u0434\u043B\u044F email
-select-theme-email=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0435\u043C\u0443 \u0434\u043B\u044F email, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u043E\u0442\u0441\u044B\u043B\u0430\u0442\u044C\u0441\u044F \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.
-i18n-enabled=\u0418\u043D\u0442\u0435\u0440\u043D\u0430\u0446\u0438\u043E\u043D\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F
-supported-locales=\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0435 \u044F\u0437\u044B\u043A\u0438
-supported-locales.placeholder=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u044F\u0437\u044B\u043A \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 Enter
-default-locale=\u042F\u0437\u044B\u043A \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-realm-cache-clear=\u041A\u044D\u0448 Realm
-realm-cache-clear.tooltip=\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u0441\u0435 \u0437\u0430\u043F\u0438\u0441\u0438 \u0432 \u043A\u044D\u0448\u0435 realm (\u0443\u0434\u0430\u043B\u0438\u0442 \u0432\u0441\u0435 \u0437\u0430\u043F\u0438\u0441\u0438 \u0434\u043B\u044F \u0432\u0441\u0435\u0445 realm)
-user-cache-clear=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0439 \u043A\u044D\u0448
-user-cache-clear.tooltip=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0432\u0441\u0435 \u0437\u0430\u043F\u0438\u0441\u0438 \u0432 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u043E\u043C \u043A\u044D\u0448\u0435 (\u044D\u0442\u043E \u0443\u0434\u0430\u043B\u0438\u0442 \u0437\u0430\u043F\u0438\u0441\u0438 \u0434\u043B\u044F \u0432\u0441\u0435\u0445 realm)
-revoke-refresh-token=\u041E\u0442\u0437\u044B\u0432 \u0442\u043E\u043A\u0435\u043D\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F
-revoke-refresh-token.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0442\u043E \u0442\u043E\u043A\u0435\u043D\u044B \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u044B \u0435\u0434\u0438\u043D\u043E\u0436\u0434\u044B. \u0412 \u043F\u0440\u043E\u0442\u0438\u0432\u043D\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435, \u0442\u043E\u043A\u0435\u043D \u043E\u0442\u0437\u044B\u0432\u0430\u0442\u044C\u0441\u044F \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438 \u043C\u043E\u0436\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043C\u043D\u043E\u0433\u043E\u043A\u0440\u0430\u0442\u043D\u043E.
-sso-session-idle=\u0422\u0430\u0439\u043C\u0430\u0443\u0442 \u0441\u0435\u0441\u0441\u0438\u0438 SSO
-seconds=\u0441\u0435\u043A\u0443\u043D\u0434
-minutes=\u043C\u0438\u043D\u0443\u0442
-hours=\u0447\u0430\u0441\u043E\u0432
-days=\u0434\u043D\u0435\u0439
-sso-session-max=\u041E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 SSO
-sso-session-idle.tooltip=\u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0431\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0441\u0435\u0441\u0441\u0438\u0438. \u041F\u043E \u0438\u0441\u0442\u0435\u0447\u0435\u043D\u0438\u0438 \u044D\u0442\u043E\u0433\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u0442\u043E\u043A\u0435\u043D\u044B \u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043D\u044B\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u0441\u0442\u0430\u043D\u043E\u0432\u044F\u0442\u0441\u044F \u043D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u044B\u043C\u0438.
-sso-session-max.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0434\u043E \u0442\u043E\u0433\u043E, \u043A\u0430\u043A \u0438\u0441\u0442\u0435\u0447\u0435\u0442 \u0441\u0435\u0441\u0441\u0438\u044F. \u041F\u043E \u0438\u0441\u0442\u0435\u0447\u0435\u043D\u0438\u0438 \u044D\u0442\u043E\u0433\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u0442\u043E\u043A\u0435\u043D\u044B \u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043D\u044B\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u0441\u0442\u0430\u043D\u043E\u0432\u044F\u0442\u0441\u044F \u043D\u0435\u0432\u0430\u043B\u0438\u0434\u043D\u044B\u043C\u0438.
-offline-session-idle=\u0422\u0430\u0439\u043C\u0430\u0443\u0442 \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0441\u0435\u0441\u0441\u0438\u0438
-offline-session-idle.tooltip=\u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0431\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0441\u0435\u0441\u0441\u0438\u0438. \u0412\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D \u0434\u043B\u044F \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u0445\u043E\u0442\u044F \u0431\u044B \u0440\u0430\u0437 \u0437\u0430 \u044D\u0442\u043E\u0442 \u043F\u0435\u0440\u0438\u043E\u0434, \u0438\u043D\u0430\u0447\u0435 \u0441\u0435\u0441\u0441\u0438\u044F \u0438\u0441\u0442\u0435\u0447\u0435\u0442.
-access-token-lifespan=\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C \u0436\u0438\u0437\u043D\u0438 \u0442\u043E\u043A\u0435\u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-access-token-lifespan.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0442\u043E\u043A\u0435\u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u0430. \u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u0443\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043A\u0430\u043A \u043C\u043E\u0436\u043D\u043E \u0431\u043B\u0438\u0436\u0435 \u043A \u0442\u0430\u0439\u043C\u0430\u0443\u0442\u0443 SSO.
-access-token-lifespan-for-implicit-flow=\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C \u0436\u0438\u0437\u043D\u0438 \u0442\u043E\u043A\u0435\u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u0434\u043B\u044F Implicit Flow
-access-token-lifespan-for-implicit-flow.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0442\u043E\u043A\u0435\u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043F\u043E\u0441\u043B\u0435 \u0442\u043E\u0433\u043E \u043A\u0430\u043A \u0441\u0435\u0441\u0441\u0438\u044F \u0442\u043E\u043A\u0435\u043D\u0430 OpenID Connect Implicit Flow \u0438\u0441\u0442\u0435\u043A\u043B\u0430. \u042D\u0442\u043E \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u043C\u043E\u0436\u043D\u043E \u0431\u043B\u0438\u0436\u0435 \u043A \u0442\u0430\u0439\u043C\u0430\u0443\u0442\u0443 SSO. \u041D\u0435\u0442 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u0438 \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u0432\u043E \u0432\u0440\u0435\u043C\u044F Implicit Flow, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u044D\u0442\u043E\u0442 \u0442\u0430\u0439\u043C\u0430\u0443\u0442 \u043E\u0442\u043B\u0438\u0447\u0430\u0435\u0442\u0441\u044F \u043E\u0442 '\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u0436\u0438\u0437\u043D\u0438 \u0442\u043E\u043A\u0435\u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u0430'
-client-login-timeout=\u0422\u0430\u0439\u043C\u0430\u0443\u0442 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-login-timeout.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430 access token. \u041E\u0431\u044B\u0447\u043D\u043E \u0443\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0440\u0430\u0432\u043D\u044B\u043C 1 \u043C\u0438\u043D\u0443\u0442\u0435.
-login-timeout=\u0422\u0430\u0439\u043C\u0430\u0443\u0442 \u0432\u0445\u043E\u0434\u0430
-login-timeout.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0432\u0445\u043E\u0434\u0430. \u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 (30 \u043C\u0438\u043D\u0443\u0442 \u0438 \u0431\u043E\u043B\u0435\u0435).
-login-action-timeout=\u0422\u0430\u0439\u043C\u0430\u0443\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E \u0432\u0445\u043E\u0434\u0443
-login-action-timeout.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F, \u0437\u0430 \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0434\u043E\u043B\u0436\u0435\u043D \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043F\u043E\u0441\u043B\u0435 \u0432\u0445\u043E\u0434\u0430, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F \u0438\u043B\u0438 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u043E\u0433\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u0430\u0440\u043E\u043B\u044F. \u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 (5 \u043C\u0438\u043D\u0443\u0442 \u0438 \u0431\u043E\u043B\u0435\u0435).
-headers=\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438
-brute-force-detection=\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435 Brute Force
+realm-detail.enabled.tooltip=Пользователи и клиенты могут получить доступ к Realm только если он включен
+realm-detail.oidc-endpoints.tooltip=Показать конфигурацию конечных точек OpenID Connect
+registrationAllowed=Самостоятельная регистрация пользователей
+registrationAllowed.tooltip=Включить/выключить страницу регистрации. Ссылка для регистрации будет также показана на странице входа.
+registrationEmailAsUsername=E-mail как имя пользователя
+registrationEmailAsUsername.tooltip=Если включено, то на форме регистрации поле имени пользователя будет скрыто и в качестве имени пользователя для новых пользователей будет использоваться E-mail.
+editUsernameAllowed=Редактируемое имя пользователя
+editUsernameAllowed.tooltip=Если включено,то имя пользователя можно будет отредактировать, иначе оно будет доступным только для чтения.
+resetPasswordAllowed=Сброс пароля
+resetPasswordAllowed.tooltip=Показывает ссылку на странице входа для пользователя, по переходу на которую пользователь сможет восстановить свои данные для входа.
+rememberMe=Запомнить меня
+rememberMe.tooltip=Показать чекбокс на странице входа, чтобы разрешить пользователю запомнить вход в учетную запись в случае если браузерная сессия устареет.
+loginWithEmailAllowed=Вход по E-mail
+loginWithEmailAllowed.tooltip=Разрешает пользователям входить с помощью E-mail.
+duplicateEmailsAllowed=Дублирующиеся E-mail
+duplicateEmailsAllowed.tooltip=Разрешает разным пользователям иметь один и тот же E-mail. Изменение этой настройки также очистит пользовательский кэш. После выключения поддержки дублирующихся email рекомендуется вручную почистить в базе данных ограничения по E-mail существующим пользователям.
+verifyEmail=Подтверждение E-mail
+verifyEmail.tooltip=Требует у пользователя подтвердить свой E-mail при первом входе в учетную запись.
+sslRequired=Требует SSL
+sslRequired.option.all=все запросы
+sslRequired.option.external=внешние запросы
+sslRequired.option.none=нет
+sslRequired.tooltip=Требуется ли HTTPS? 'нет' означает, что HTTPS не требуется для клиентов с любым IP адресом. 'Внешние запросы' означает, что localhost и внутренние IP адреса могут получить доступ без HTTPS. 'Все запросы' означает, что HTTPS требуется вне зависимости от IP адреса.
+publicKeys=Публичные ключи
+publicKey=Публичный ключ
+privateKey=Приватный ключ
+gen-new-keys=Сгенерировать новый ключ
+certificate=Сертификат
+host=Сервер
+smtp-host=SMTP сервер
+port=Порт
+smtp-port=SMTP порт (по умолчанию 25)
+from=От
+sender-email-addr=E-mail отправителя
+enable-ssl=Включить SSL
+enable-start-tls=Включить StartTLS
+enable-auth=Включить аутентификацию
+username=Имя пользователя
+login-username=Имя пользователя для входа
+password=Пароль
+login-password=Пароль для входа
+login-theme=Тема страницы входа
+login-theme.tooltip=Выберите тему для страниц входа, временного одноразового пароля (TOTP), выдачи разрешений, регистрации и восстановления пароля.
+account-theme=Тема учетной записи
+account-theme.tooltip=Выберите тему для управления учетной записью пользователя.
+admin-console-theme=Тема консоли администратора
+select-theme-admin-console=Выберите тему для консоли администратора.
+email-theme=Тема для E-mail
+select-theme-email=Выберите тему для E-mail, которые будут отсылаться с сервера.
+i18n-enabled=Интернационализация
+supported-locales=Поддерживаемые языки
+supported-locales.placeholder=Выберите язык и нажмите Enter
+default-locale=Язык по умолчанию
+realm-cache-clear=Кэш Realm
+realm-cache-clear.tooltip=Удалить все записи в кэше realm (удалит все записи для всех realm)
+user-cache-clear=Кэш пользователей
+user-cache-clear.tooltip=Очистить все записи в пользовательском кэше (это удалит записи для всех realm)
+keys-cache-clear=Кэш ключей
+keys-cache-clear.tooltip=Очистить все записи в кэше внешних публичных ключей. Эти ключи внешних ключей или провайдеров идентификации. (это очистит все записи для всех realm)
+revoke-refresh-token=Одноразовые токены обновления
+revoke-refresh-token.tooltip=Если включено, то токены обновления могут быть использованы один раз. Иначе токен отзываться не будет и может использоваться многократно.
+sso-session-idle=Таймаут сессии SSO
+seconds=секунд
+minutes=минут
+hours=часов
+days=дней
+sso-session-max=Ограничение сессии SSO
+sso-session-idle.tooltip=Допустимое время бездействия сессии. По истечении этого времени токены и браузерные сессии становятся невалидными.
+sso-session-max.tooltip=Максимальное время до того, как истечет сессия. По истечении этого времени токены и браузерные сессии становятся невалидными.
+offline-session-idle=Таймаут оффлайн сессии
+offline-session-idle.tooltip=Допустимое время бездействия оффлайн сессии. Вам необходимо использовать оффлайн токен для обновления хотя бы раз за этот период, иначе сессия истечет.
+access-token-lifespan=Продолжительность жизни токена доступа
+access-token-lifespan.tooltip=Максимальное время действия токена доступа. Значение рекомендуется устанавливать как можно ближе к таймауту SSO.
+access-token-lifespan-for-implicit-flow=Продолжительность жизни токена доступа для Implicit Flow
+access-token-lifespan-for-implicit-flow.tooltip=Максимальное время действия токена доступа после того как сессия токена OpenID Connect Implicit Flow истекла. Это значение рекомендуется установить как можно ближе к таймауту SSO. Нет возможности обновить токен во время Implicit Flow, поэтому этот таймаут отличается от 'Продолжительности жизни токена доступа'
+client-login-timeout=Таймаут авторизации клиента
+client-login-timeout.tooltip=Максимальное время клиента для завершения протокола access token. Обычно устанавливается равным 1-ой минуте.
+login-timeout=Таймаут входа
+login-timeout.tooltip=Максимальное время пользователя для завершения входа. Рекомендуется установить относительно большое значение (30 минут и более).
+login-action-timeout=Таймаут действий по входу
+login-action-timeout.tooltip=Максимальное время, за которое пользователь должен выполнить и завершить действие после входа. Например, обновление пароля или конфигурация одноразового временного пароля. Рекомендуется установить относительно большое значение (5 минут и более).
+headers=Заголовки
+brute-force-detection=Определение Brute Force
x-frame-options=X-Frame-Options
-x-frame-options-tooltip=\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u043D\u0435 \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430\u043C \u0431\u044B\u0442\u044C \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u043C\u0438 \u0432 iframe \u0441\u0442\u043E\u0440\u043E\u043D\u043D\u0438\u0445 \u0441\u0430\u0439\u0442\u043E\u0432 (\u043F\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0434\u043B\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0439 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438)
+x-frame-options-tooltip=Значение по умолчанию не позволяет страницам быть включенными в iframe сторонних сайтов (перейдите по ссылке для получения дополнительной информации)
content-sec-policy=Content-Security-Policy
-content-sec-policy-tooltip=\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u043D\u0435 \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430\u043C \u0431\u044B\u0442\u044C \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u043C\u0438 \u0432 iframe \u0441\u0442\u043E\u0440\u043E\u043D\u043D\u0438\u0445 \u0441\u0430\u0439\u0442\u043E\u0432 (\u043F\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0434\u043B\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0439 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438)
+content-sec-policy-tooltip=Значение по умолчанию не позволяет страницам быть включенными в iframe сторонних сайтов (перейдите по ссылке для получения дополнительной информации)
content-type-options=X-Content-Type-Options
-content-type-options-tooltip=\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u043D\u0435 \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u043C Internet Explorer \u0438 Google Chrome \u0432\u044B\u0447\u0438\u0441\u043B\u044F\u0442\u044C \u0442\u0438\u043F \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043E\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u0430\u043B\u044C\u0448\u0435 \u043E\u0442 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0433\u043E \u0442\u0438\u043F\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E (\u043F\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0434\u043B\u044F \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0439 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438)
-max-login-failures=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0432\u0445\u043E\u0434\u0430
-max-login-failures.tooltip=\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0432\u0445\u043E\u0434\u0430 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C, \u043F\u043E\u043A\u0430 \u044D\u0442\u043E \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u043E.
-wait-increment=\u041F\u043E\u0440\u043E\u0433 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F
-wait-increment.tooltip=\u0415\u0441\u043B\u0438 \u043F\u043E\u0440\u043E\u0433 \u043E\u0448\u0438\u0431\u043E\u043A \u043F\u0440\u0435\u0432\u044B\u0448\u0435\u043D, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D?
-quick-login-check-millis=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434 \u043C\u0435\u0436\u0434\u0443 \u043F\u043E\u043F\u044B\u0442\u043A\u0430\u043C\u0438 \u0432\u0445\u043E\u0434\u0430
-quick-login-check-millis.tooltip=\u0415\u0441\u043B\u0438 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u044F\u0442 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0447\u0430\u0441\u0442\u043E, \u0442\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C.
-min-quick-login-wait=\u041C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 \u0431\u044B\u0441\u0442\u0440\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430
-min-quick-login-wait.tooltip=\u041A\u0430\u043A \u0434\u043E\u043B\u0433\u043E \u0436\u0434\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u043E\u0439 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u0431\u044B\u0441\u0442\u0440\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430.
-max-wait=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435
-max-wait.tooltip=\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F, \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D.
-failure-reset-time=\u0412\u0440\u0435\u043C\u044F \u0441\u0431\u0440\u043E\u0441\u0430 \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A
-failure-reset-time.tooltip=\u0427\u0435\u0440\u0435\u0437 \u043A\u0430\u043A\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0441\u0447\u0435\u0442\u0447\u0438\u043A \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0431\u0443\u0434\u0435\u0442 \u0441\u0431\u0440\u043E\u0448\u0435\u043D?
-realm-tab-login=\u0412\u0445\u043E\u0434
-realm-tab-keys=\u041A\u043B\u044E\u0447\u0438
-realm-tab-email=Email
-realm-tab-themes=\u0422\u0435\u043C\u044B
-realm-tab-cache=\u041A\u044D\u0448
-realm-tab-tokens=\u0422\u043E\u043A\u0435\u043D\u044B
-realm-tab-client-initial-access=\u041F\u0435\u0440\u0432\u043E\u043D\u0430\u0447\u0430\u043B\u044C\u043D\u044B\u0435 \u0442\u043E\u043A\u0435\u043D\u044B \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-realm-tab-security-defenses=\u0417\u0430\u0449\u0438\u0442\u0430 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438
-realm-tab-general=\u0413\u043B\u0430\u0432\u043D\u0430\u044F
-add-realm=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C realm
+content-type-options-tooltip=Значение по умолчанию не позволяет браузерам Internet Explorer и Google Chrome вычислять тип содержимого в ответе от сервера дальше от объявленного типа содержимого (перейдите по ссылке для получения дополнительной информации)
+max-login-failures=Максимальное количество неудачных попыток входа
+max-login-failures.tooltip=Количество неудачных попыток входа до блокировки пользователя.
+wait-increment=Порог ожидания
+wait-increment.tooltip=Если порог ошибок превышен, сколько времени пользователь будет заблокирован?
+quick-login-check-millis=Проверка количества миллисекунд между попытками входа
+quick-login-check-millis.tooltip=Если попытки аутентификации происходят слишком часто, то пользователя необходимо заблокировать.
+min-quick-login-wait=Минимальное ожидание быстрого входа
+min-quick-login-wait.tooltip=Как долго ждать после неудачной попытки быстрого входа.
+max-wait=Максимальное ожидание
+max-wait.tooltip=Максимальное время, на которое пользователь будет заблокирован.
+failure-reset-time=Время сброса неудачных попыток
+failure-reset-time.tooltip=Через какое время счетчик неудачных попыток будет сброшен?
+realm-tab-login=Вход
+realm-tab-keys=Ключи
+realm-tab-email=E-mail
+realm-tab-themes=Темы
+realm-tab-cache=Кэш
+realm-tab-tokens=Токены
+realm-tab-client-initial-access=Первоначальные токены доступа
+realm-tab-security-defenses=Защита безопасности
+realm-tab-general=Главная
+add-realm=Добавить realm
#Session settings
-realm-sessions=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 Realm
-revocation=\u041E\u0442\u0437\u044B\u0432
-logout-all=\u0420\u0430\u0437\u043B\u043E\u0433\u0438\u043D\u0438\u0442\u044C \u0432\u0441\u0435 \u0441\u0435\u0441\u0441\u0438\u0438
-active-sessions=\u0410\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0441\u0435\u0441\u0441\u0438\u0438
-sessions=\u0421\u0435\u0441\u0441\u0438\u0438
-not-before=\u041D\u0435 \u0440\u0430\u043D\u0435\u0435 \u0447\u0435\u043C
-not-before.tooltip=\u041E\u0442\u043E\u0437\u0432\u0430\u0442\u044C \u043B\u044E\u0431\u044B\u0435 \u0442\u043E\u043A\u0435\u043D\u044B, \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0435 \u0440\u0430\u043D\u0435\u0435 \u044D\u0442\u043E\u0439 \u0434\u0430\u0442\u044B.
-set-to-now=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043D\u0430 \u0441\u0435\u0439\u0447\u0430\u0441
-push=\u0420\u0430\u0437\u043E\u0441\u043B\u0430\u0442\u044C
-push.tooltip=\u0423\u0432\u0435\u0434\u043E\u043C\u0438\u0442\u044C \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0438\u043C\u0435\u044E\u0449\u0435\u0433\u043E URL \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430, \u043E \u043D\u043E\u0432\u043E\u0439 \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0435 \u043E\u0442\u0437\u044B\u0432\u0430 \u0442\u043E\u043A\u0435\u043D\u0430.
+realm-sessions=Настройки Realm
+revocation=Отзыв
+logout-all=Разлогинить все сессии
+active-sessions=Активные сессии
+sessions=Сессии
+not-before=Не ранее чем
+not-before.tooltip=Отозвать любые токены, выданные ранее этой даты.
+set-to-now=Установить на сейчас
+push=Разослать
+push.tooltip=Уведомить каждого клиента, имеющего URL администратора, о новой политике отзыва токена.
#Protocol Mapper
-usermodel.prop.label=\u0421\u0432\u043E\u0439\u0441\u0442\u0432\u043E
-usermodel.prop.tooltip=\u0418\u043C\u044F \u0441\u0432\u043E\u0439\u0441\u0442\u0432\u0430 \u043C\u0435\u0442\u043E\u0434\u0430 \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 UserModel. \u0414\u043B\u044F \u043F\u0440\u0438\u043C\u0435\u0440\u0430, \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 'email' \u0431\u0443\u0434\u0435\u0442 \u0441\u0441\u044B\u043B\u043A\u043E\u0439 \u043D\u0430 \u043C\u0435\u0442\u043E\u0434 UserModel.getEmail().
-usermodel.attr.label=\u0410\u0442\u0440\u0438\u0431\u0443\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-usermodel.attr.tooltip=\u0418\u043C\u044F \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u043E\u0433\u043E \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0438\u043C\u0435\u043D\u0435\u043C \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430, \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u043C \u0441 UserModel.attribute.
-userSession.modelNote.label=\u0417\u0430\u043C\u0435\u0442\u043A\u0430 \u0441\u0435\u0441\u0441\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-userSession.modelNote.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0446\u0435\u0434\u0443\u0440\u044B \u0437\u0430\u043C\u0435\u0442\u043A\u0438 \u0441\u0435\u0441\u0441\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u043C \u0441 UserSessionModel.note.
-multivalued.label=\u041D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439
-multivalued.tooltip=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044F, \u0435\u0441\u043B\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439. \u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D, \u0442\u043E \u0441\u043F\u0438\u0441\u043E\u043A \u0432\u0441\u0435\u0445 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0431\u0443\u0434\u0435\u0442 \u043F\u0440\u0435\u0442\u0435\u043D\u0434\u043E\u0432\u0430\u0442\u044C \u043D\u0430 \u044D\u0442\u043E\u0442 \u0430\u0442\u0440\u0438\u0431\u0443\u0442. \u0412 \u043F\u0440\u043E\u0442\u0438\u0432\u043D\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435 \u0432\u044B\u0431\u0438\u0440\u0430\u0442\u044C\u0441\u044F \u0431\u0443\u0434\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0435\u0440\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435
-selectRole.label=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u043E\u043B\u044C
-selectRole.tooltip=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0440\u043E\u043B\u044C \u0432 \u0442\u0435\u043A\u0441\u0442\u043E\u0432\u043E\u0435 \u043F\u043E\u043B\u0435 \u0441\u043B\u0435\u0432\u0430, \u0438\u043B\u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0443, \u0447\u0442\u043E\u0431\u044B \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u0436\u0435\u043B\u0430\u0435\u043C\u0443\u044E \u0440\u043E\u043B\u044C.
-tokenClaimName.label=\u0418\u043C\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 \u0432 \u0442\u043E\u043A\u0435\u043D\u0435
-tokenClaimName.tooltip=\u0418\u043C\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438 \u0435\u0435 \u0432 \u0442\u043E\u043A\u0435\u043D. \u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0435 \u0438\u043C\u044F, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 'address.street'. \u0412 \u0442\u0430\u043A\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043E\u0437\u0434\u0430\u043D \u0432\u043B\u043E\u0436\u0435\u043D\u043D\u044B\u0439 json \u043E\u0431\u044A\u0435\u043A\u0442.
-jsonType.label=\u0422\u0438\u043F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 JSON
-jsonType.tooltip=\u0422\u0438\u043F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 \u0432 JSON, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u043B\u0436\u0435\u043D \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043F\u0440\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0438 \u0435\u0435 \u0432 \u0442\u043E\u043A\u0435\u043D. \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F long, int, boolean, \u0438 String.
-includeInIdToken.label=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u0442\u043E\u043A\u0435\u043D ID
-includeInIdToken.tooltip=\u0414\u043E\u043B\u0436\u043D\u043E \u043B\u0438 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0431\u044B\u0442\u044C \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u0432 \u0442\u043E\u043A\u0435\u043D ID?
-includeInAccessToken.label=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u0442\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-includeInAccessToken.tooltip=\u0414\u043E\u043B\u0436\u043D\u043E \u043B\u0438 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0431\u044B\u0442\u044C \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u0432 \u0442\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430?
-
+usermodel.prop.label=Свойство
+usermodel.prop.tooltip=Имя свойства метода в интерфейсе UserModel. Для примера, значение 'email' будет ссылкой на метод UserModel.getEmail().
+usermodel.attr.label=Атрибут пользователя
+usermodel.attr.tooltip=Имя сохраненного атрибута пользователя, которое является именем атрибута, согласованным с UserModel.attribute.
+userSession.modelNote.label=Заметка сессии пользователя
+userSession.modelNote.tooltip=Наименование процедуры заметки сессии пользователя согласованным с UserSessionModel.note.
+multivalued.label=Несколько значений
+multivalued.tooltip=Отображается, если атрибут поддерживает несколько значений. Если включен, то список всех значений будет претендовать на этот атрибут. В противном случае выбираться будет только первое значение
+selectRole.label=Выберите роль
+selectRole.tooltip=Введите роль в текстовом поле слева, или нажмите на кнопку, чтобы выбрать желаемую роль.
+tokenClaimName.label=Имя переменной в токене
+tokenClaimName.tooltip=Имя переменной при добавлении ее в токен. Может быть полное имя, например 'address.street'. В таком случае будет создан вложенный json объект.
+jsonType.label=Тип переменной JSON
+jsonType.tooltip=Тип переменной в JSON, который должен использоваться при добавлении ее в токен. Допустимые значения long, int, boolean, и String.
+includeInIdToken.label=Добавить в токен ID
+includeInIdToken.tooltip=Должно ли значение быть добавлено в токен ID?
+includeInAccessToken.label=Добавить в токен доступа
+includeInAccessToken.tooltip=Должно ли значение быть добавлено в токен доступа?
+includeInUserInfo.label=Добавить в информацию о пользователе
+includeInUserInfo.tooltip=Должно ли требование быть добавлено в информацию о пользователе?
+usermodel.clientRoleMapping.clientId.label=ID клиента
+usermodel.clientRoleMapping.clientId.tooltip=ID клиента для сопоставления ролей
+usermodel.clientRoleMapping.rolePrefix.label=Префикс ролей клиента
+usermodel.clientRoleMapping.rolePrefix.tooltip=Префикс для каждой роли клиента (опционально).
+usermodel.realmRoleMapping.rolePrefix.label=Префикс ролей Realm
+usermodel.realmRoleMapping.rolePrefix.tooltip=Префикс для каждой роли Realm (опционально).
+sectorIdentifierUri.label=Сектор идентификации URI
+sectorIdentifierUri.tooltip=Провайдеры, использующие пары вспомогательных значений и поддерживающие динамическую регистрацию клиентов ДОЛЖНЫ использовать sector_identified_uri параметр. Это обеспечивает способ для группы сайтов под общим административным контролем, чтобы иметь последовательные попарные значения независимо от индивидуальных доменных имен. Это также обеспечивает способ для клиентов для изменения redirect_uri доменов, не имещющих возможности перерегистрации всех своих пользователей.
+pairwiseSubAlgorithmSalt.label=Соль
+pairwiseSubAlgorithmSalt.tooltip=Соль, используемая для вычисления парного субъекта идентификатора. Если поле не заполнено, то соль будет сгенерирована.
+addressClaim.street.label=Имя пользовательского атрибута, обозначающего Улицу
+addressClaim.street.tooltip=Имя пользовательского атрибута, которое будет использоваться для сопоставления атрибута 'street_address' внутри атрибута 'address' токена. По умолчанию 'street' .
+addressClaim.locality.label=Имя пользовательского атрибута, обозначающего Местонахождение
+addressClaim.locality.tooltip=Имя пользовательского атрибута, которое будет использоваться для сопоставления атрибута 'locality' внутри атрибута 'address' токена. По умолчанию 'locality' .
+addressClaim.region.label=Имя пользовательского атрибута, обозначающего Регион
+addressClaim.region.tooltip=Имя пользовательского атрибута, которое будет использоваться для сопоставления атрибута 'region' внутри атрибута 'address' токена. По умолчанию 'region' .
+addressClaim.postal_code.label=Имя пользовательского атрибута, обозначающего Почтовый индекс
+addressClaim.postal_code.tooltip=Имя пользоватеслького атрибута, котоое будет использоваться для сопоставления атрибута 'postal_code' внутри атрибута 'address' токена. По умолчанию 'postal_code' .
+addressClaim.country.label=Имя пользовательского атрибута, обозначающего Страна
+addressClaim.country.tooltip=Имя пользовательского атрибута, которое будет использоватлься для сопоставления атрибута 'country' внутри атрибута 'address' токена. По умолчанию 'country' .
+addressClaim.formatted.label=Имя пользовательсокого атрибута, обозначающего Формитированный адрес
+addressClaim.formatted.tooltip=Имя пользовательского атрибута, которое будет использоваться для сопоставления атрибута 'formatted' внутри атрибута 'address' токена. По умолчанию 'formatted' .
# client details
-clients.tooltip=\u041A\u043B\u0438\u0435\u043D\u0442\u044B \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043D\u044B\u0445 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439 \u0438 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u043E\u0432 \u0432 realm. \u042D\u0442\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u044B \u043C\u043E\u0433\u0443\u0442 \u0437\u0430\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C \u0432\u0445\u043E\u0434. \u0412\u044B \u0442\u0430\u043A\u0436\u0435 \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0440\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-search.placeholder=\u041F\u043E\u0438\u0441\u043A...
-create=\u0421\u043E\u0437\u0434\u0430\u0442\u044C
-import=\u0418\u043C\u043F\u043E\u0440\u0442
-client-id=ID \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-base-url=\u0411\u0430\u0437\u043E\u0432\u044B\u0439 URL
-actions=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F
-not-defined=\u041D\u0435 \u0437\u0430\u0434\u0430\u043D
-edit=\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C
-delete=\u0423\u0434\u0430\u043B\u0438\u0442\u044C
-no-results=\u041D\u0435\u0442 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432
-no-clients-available=\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-add-client=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-select-file=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0444\u0430\u0439\u043B
-view-details=\u0421\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0434\u0435\u0442\u0430\u043B\u0438
-clear-import=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0438\u043C\u043F\u043E\u0440\u0442
-client-id.tooltip=\u0417\u0430\u0434\u0430\u0435\u0442 ID, \u0441\u0441\u044B\u043B\u0430\u044E\u0449\u0438\u0439\u0441\u044F \u0432 URI \u0438 \u0442\u043E\u043A\u0435\u043D\u0430\u0445. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 'my-client'. \u0414\u043B\u044F SAML \u044D\u0442\u043E \u0442\u0430\u043A\u0436\u0435 \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u043E\u0435 \u0438\u043C\u044F \u0438\u0437\u0434\u0430\u0442\u0435\u043B\u044F \u0434\u043B\u044F \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-client.name.tooltip=\u0417\u0430\u0434\u0430\u0435\u0442 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 'My Client'. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043A\u043B\u044E\u0447\u0438 \u0434\u043B\u044F \u043B\u043E\u043A\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440\\: ${my_client}
-client.enabled.tooltip=\u041E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u044B \u043D\u0435 \u043C\u043E\u0433\u0443\u0442 \u0438\u043D\u0438\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0445\u043E\u0434 \u0438\u043B\u0438 \u0438\u043C\u0435\u0442\u044C \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D\u044B \u0434\u043E\u0441\u0442\u0443\u043F\u0430.
-consent-required=\u041D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435
-consent-required.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0434\u043E\u043B\u0436\u043D\u044B \u0434\u0430\u0442\u044C \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u0434\u043E\u0441\u0442\u0443\u043F \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u043C\u0443 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044E.
-client-protocol=\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-protocol.tooltip='OpenID connect' \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043B\u0438\u0447\u043D\u043E\u0441\u0442\u044C \u043A\u043E\u043D\u0435\u0447\u043D\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F, \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u043D\u0430 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043D\u0430 \u0421\u0435\u0440\u0432\u0435\u0440\u0435 \u0410\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438.'SAML' \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0432\u0435\u0431-\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0432\u043A\u043B\u044E\u0447\u0430\u044F \u043A\u0440\u043E\u0441\u0441\u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0435 \u0442\u043E\u0447\u043A\u0438 \u0442\u0435\u0445\u043D\u043E\u043B\u043E\u0433\u0438\u0438 \u0435\u0434\u0438\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430 (SSO) \u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0449\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u044B \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438, \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u043D\u0430 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438.
-access-type=\u0422\u0438\u043F \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-access-type.tooltip='Confidential' \u043A\u043B\u0438\u0435\u043D\u0442\u044B \u0442\u0440\u0435\u0431\u0443\u044E\u0442 \u0441\u0435\u043A\u0440\u0435\u0442 \u0434\u043B\u044F \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430 \u0432\u0445\u043E\u0434\u0430. 'Public' \u043A\u043B\u0438\u0435\u043D\u0442\u0430\u043C \u0441\u0435\u043A\u0440\u0435\u0442 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F. 'Bearer-only' \u043A\u043B\u0438\u0435\u043D\u0442\u044B \u0438 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u044B \u043D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u0443\u044E\u0442 \u0432\u0445\u043E\u0434.
-standard-flow-enabled=Standard Flow \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-standard-flow-enabled.tooltip=\u0412\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u043E\u0435 OpenID Connect \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435, \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u043D\u043E\u0435 \u043D\u0430 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0441 \u043A\u043E\u0434\u043E\u043C \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0412 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u0445 OpenID Connect \u0438\u043B\u0438 OAuth2 \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0439 \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u0442 'Authorization Code Flow' \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-implicit-flow-enabled=Implicit Flow \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-implicit-flow-enabled.tooltip=\u0412\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443 OpenID Connect \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F, \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u043D\u0430 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0431\u0435\u0437 \u043A\u043E\u0434\u0430 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0412 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u0445 OpenID Connect \u0438\u043B\u0438 OAuth2 \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0439 \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443 'Implicit Flow' \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-direct-access-grants-enabled=Direct Access Grants \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-direct-access-grants-enabled.tooltip=\u0412\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443 Direct Access Grants, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u043A\u043B\u0438\u0435\u043D\u0442 \u0438\u043C\u0435\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F \u043A \u0438\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0438 \u043F\u0430\u0440\u043E\u043B\u044F \u0438 \u043E\u0431\u043C\u0435\u043D\u0438\u0432\u0430\u0435\u0442 \u0438\u0445 \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u043C Keycloak \u043D\u0430 \u0442\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430. \u0412 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u0445 OAuth2 \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443 'Resource Owner Password Credentials Grant' \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-service-accounts-enabled=Service Accounts \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-service-accounts-enabled.tooltip=\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0412\u0430\u043C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0432 Keycloak \u0438 \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u0441\u043F\u0435\u0446\u0438\u0430\u043B\u044C\u043D\u043E \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u0412 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u0445 OAuth2 \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443 'Client Credentials Grant' \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-include-authnstatement=\u0412\u043A\u043B\u044E\u0447\u0430\u0442\u044C \u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0435 \u0417\u0430\u044F\u0432\u043A\u0438
-include-authnstatement.tooltip=\u0414\u043E\u043B\u0436\u043D\u044B \u043B\u0438 \u0437\u0430\u044F\u0432\u043A\u0438 \u043D\u0430 \u043C\u0435\u0442\u043E\u0434\u044B \u0438 \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043C\u0435\u0442\u043A\u0438 \u0431\u044B\u0442\u044C \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u044B \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043D\u0430 \u0432\u0445\u043E\u0434?
-sign-documents=\u041F\u043E\u0434\u043F\u0438\u0441\u044C \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u0432
-sign-documents.tooltip=\u0414\u043E\u043B\u0436\u043D\u044B \u043B\u0438 SAML \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u044B \u0431\u044B\u0442\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u044B \u0432 realm?
+clients.tooltip=Клиенты доверенных браузерных приложений и веб-сервисов в realm. Эти клиенты могут запрашивать вход. Вы также можете определить конкретные роли клиента.
+search.placeholder=Поиск...
+create=Создать
+import=Импорт
+client-id=ID клиента
+base-url=Базовый URL
+actions=Действия
+not-defined=Не задан
+edit=Редактировать
+delete=Удалить
+no-results=Нет результатов
+no-clients-available=Нет доступных клиентов
+add-client=Добавить клиента
+select-file=Выберите файл
+view-details=Смотреть детали
+clear-import=Очистить импорт
+client-id.tooltip=Задает идентификатор, указываемый в URI и в токенах. Например 'my-client'. Для SAML это также ожидаемое имя издателя для запросов аутентификации
+client.name.tooltip=Задает отображаемое название клиента. Например 'My Client'. Поддерживает ключи для локализованных значений. Например\\: ${my_client}
+client.enabled.tooltip=Отключенные клиенты не могут инициировать вход или иметь возможность получить токены доступа.
+consent-required=Необходимо согласие
+consent-required.tooltip=Если включено, пользователи должны дать согласие на доступ клиентскому приложению.
+client-protocol=Протокол клиента
+client-protocol.tooltip='OpenID connect' разрешает клиентам проверить личность конечного пользователя, основанного на выполнении аутентификации на Сервере Авторизации.'SAML' включает веб-сценарии аутентификации и авторизации, включая кроссдоменные центры единого управления доступом (SSO) и использующие токены безопасности, содержащие заявления на передачу информации.
+access-type=Тип доступа
+access-type.tooltip='Confidential' клиенты требуют секрет для инициализации протокола входа. 'Public' клиентам секрет не требуется. 'Bearer-only' клиенты и веб-сервисы никогда не инициализируют вход.
+standard-flow-enabled=Standard Flow включен
+standard-flow-enabled.tooltip=Включает стандартное OpenID Connect перенаправление, основанное на аутентификации с кодом авторизации. В терминах OpenID Connect или OAuth2 спецификаций включает 'Authorization Code Flow' для этого клиента.
+implicit-flow-enabled=Implicit Flow включен
+implicit-flow-enabled.tooltip=Включает поддержку OpenID Connect перенаправления, основанного на аутентификации без кода авторизации. В терминах OpenID Connect или OAuth2 спецификаций включает поддержку 'Implicit Flow' для этого клиента.
+direct-access-grants-enabled=Direct Access Grants включен
+direct-access-grants-enabled.tooltip=Включает поддержку Direct Access Grants, которая означает, что клиент имеет доступ к имени пользователя и пароля и обменивает их напрямую с сервером Keycloak на токен доступа. В терминах OAuth2 спецификации означает поддержку 'Resource Owner Password Credentials Grant' для этого клиента.
+service-accounts-enabled=Service Accounts включен
+service-accounts-enabled.tooltip=Разрешает Вам аутентифицировать этого клиента в Keycloak и получить токен доступа специально для этого клиента. В терминах OAuth2 спецификации включает поддержку 'Client Credentials Grant' для этого клиента.
+include-authnstatement=Включать Аутентификационные Заявки
+include-authnstatement.tooltip=Должны ли заявки на методы и временные метки быть включены в ответе на вход?
+include-onetimeuse-condition=Включить условие одноразового использования
+include-onetimeuse-condition.tooltip=Должно ли условие одноразового использования быть включено в ответе на вход?
+sign-documents=Подпись документов
+sign-documents.tooltip=Должны ли SAML документы быть подписаны в realm?
+sign-documents-redirect-enable-key-info-ext=Оптимизация REDIRECT поиска подписанного ключа
+sign-documents-redirect-enable-key-info-ext.tooltip=При подписи SAML документов при REDIRECT сопоставлении с SP, который обеспечивается безопасностью адаптера Keycloak, должен ли включать ID подписанного ключа в сообщение по протоколу SAML в <Extensions> элемент? Это оптимизирует валидацию сигнатуры, где в качестве проверки используется один ключ вместо попытки проверки каждого ключа во время валидации.
sign-assertions=Sign Assertions
-sign-assertions.tooltip=\u0414\u043E\u043B\u0436\u043D\u044B \u043B\u0438 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0432\u043D\u0443\u0442\u0440\u0438 SAML \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u0431\u044B\u0442\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u044B? \u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E\u0441\u0442\u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u0442\u044C \u0443\u0436\u0435 \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u044B.
-signature-algorithm=\u0410\u043B\u0433\u043E\u0440\u0438\u0442\u043C \u043F\u043E\u0434\u043F\u0438\u0441\u0438
-signature-algorithm.tooltip=\u0410\u043B\u0433\u043E\u0440\u0438\u0442\u043C \u043F\u043E\u0434\u043F\u0438\u0441\u0438, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C\u044B\u0439 \u0434\u043B\u044F \u043F\u043E\u0434\u043F\u0438\u0441\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u0432.
-canonicalization-method=\u041C\u0435\u0442\u043E\u0434 \u043A\u0430\u043D\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438
-canonicalization-method.tooltip=\u041C\u0435\u0442\u043E\u0434 \u043A\u0430\u043D\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u043B\u044F XML \u0441\u0438\u0433\u043D\u0430\u0442\u0443\u0440.
-encrypt-assertions=\u0417\u0430\u0448\u0438\u0444\u0440\u043E\u0432\u043A\u0430 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0439
-encrypt-assertions.tooltip=\u0414\u043E\u043B\u0436\u043D\u044B \u043B\u0438 SAML \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0431\u044B\u0442\u044C \u0437\u0430\u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u044B \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u044B\u043C \u043A\u043B\u044E\u0447\u043E\u043C \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044F AES?
-client-signature-required=\u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u043F\u043E\u0434\u043F\u0438\u0441\u044C \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-signature-required.tooltip=\u0411\u0443\u0434\u0435\u0442 \u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442 \u043F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u0442\u044C \u0441\u0432\u043E\u0438 saml \u0437\u0430\u043F\u0440\u043E\u0441\u044B \u0438 \u043E\u0442\u0432\u0435\u0442\u044B? \u0418 \u0434\u043E\u043B\u0436\u043D\u044B \u043B\u0438 \u043E\u043D\u0438 \u0431\u044B\u0442\u044C \u043F\u0440\u043E\u0432\u0430\u043B\u0438\u0434\u0438\u0440\u043E\u0432\u0430\u043D\u044B?
-force-post-binding=\u0424\u043E\u0440\u0441\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 POST
-force-post-binding.tooltip=\u0412\u0441\u0435\u0433\u0434\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C POST \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 \u0434\u043B\u044F \u043E\u0442\u0432\u0435\u0442\u043E\u0432.
-front-channel-logout=\u0412\u044B\u0445\u043E\u0434 \u0441 \u043F\u0435\u0440\u0435\u0434\u043D\u0435\u0433\u043E \u043A\u0430\u043D\u0430\u043B\u0430
-front-channel-logout.tooltip=\u041A\u043E\u0433\u0434\u0430 \u043F\u0440\u0430\u0432\u0438\u043B\u0430, \u0432\u044B\u0445\u043E\u0434 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043D\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u0415\u0441\u043B\u0438 \u043B\u043E\u0436\u044C, \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442 \u0444\u043E\u043D\u043E\u0432\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u0434\u043B\u044F \u0432\u044B\u0445\u043E\u0434\u0430 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043C\u044B.
-force-name-id-format=\u0424\u043E\u0440\u0441\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0444\u043E\u0440\u043C\u0430\u0442\u0430 ID
-force-name-id-format.tooltip=\u0418\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043F\u0440\u043E\u0448\u0435\u043D\u043D\u044B\u0439 NameID \u0444\u043E\u0440\u043C\u0430\u0442 \u0442\u0435\u043C\u044B \u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u0443\u044E \u043A\u043E\u043D\u0441\u043E\u043B\u044C \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430.
-name-id-format=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0444\u043E\u0440\u043C\u0430\u0442\u0430 ID
-name-id-format.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0444\u043E\u0440\u043C\u0430\u0442\u0430 ID \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u0432 \u0442\u0435\u043C\u0435.
-root-url=\u041A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 URL
-root-url.tooltip=\u041A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 URL \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043A \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u043C URL
-valid-redirect-uris=\u0412\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044F URI \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0439
-valid-redirect-uris.tooltip=\u0412\u0430\u043B\u0438\u0434\u0438\u0440\u0443\u0435\u0442 \u043F\u0430\u0442\u0442\u0435\u0440\u043D URI, \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D \u043F\u043E\u0441\u043B\u0435 \u0443\u0441\u043F\u0435\u0448\u043D\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430 \u0438\u043B\u0438 \u0432\u044B\u0445\u043E\u0434\u0430. \u041F\u0440\u043E\u0441\u0442\u044B\u0435 \u0441\u0441\u044B\u043B\u043A\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u044B. \u041D\u0430\u043F\u0440. 'http://example.com/*'. \u041E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0442\u0430\u043A\u0436\u0435 \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D, \u043D\u0430\u043F\u0440. /my/relative/path/*. \u041E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u0443\u0442\u0438 \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0433\u043E URL \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0438\u043B\u0438 \u0435\u0441\u043B\u0438 \u043E\u043D \u043D\u0435 \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D, \u0442\u043E \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0439 URL \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D. \u0414\u043B\u044F SAML \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0437\u0430\u0434\u0430\u0442\u044C \u0432\u0430\u043B\u0438\u0434\u043D\u044B\u0439 \u043F\u0430\u0442\u0442\u0435\u0440\u043D. \u0415\u0441\u043B\u0438 \u0412\u044B \u043F\u043E\u043B\u0430\u0433\u0430\u0435\u0442\u0435\u0441\u044C \u043D\u0430 \u0432\u043D\u0435\u0434\u0440\u0435\u043D\u043D\u044B\u0439 URL \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0437\u0430\u043A\u0430\u0437\u0447\u0438\u043A\u0430 \u0441 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u043C \u0432\u0445\u043E\u0434\u0430.
-base-url.tooltip=URL \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F, \u0435\u0441\u043B\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0438\u043B\u0438 \u0441\u0441\u044B\u043B\u043A\u0430 \u043E\u0431\u0440\u0430\u0442\u043D\u043E \u043D\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-admin-url=URL \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F
-admin-url.tooltip=URL \u0434\u043B\u044F \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u0432 \u0434\u0430\u043D\u043D\u043E\u043C \u043A\u043B\u0438\u0435\u043D\u0442\u0435. \u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 \u044D\u0442\u043E, \u0435\u0441\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0430\u0434\u0430\u043F\u0442\u0435\u0440 REST API. \u042D\u0442\u043E REST API \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u043B\u0430\u0442\u044C \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438 \u043E\u0442\u0437\u044B\u0432\u0430 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u041E\u0431\u044B\u0447\u043D\u043E \u0443\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0432 \u0431\u0430\u0437\u043E\u0432\u044B\u0439 URL \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-master-saml-processing-url=\u041E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 URL \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430 SAML
-master-saml-processing-url.tooltip=\u0415\u0441\u043B\u0438 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C, \u0442\u043E \u044D\u0442\u043E\u0442 URL \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u044F \u0434\u043B\u044F \u043E\u0431\u043E\u0438\u0445 SP's Assertion Consumer \u0438 Single Logout Services. \u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0438\u043D\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043B\u044C\u043D\u043E \u043F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043E \u0434\u043B\u044F \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u044F \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0432 \u0442\u043E\u043D\u043A\u043E\u0439 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0435 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043A\u043E\u043D\u0435\u0447\u043D\u044B\u0445 \u0442\u043E\u0447\u0435\u043A \u0434\u043E\u0441\u0442\u0443\u043F\u0430 SAML.
-idp-sso-url-ref=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 URL \u0434\u043B\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 IDP, \u0438\u043D\u0438\u0446\u0438\u0438\u0440\u0443\u044E\u0449\u0435\u0433\u043E SSO
-idp-sso-url-ref.tooltip=\u0418\u043C\u044F URL \u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442\u0430, \u043E\u0431\u043E\u0437\u043D\u0430\u0447\u0430\u044E\u0449\u0435\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0435\u0441\u043B\u0438 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B SSO \u0431\u044B\u043B \u043F\u0440\u043E\u0438\u043D\u0438\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438. \u041E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0447\u0442\u043E\u0431\u044B \u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0438\u043D\u0438\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 SSO \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438. URL \u0434\u043B\u044F \u0441\u0441\u044B\u043B\u043A\u0438 \u0432\u0430\u0448\u0435\u0433\u043E \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0432 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0432\u0438\u0434\u0435: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}
-idp-sso-relay-state=\u041F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F SSO \u0438\u043D\u0438\u0446\u0438\u0438\u0440\u0443\u044E\u0449\u0438\u043C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-idp-sso-relay-state.tooltip=\u041F\u0435\u0440\u0435\u0434\u0430\u0442\u044C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u043F\u043E\u0441\u043B\u0430\u0442\u044C \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 SAML \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u043C, \u043A\u043E\u0442\u043E\u0440\u044B\u043C \u0445\u043E\u0442\u0438\u0442\u0435 \u043F\u0440\u043E\u0438\u043D\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u0442\u044C SSO \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-web-origins=Web \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0438
-web-origins.tooltip=\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 CORS \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0430\u043C. \u0427\u0442\u043E\u0431\u044B \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0432\u0441\u0435\u043C \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0438 \u0441 \u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u043C\u0438 URI-\u0430\u0434\u0440\u0435\u0441\u0430\u043C\u0438 \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0438, \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 '+'. \u0427\u0442\u043E\u0431\u044B \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0432\u0441\u0435 \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0438, \u0434\u043E\u0431\u0430\u0432\u044C\u0442\u0435 '*'.
-fine-saml-endpoint-conf=\u0422\u043E\u043D\u043A\u0430\u044F \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043A\u043E\u043D\u0435\u0447\u043D\u044B\u0445 \u0442\u043E\u0447\u0435\u043A \u0434\u043E\u0441\u0442\u0443\u043F\u0430 SAML
-fine-saml-endpoint-conf.tooltip=\u0420\u0430\u0437\u0432\u0435\u0440\u043D\u0438\u0442\u0435 \u044D\u0442\u0443 \u0441\u0435\u043A\u0446\u0438\u0438, \u0447\u0442\u043E\u0431\u044B \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0442\u043E\u0447\u043D\u044B\u0435 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0434\u043B\u044F \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0435\u0434\u0438\u043D\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430.
-assertion-consumer-post-binding-url=\u0421\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 URL \u0434\u043B\u044F POST \u0437\u0430\u043F\u0440\u043E\u0441\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F
-assertion-consumer-post-binding-url.tooltip=SAML POST \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 URL \u0434\u043B\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043E\u0432 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F (\u0437\u0430\u043F\u0440\u043E\u0441\u044B \u0432\u0445\u043E\u0434\u0430). \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0435\u0441\u043B\u0438 \u043D\u0435 \u0438\u043C\u0435\u0435\u0442\u0435 URL \u0434\u043B\u044F \u043E\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043B\u0435\u043D\u0438\u044F \u0442\u0430\u043A\u043E\u0439 \u0441\u0432\u044F\u0437\u043A\u0438.
-assertion-consumer-redirect-binding-url=\u0421\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 URL \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0438 \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043E\u043C \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F
-assertion-consumer-redirect-binding-url.tooltip=SAML \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044F \u043D\u0430 \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 URL \u0434\u043B\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F (\u0437\u0430\u043F\u0440\u043E\u0441\u044B \u0432\u0445\u043E\u0434\u0430). \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0435\u0441\u043B\u0438 \u0432\u044B \u043D\u0435 \u0438\u043C\u0435\u0435\u0442\u0435 URL \u0434\u043B\u044F \u0442\u0430\u043A\u043E\u0433\u043E \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u044F.
-logout-service-post-binding-url=\u0421\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 URL \u0434\u043B\u044F \u0432\u044B\u0445\u043E\u0434\u0430 \u0438\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043B\u044F POST-\u043C\u0435\u0442\u043E\u0434\u0430
-logout-service-post-binding-url.tooltip=SAML POST \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 URL \u0434\u043B\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0435\u0434\u0438\u043D\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430. \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0435\u0441\u043B\u0438 \u0432\u044B \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0435 \u0440\u0430\u0437\u043B\u0438\u0447\u043D\u044B\u0435 \u0441\u0432\u044F\u0437\u0438
-logout-service-redir-binding-url=\u0421\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 URL \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0438 \u0434\u043B\u044F \u0432\u044B\u0445\u043E\u0434\u0430 \u0438\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u0430
-logout-service-redir-binding-url.tooltip=SAML \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0443\u0435\u0442 \u043D\u0430 \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 URL \u0434\u043B\u044F \u0435\u0434\u0438\u043D\u043E\u0439 \u0442\u043E\u0447\u043A\u0438 \u0432\u044B\u0445\u043E\u0434\u0430 \u0438\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043B\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432. \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0435\u0441\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0435 \u0440\u0430\u0437\u043B\u0438\u0447\u043D\u044B\u0435 \u0441\u0432\u044F\u0437\u0438.
+sign-assertions.tooltip=Должны ли утверждения внутри SAML документов быть подписаны? Устанавливает отсутствие необходимости подписывать уже подписанные документы.
+signature-algorithm=Алгоритм подписи
+signature-algorithm.tooltip=Алгоритм, используемый для подписи документов.
+canonicalization-method=Метод канонизации
+canonicalization-method.tooltip=Метод канонизации для XML сигнатур.
+encrypt-assertions=Зашифровка утверждений
+encrypt-assertions.tooltip=Должны ли SAML утверждения быть зашифрованы публичным ключом клиента, используя AES?
+client-signature-required=Подпись клиента обязательна
+client-signature-required.tooltip=Будет ли клиент подписывать свои saml запросы и ответы? И должны ли они быть провалидированы?
+force-post-binding=Принудительно использовать POST Binding
+force-post-binding.tooltip=Всегда использовать POST Binding для ответов.
+front-channel-logout=Выход с переднего канала
+front-channel-logout.tooltip=Когда правила, выход требует перенаправить браузер на клиента. Если ложь, сервер выполняет фоновый режим для выхода из системы.
+force-name-id-format=Принудительно использовать формат ID
+force-name-id-format.tooltip=Игнорирует запрошенный формат заголовка NameID и использует сконфигурированный через консоль администратора.
+name-id-format=Наименование формата ID
+name-id-format.tooltip=Наименование формата ID для использования в теме.
+root-url=Корневой URL
+root-url.tooltip=Корневой URL добавляется к относительным URL
+valid-redirect-uris=Валидация URI перенаправления
+valid-redirect-uris.tooltip=Валидирует паттерн URI, на который может быть перенаправлен браузер после успешного входа или выхода. Разрешены простые ссылки, напр. 'http://example.com/*'. Также допускается использовать относительный путь, напр. '/my/relative/path/*'. Относительные пути необходимо указывать относительно корневого URL клиента, или, если он не специфицирован, корневого URL сервера авторизации. Для SAML Вы должны задать валидный паттерн URI, если Вы полагаетесь на URL сервиса потребителя, внедренного в запрос авторизации.
+base-url.tooltip=Используемый URL по умолчанию. Используется в случае, если серверу требуется перенаправление или обратная ссылка на клиента.
+admin-url=URL администрирования приложения
+admin-url.tooltip=URL для доступа к интерфейсу администратора в заданном клиенте. Необходимо установить, если клиент поддерживает адаптер REST API. Это REST API разрешает серверу авторизации слать политики отзыва и прочие административные задачи. Обычно устанавливается значение, соответствующее базовому URL клиента.
+master-saml-processing-url=Основной URL обработчика SAML
+master-saml-processing-url.tooltip=Если URL сконфигурирован, то он будет каждый раз для связывания SP's Assertion Consumer и Single Logout Services. Может быть переопределен индивидуально для связывания каждого сервиса в тонкой настройке конфигурации конечных точек доступа SAML.
+idp-sso-url-ref=Наименование URL для поставщика идентификации IDP, инициирующего SSO
+idp-sso-url-ref.tooltip=Имя URL фрагмента, обозначающего клиента, если вы хотите, чтобы SSO был проинициирован поставщиком идентификации. Оставьте это поле пустым, чтобы отключить инициирование SSO с помощью поставщика идентификации. URL для ссылки вашего браузера может быть в следующем виде: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}
+idp-sso-relay-state=Передача состояния SSO инициирующим поставщиком идентификации
+idp-sso-relay-state.tooltip=Передать состояние, которое вы хотите послать вместе с SAML запросом, которым хотите проиницировать SSO поставщиком идентификации.
+web-origins=Web источники
+web-origins.tooltip=Разрешает CORS источникам. Чтобы разрешить всем источники с допустимыми URI-адресами переадресации, добавьте '+'. Чтобы разрешить все источники, добавьте '*'.
+fine-oidc-endpoint-conf=Тонкая настройка конфигурации OpenID Connect
+fine-oidc-endpoint-conf.tooltip=Раскройте эту секцию, чтобы сконфигурировать настройки клиента, связанные с протоколом OpenID Connect
+user-info-signed-response-alg=Алгоритм подписи ответа информации о пользователе
+user-info-signed-response-alg.tooltip=JWA алгоритм используется для подписи ответа ресурса информации о пользователе. Если установлено в 'unsigned', то ответ инофрмации о пользователе не будет подписан и будет возвращен в формате application/json.
+request-object-signature-alg=Алгоритм сигнатуры объекта запроса
+request-object-signature-alg.tooltip=JWA алгоритм, который необходим клиенту для использования во время отсылки OIDC запроса объекта, специфицированного по 'request' или 'request_uri' параметрам. Если установлено в 'any', то объект запроса будет подписан любым алгоритмом (включая 'none' ).
+fine-saml-endpoint-conf=Тонкая настройка конфигурации конечных точек доступа SAML
+fine-saml-endpoint-conf.tooltip=Разверните эту секции, чтобы сконфигурировать точные URL-адреса для утвержденного потребителя и сервиса единого выхода.
+assertion-consumer-post-binding-url=Привязка URL POST-запроса для сервиса подтверждения потребителей
+assertion-consumer-post-binding-url.tooltip=URL-адрес SAML POST запроса для клиентских сервисов подтверждения потребителей (запросы входа). Вы можете оставить это поле пустым, если не имеете URL для осуществления такой приввязки.
+assertion-consumer-redirect-binding-url=Привязка URL-адреса переадресации для сервиса подтверждения потребителей
+assertion-consumer-redirect-binding-url.tooltip=SAML переадресация на привязанный URL для клиентского сервиса подтверждения потребителей (запросы входа). Вы можете оставить это поле пустым, если вы не имеете URL для осуществления такой привязки.
+logout-service-post-binding-url=URL для выхода из сервиса в привязанном POST-методе
+logout-service-post-binding-url.tooltip=SAML POST связанный URL для клиентского сервиса единого выхода. Если Вы используете другие привязки, то можете оставить это поле пустым.
+logout-service-redir-binding-url=URL переадресации для выхода из сервиса
+logout-service-redir-binding-url.tooltip=SAML переадресует на привязанный URL для единой точки выхода из сервиса для клиентов. Если Вы используете другие привязки, то можете остаавить это поле пустым.
+saml-signature-keyName-transformer=Наименование ключа сигнатуры SAML
+saml-signature-keyName-transformer.tooltip=Подписанные SAML документы содержат идентификаторы ключей подписи в элементе KeyName. Для Keycloak / RH-SSO контрагентов, используйте KEY_ID, для MS AD FS используйте CERT_SUBJECT, для остальных установите и используйте NONE если другие опции не работают.
# client import
-import-client=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043A\u043B\u0438\u0435\u043D\u0442
-format-option=\u0424\u043E\u0440\u043C\u0430\u0442
-select-format=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0444\u043E\u0440\u043C\u0430\u0442
-import-file=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0444\u0430\u0439\u043B
+import-client=Импортировать клиента
+format-option=Формат
+select-format=Выберите формат
+import-file=Импортировать файл
# client tabs
-settings=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438
-credentials=\u0423\u0447\u0435\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435
-saml-keys=\u041A\u043B\u044E\u0447\u0438 SAML
-roles=\u0420\u043E\u043B\u0438
-mappers=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F
-mappers.tooltip=\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0439, \u043E\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043B\u044F\u044E\u0449\u0438\u0445 \u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u0432 \u0442\u043E\u043A\u0435\u043D\u044B \u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u044B. \u041C\u043E\u0433\u0443\u0442 \u0434\u0435\u043B\u0430\u0442\u044C \u0442\u0430\u043A\u0438\u0435 \u0432\u0435\u0449\u0438 \u043A\u0430\u043A \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 \u0432 \u0437\u0430\u044F\u0432\u043A\u0438 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430, \u0438\u043B\u0438 \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u043E\u0432\u0430\u0442\u044C \u043B\u044E\u0431\u043E\u0439 \u0437\u0430\u043F\u0440\u043E\u0441, \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u044F\u0449\u0438\u0439 \u043C\u0435\u0436\u0434\u0443 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u043C \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u043C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-scope=\u041E\u0431\u043B\u0430\u0441\u0442\u044C
-scope.tooltip=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043E\u0431\u043B\u0430\u0441\u0442\u0438 \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0432\u0430\u043C \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0438\u0442\u044C \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F, \u0432\u043A\u043B\u044E\u0447\u0430\u0435\u043C\u044B\u0435 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0442\u043E\u043A\u0435\u043D\u043E\u043C \u0434\u043E\u0441\u0442\u0443\u043F\u0430, \u0437\u0430\u043F\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u043C.
-sessions.tooltip=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u0441\u0435\u0441\u0441\u0438\u0439 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u041F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0412\u0430\u043C \u0443\u0432\u0438\u0434\u0435\u0442\u044C, \u043A\u0430\u043A\u0438\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0430\u043A\u0442\u0438\u0432\u043D\u044B \u0438 \u043A\u043E\u0433\u0434\u0430 \u043E\u043D\u0438 \u0432\u043E\u0448\u043B\u0438.
-offline-access=\u041E\u0444\u0444\u043B\u0430\u0439\u043D \u0434\u043E\u0441\u0442\u0443\u043F
-offline-access.tooltip=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0441\u0435\u0441\u0441\u0438\u0439 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u041F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0412\u0430\u043C \u0443\u0432\u0438\u0434\u0435\u0442\u044C, \u043A\u0430\u043A\u0438\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0438 \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D \u0438 \u043A\u043E\u0433\u0434\u0430 \u043E\u043D\u0438 \u0435\u0433\u043E \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0438. \u0427\u0442\u043E\u0431\u044B \u043E\u0442\u043E\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435 \u0442\u043E\u043A\u0435\u043D\u044B \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043F\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043D\u0430 \u0432\u043A\u043B\u0430\u0434\u043A\u0443 \u043E\u0442\u0437\u044B\u0432\u0430 \u0438 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u0440\u0430\u043D\u0435\u0435 \u0447\u0435\u043C \u0441\u0435\u0439\u0447\u0430\u0441.
-clustering=\u041A\u043B\u0430\u0441\u0442\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044F
-installation=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430
-installation.tooltip=\u0412\u0441\u043F\u043E\u043C\u043E\u0433\u0430\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0443\u0442\u0438\u043B\u0438\u0442\u0430 \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u043B\u0438\u0447\u043D\u044B\u0445 \u0444\u043E\u0440\u043C\u0430\u0442\u043E\u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0430\u0434\u0430\u043F\u0442\u0435\u0440\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u0438\u043B\u0438 \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0412\u0430\u0448\u0438\u0445 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432.
-service-account-roles=\u0420\u043E\u043B\u0438 Service Account
-service-account-roles.tooltip=\u0420\u0430\u0437\u0440\u0435\u0448\u0430\u044E\u0442 \u0412\u0430\u043C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438 \u0434\u043B\u044F \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439, \u0432\u044B\u0434\u0435\u043B\u0435\u043D\u043D\u043E\u0433\u043E \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
+settings=Настройки
+credentials=Учетные данные
+saml-keys=Ключи SAML
+roles=Роли
+mappers=Сопоставления
+mappers.tooltip=Протокол сопоставлений, осуществляющих преобразование в токены и документы. Могут делать такие вещи как сопоставление пользовательских данных в заявки протокола, или просто преобразовать любой запрос, происходящий между клиентом и сервером аутентификации.
+scope=Область
+scope.tooltip=Сопоставление области позволяет вам ограничить сопоставленные роли пользователя, включаемые вместе с токеном доступа, запрошенного клиентом.
+sessions.tooltip=Просмотр сессий для этого клиента. Позволяет Вам увидеть, какие пользователи активны и когда они вошли.
+offline-access=Оффлайн доступ
+offline-access.tooltip=Просмотр оффлайн сессий для этого клиента. Позволяет Вам увидеть, какие пользователи получали оффлайн токен и когда они его получили. Чтобы выбрать все токены для этого клиента, перейдите на вкладку отзыва и установите значение в текущее время.
+clustering=Кластеризация
+installation=Установка
+installation.tooltip=Вспомогательная утилита для генерации различных форматов конфигурации адаптера клиента, которые Вы можете скачать или скопировать для конфигурации Ваших клиентов.
+service-account-roles=Роли Service Account
+service-account-roles.tooltip=Разрешают Вам аутентифицировать сопоставленные роли для сервиса учетных записей, выделенного для этого клиента.
# client credentials
-client-authenticator=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043B\u0438\u043D\u043D\u043E\u0441\u0442\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-authenticator.tooltip=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043B\u0438\u043D\u043D\u043E\u0441\u0442\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0432\u043E\u043F\u0440\u0435\u043A\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 Keycloak
-certificate.tooltip=\u041A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u0439 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442 \u0434\u043B\u044F \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438 JWT \u0432\u044B\u043F\u0443\u0449\u0435\u043D\u043D\u044B\u0439 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u043C \u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0439 \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u043C \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u044B\u043C \u043A\u043B\u044E\u0447\u043E\u043C \u0438\u0437 \u0412\u0430\u0448\u0435\u0433\u043E \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430 \u043A\u043B\u044E\u0447\u0435\u0439.
-no-client-certificate-configured=\u041A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u0439 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442 \u043D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D
-gen-new-keys-and-cert=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u043E\u0432\u044B\u0435 \u043A\u043B\u044E\u0447\u0438 \u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442
-import-certificate=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442
-gen-client-private-key=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u044B\u0439 \u043A\u043B\u044E\u0447 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-generate-private-key=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u044B\u0439 \u043A\u043B\u044E\u0447
-archive-format=\u0424\u043E\u0440\u043C\u0430\u0442 \u0430\u0440\u0445\u0438\u0432\u0430\u0446\u0438\u0438
-archive-format.tooltip=Java keystore \u0438\u043B\u0438 PKCS12 \u0444\u043E\u0440\u043C\u0430\u0442 \u0430\u0440\u0445\u0438\u0432\u0430\u0446\u0438\u0438.
-key-alias=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u043A\u043B\u044E\u0447\u0430
-key-alias.tooltip=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u0430\u0440\u0445\u0438\u0432\u0430 \u0434\u043B\u044F \u0412\u0430\u0448\u0435\u0433\u043E \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 \u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0430.
-key-password=\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F \u043A\u043B\u044E\u0447\u0430
-key-password.tooltip=\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u043F\u0440\u0438\u0432\u0430\u0442\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0443 \u0432 \u0430\u0440\u0445\u0438\u0432\u0435
-store-password=\u041F\u0430\u0440\u043E\u043B\u044C \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430
-store-password.tooltip=\u041F\u0430\u0440\u043E\u043B\u044C \u0434\u043B\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u0432 \u0441\u0430\u043C \u0430\u0440\u0445\u0438\u0432
-generate-and-download=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0438 \u0441\u043A\u0430\u0447\u0430\u0442\u044C
-client-certificate-import=\u0418\u043C\u043F\u043E\u0440\u0442 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-import-client-certificate=\u0418\u043C\u043F\u043E\u0440\u0442 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-jwt-import.key-alias.tooltip=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u0430\u0440\u0445\u0438\u0432\u0430 \u0434\u043B\u044F \u0432\u0430\u0448\u0435\u0433\u043E \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0430.
-secret=\u0421\u0435\u043A\u0440\u0435\u0442
-regenerate-secret=\u041F\u0435\u0440\u0435\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0435\u043A\u0440\u0435\u0442
-registrationAccessToken=\u0422\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438
-registrationAccessToken.regenerate=\u041F\u0435\u0440\u0435\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438
-registrationAccessToken.tooltip=\u0422\u043E\u043A\u0435\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043E\u0431\u0435\u0441\u043F\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F \u0434\u043B\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432 \u043A \u0441\u0435\u0440\u0432\u0438\u0441\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432.
-add-role=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0440\u043E\u043B\u044C
-role-name=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0440\u043E\u043B\u0438
-composite=\u0421\u043E\u0441\u0442\u0430\u0432\u043D\u0430\u044F
-description=\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435
-no-client-roles-available=\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u0440\u043E\u043B\u0435\u0439 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-scope-param-required=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u043E\u0431\u043B\u0430\u0441\u0442\u0438
-scope-param-required.tooltip=\u042D\u0442\u0430 \u0440\u043E\u043B\u044C \u0431\u0443\u0434\u0435\u0442 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0430 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u043E\u0431\u043B\u0430\u0441\u0442\u0438 \u0441 \u043D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435\u043C \u0440\u043E\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0432 \u0437\u0430\u043F\u0440\u043E\u0441\u0430\u0445 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438/\u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044F \u0442\u043E\u043A\u0435\u043D\u0430.
-composite-roles=\u0421\u043E\u0441\u0442\u0430\u0432\u043D\u044B\u0435 \u0440\u043E\u043B\u0438
-composite-roles.tooltip=\u041A\u043E\u0433\u0434\u0430 \u044D\u0442\u0430 \u0440\u043E\u043B\u044C (\u043D\u0435)\u0430\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u0430 \u0441 \u043B\u044E\u0431\u043E\u0439 \u0440\u043E\u043B\u044C\u044E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439, \u043E\u043D\u0430 (\u043D\u0435)\u0431\u0443\u0434\u0435\u0442 \u043D\u0435\u044F\u0432\u043D\u043E \u0430\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u0430.
-realm-roles=\u0420\u043E\u043B\u0438 Realm
-available-roles=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0440\u043E\u043B\u0438
-add-selected=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0435
-associated-roles=\u0410\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438
-composite.associated-realm-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u0430\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0441 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u044B\u043C\u0438 \u0440\u043E\u043B\u044F\u043C\u0438.
-composite.available-realm-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u0430\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0441 \u044D\u0442\u043E\u0439 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-remove-selected=\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0435
-client-roles=\u0420\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-select-client-to-view-roles=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0434\u043B\u044F \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0430 \u0435\u0433\u043E \u0440\u043E\u043B\u0435\u0439
-available-roles.tooltip=\u0420\u043E\u043B\u0438 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0430\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-client.associated-roles.tooltip=\u041A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u0435 \u0440\u043E\u043B\u0438, \u0430\u0441\u0441\u043E\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0441 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-add-builtin=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0435
-category=\u041A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F
-type=\u0422\u0438\u043F
-no-mappers-available=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u043D\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B
-add-builtin-protocol-mappers=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0435 \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430
-add-builtin-protocol-mapper=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u043E\u0435 \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430
-scope-mappings=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043E\u0431\u043B\u0430\u0441\u0442\u0435\u0439
-full-scope-allowed=\u041F\u043E\u043B\u043D\u044B\u0439 \u0434\u043E\u0441\u0442\u0443\u043F \u043A \u043E\u0431\u043B\u0430\u0441\u0442\u044F\u043C
-full-scope-allowed.tooltip=\u041E\u0442\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0432\u0441\u0435 \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0438\u044F.
-scope.available-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043F\u0440\u0438\u0441\u0432\u043E\u0435\u043D\u044B \u043E\u0431\u043B\u0430\u0441\u0442\u0438.
-assigned-roles=\u041F\u0440\u0438\u0441\u0432\u043E\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438
-assigned-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u043F\u0440\u0438\u0441\u0432\u043E\u0435\u043D\u043D\u044B\u0435 \u043E\u0431\u043B\u0430\u0441\u0442\u0438.
-effective-roles=\u042D\u0444\u0444\u0435\u043A\u0442\u0438\u0432\u043D\u044B\u0435 \u0440\u043E\u043B\u0438
-realm.effective-roles.tooltip=\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u043D\u044B \u0438\u0437 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u0438.
-select-client-roles.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0434\u043B\u044F \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0430 \u0435\u0433\u043E \u0440\u043E\u043B\u0435\u0439
-assign.available-roles.tooltip=\u0420\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432, \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0434\u043B\u044F \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F.
-client.assigned-roles.tooltip=\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-client.effective-roles.tooltip=\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u044C \u0438\u0437 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u0438.
-basic-configuration=\u041E\u0441\u043D\u043E\u0432\u043D\u0430\u044F \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F
-node-reregistration-timeout=\u0422\u0430\u0439\u043C\u0430\u0443\u0442 \u0443\u0437\u043B\u0430 \u043F\u0435\u0440\u0435\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438
-node-reregistration-timeout.tooltip=\u0418\u043D\u0442\u0435\u0440\u0432\u0430\u043B, \u043E\u0437\u043D\u0430\u0447\u0430\u044E\u0449\u0438\u0439 \u043C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u0434\u043B\u044F \u0443\u0437\u043B\u043E\u0432 \u043A\u043B\u0430\u0441\u0442\u0435\u0440\u0430 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432 \u0434\u043B\u044F \u0438\u0445 \u043F\u0435\u0440\u0435\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438. \u0415\u0441\u043B\u0438 \u0443\u0437\u0435\u043B \u043A\u043B\u0430\u0441\u0442\u0435\u0440\u0430 \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0441\u043B\u0430\u0442\u044C \u0437\u0430\u043F\u0440\u043E\u0441 \u043F\u0435\u0440\u0435\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0432 Keycloak \u0437\u0430 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F, \u0442\u043E \u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D \u0438\u0437 Keycloak
-registered-cluster-nodes=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0443\u0437\u043B\u044B \u043A\u043B\u0430\u0441\u0442\u0435\u0440\u0430
-register-node-manually=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0443\u0437\u0435\u043B \u0432\u0440\u0443\u0447\u043D\u0443\u044E
-test-cluster-availability=\u041F\u0440\u043E\u0442\u0435\u0441\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C \u043A\u043B\u0430\u0441\u0442\u0435\u0440\u0430
-last-registration=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F
-node-host=\u0425\u043E\u0441\u0442 \u0443\u0437\u043B\u0430
-no-registered-cluster-nodes=\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0443\u0437\u043B\u043E\u0432 \u043A\u043B\u0430\u0441\u0442\u0435\u0440\u0430
-cluster-nodes=\u0423\u0437\u043B\u044B \u043A\u043B\u0430\u0441\u0442\u0435\u0440\u0430
-add-node=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0443\u0437\u0435\u043B
-active-sessions.tooltip=\u041E\u0431\u0449\u0435\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-show-sessions=\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0441\u0435\u0441\u0441\u0438\u0438
-show-sessions.tooltip=\u041F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435, \u044D\u0442\u043E \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E \u043D\u0430\u043A\u043B\u0430\u0434\u043D\u0430\u044F \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044F \u0432 \u0441\u043B\u0443\u0447\u0430\u0435 \u0431\u043E\u043B\u044C\u0448\u043E\u0433\u043E \u0447\u0438\u0441\u043B\u0430 \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439.
-user=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C
-from-ip=\u0421 IP
-session-start=\u0421\u0435\u0441\u0441\u0438\u044F \u043D\u0430\u0447\u0430\u0442\u0430
-first-page=\u041F\u0435\u0440\u0432\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430
-previous-page=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430
-next-page=\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430
-client-revoke.not-before.tooltip=\u041E\u0442\u043E\u0437\u0432\u0430\u0442\u044C \u043B\u044E\u0431\u044B\u0435 \u0442\u043E\u043A\u0435\u043D\u044B, \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0435 \u0434\u043E \u044D\u0442\u043E\u0439 \u0434\u0430\u0442\u044B \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-client-revoke.push.tooltip=\u0415\u0441\u043B\u0438 URL \u0441\u0438\u0441\u0442\u0435\u043C\u044B \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043F\u043E\u0441\u043B\u0430\u0442\u044C \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438 \u044D\u0442\u043E\u043C\u0443 \u043A\u043B\u0438\u0435\u043D\u0442\u0443.
-select-a-format=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0444\u043E\u0440\u043C\u0430\u0442
-download=\u0421\u043A\u0430\u0447\u0430\u0442\u044C
-offline-tokens=\u041E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D\u044B
-offline-tokens.tooltip=\u041E\u0431\u0449\u0435\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-show-offline-tokens=\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D\u044B
-show-offline-tokens.tooltip=\u041F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435, \u044D\u0442\u043E \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E \u043D\u0430\u043A\u043B\u0430\u0434\u043D\u0430\u044F \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044F \u0432 \u0441\u043B\u0443\u0447\u0430\u0435 \u0431\u043E\u043B\u044C\u0448\u043E\u0433\u043E \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043E\u0444\u0444\u043B\u0430\u0439\u043D \u0442\u043E\u043A\u0435\u043D\u043E\u0432.
-token-issued=\u0422\u043E\u043A\u0435\u043D \u0432\u044B\u0434\u0430\u043D
-last-access=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0434\u043E\u0441\u0442\u0443\u043F
-last-refresh=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435
-key-export=\u042D\u043A\u0441\u043F\u043E\u0440\u0442 \u043A\u043B\u044E\u0447\u0430
-key-import=\u0418\u043C\u043F\u043E\u0440\u0442 \u043A\u043B\u044E\u0447\u0430
-export-saml-key=\u042D\u043A\u0441\u043F\u043E\u0440\u0442 SAML \u043A\u043B\u044E\u0447\u0430
-import-saml-key=\u0418\u043C\u043F\u043E\u0440\u0442 SAML \u043A\u043B\u044E\u0447\u0430
-realm-certificate-alias=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0430 Realm
-realm-certificate-alias.tooltip=\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442 Realm \u0442\u0430\u043A\u0436\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u0432 \u0430\u0440\u0445\u0438\u0432\u0435. \u042D\u0442\u043E \u0441\u0438\u043D\u043E\u043D\u0438\u043C \u0434\u043B\u044F \u043D\u0435\u0433\u043E.
-signing-key=\u041A\u043B\u044E\u0447 \u043F\u043E\u0434\u043F\u0438\u0441\u0438
-saml-signing-key=SAML-\u043A\u043B\u044E\u0447 \u043F\u043E\u0434\u043F\u0438\u0441\u0438.
-private-key=\u041F\u0440\u0438\u0432\u0430\u0442\u043D\u044B\u0439 \u043A\u043B\u044E\u0447
-generate-new-keys=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u043E\u0432\u044B\u0435 \u043A\u043B\u044E\u0447\u0438
-export=\u042D\u043A\u0441\u043F\u043E\u0440\u0442
-encryption-key=\u041A\u043B\u044E\u0447 \u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u0438\u044F
-saml-encryption-key.tooltip=SAML \u043A\u043B\u044E\u0447 \u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u0438\u044F.
-service-accounts=\u0423\u0447\u0435\u0442\u043D\u044B\u0435 \u0437\u0430\u043F\u0438\u0441\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430
-service-account.available-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043F\u0440\u0438\u0441\u0432\u043E\u0435\u043D\u044B \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430.
-service-account.assigned-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0435 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430.
-service-account-is-not-enabled-for=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043D\u0435 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0430 \u0434\u043B\u044F {{client}}
-create-protocol-mappers=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430
-create-protocol-mapper=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430
-protocol=\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B
-protocol.tooltip=\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B...
+client-authenticator=Проверка подлинности клиента
+client-authenticator.tooltip=Проверка подлинности клиента используется для аутентификации этого клиента вместо сервера Keycloak
+certificate.tooltip=Клиентский сертификат для валидации JWT, выпущенный клиентом и подписанный клиентским приватным ключом из Вашего хранилища ключей.
+publicKey.tooltip=Публичный ключ для валидации JWT, выпущенный клиентом и подписанный клиентским приватным ключом.
+no-client-certificate-configured=Клиентский сертификат не сконфигурирован
+gen-new-keys-and-cert=Сгенерировать новые ключи и сертификат
+import-certificate=Импортировать сертификат
+gen-client-private-key=Сгенерировать приватный ключ клиента
+generate-private-key=Сгенерировать приватный ключ
+kid=KID
+kid.tooltip=KID (Key ID) публичного ключа клиента из импортированного JWKS.
+use-jwks-url=Использовать JWKS URL
+use-jwks-url.tooltip=Если включено, то публичные ключи клиента будут скачиваться с заданного адреса JWKS. Это дает большую гибкость, поскольку в случае, если клиент сгенерирует новую пару, то новые ключи всегда будут перезакачиваться. Если выключено, то используется публичный ключ (или сертификат) из базы Keycloak DB, так если пара клиента изменится, вам будет необходимо всегда импортировать новый ключ (или сертификат) в базу Keycloak.
+jwks-url=JWKS URL
+jwks-url.tooltip=URL, где клиентские ключи хранятся в формате JWK. Для дополнительных деталей смотрите спецификацию JWK. Если Вы будете использовать адаптер клиента keycloak с учетными записями "jwt", то Вы можете использовать URL вашего приложения с суффиксом '/k_jwks'. Например 'http://www.myhost.com/myapp/k_jwks' .
+archive-format=Формат архивации
+archive-format.tooltip=Формат архивации Java keystore или PKCS12.
+key-alias=Синоним ключа
+key-alias.tooltip=Синоним архива для Вашего приватного ключа и сертификата.
+key-password=Пароль для ключа
+key-password.tooltip=Пароль для доступа к приватного ключу в архиве
+store-password=Пароль хранилища
+store-password.tooltip=Пароль для доступа в сам архив
+generate-and-download=Сгенерировать и скачать
+client-certificate-import=Импорт сертификата клиента
+import-client-certificate=Импорт сертификата клиента
+jwt-import.key-alias.tooltip=Синоним архива для вашего сертификата.
+secret=Секрет
+regenerate-secret=Перегенерировать секрет
+registrationAccessToken=Токен доступа к регистрации
+registrationAccessToken.regenerate=Перегенерировать токен доступа к регистрации
+registrationAccessToken.tooltip=Токен доступа к регистрации обеспечивает доступ для клиентов к сервису регистрации клиентов.
+add-role=Добавить роль
+role-name=Наименование роли
+composite=Составная
+description=Описание
+no-client-roles-available=Нет доступных ролей клиента
+scope-param-required=Требуется параметр области
+scope-param-required.tooltip=Эта роль будет предоставлена только если параметр области с наименованием роли используется в запросах авторизации/получения токена.
+composite-roles=Составные роли
+composite-roles.tooltip=Когда эта роль (не)ассоциирована с любой ролью пользователей, она (не)будет неявно ассоциирована.
+realm-roles=Роли Realm
+available-roles=Доступные роли
+add-selected=Добавить выбранное
+associated-roles=Ассоциированные роли
+composite.associated-realm-roles.tooltip=Роли уровня Realm, ассоциированные с составными ролями.
+composite.available-realm-roles.tooltip=Роли уровня Realm, ассоциированные с этой составной ролью.
+remove-selected=Удалить выбранное
+client-roles=Роли клиентов
+select-client-to-view-roles=Выберите клиента для просмотра его ролей
+available-roles.tooltip=Роли этого клиента, которые вы можете ассоциировать с составной ролью.
+client.associated-roles.tooltip=Клиентские роли, ассоциированные с составной ролью.
+add-builtin=Добавить встроенные
+category=Категория
+type=Тип
+no-mappers-available=Сопоставления не доступны
+add-builtin-protocol-mappers=Добавить встроенные сопоставления протокола
+add-builtin-protocol-mapper=Добавить встроенное сопоставление протокола
+scope-mappings=Сопоставление областей
+full-scope-allowed=Полный доступ к областям
+full-scope-allowed.tooltip=Отключает все ограничения.
+scope.available-roles.tooltip=Роли уровня Realm, которые могут быть присвоены области.
+assigned-roles=Присвоенные роли
+assigned-roles.tooltip=Роли уровня Realm, присвоенные области.
+effective-roles=Назначенные роли
+realm.effective-roles.tooltip=Назначенные роли уровня realm, которые могут быть унаследованы из составной роли.
+select-client-roles.tooltip=Выберите клиента для просмотра его ролей
+assign.available-roles.tooltip=Роли клиентов, доступные для назначения.
+client.assigned-roles.tooltip=Назначенные роли клиента.
+client.effective-roles.tooltip=Назначенные роли клиента, которые можно унаследовать из составной роли.
+basic-configuration=Основная конфигурация
+node-reregistration-timeout=Таймаут узла перерегистрации
+node-reregistration-timeout.tooltip=Интервал, означающий максимальное время для узлов кластера зарегистрированных клиентов для их перерегистрации. Если узел кластера не может послать запрос перерегистрации в Keycloak за указанное время, то он будет разрегистрирован из Keycloak
+registered-cluster-nodes=Зарегистрированные узлы кластера
+register-node-manually=Зарегистрировать узел вручную
+test-cluster-availability=Протестировать доступность кластера
+last-registration=Последняя регистрация
+node-host=Хост узла
+no-registered-cluster-nodes=Нет доступных зарегистрированных узлов кластера
+cluster-nodes=Узлы кластера
+add-node=Добавить узел
+active-sessions.tooltip=Общее количество активных сессий пользователей для этого клиента.
+show-sessions=Показать сессии
+show-sessions.tooltip=Предупреждение! В случае большого числа активных сессий это будет достаточно накладная операция.
+user=Пользователь
+from-ip=С IP
+session-start=Сессия начата
+first-page=Первая страница
+previous-page=Последняя страница
+next-page=Следующая страница
+client-revoke.not-before.tooltip=Отозвать любые токены, выданные до указанной даты для этого клиента.
+client-revoke.push.tooltip=Если URL системы администрации сконфигурирован для этого клиента, то необходимо послать политики этому клиенту.
+select-a-format=Выберите формат
+download=Скачать
+offline-tokens=Оффлайн токены
+offline-tokens.tooltip=Общее количество оффлайн токенов для этого клиента.
+show-offline-tokens=Показать оффлайн токены
+show-offline-tokens.tooltip=Предупреждение! В случае большого числа активных сессий это будет достаточно накладная операция.
+token-issued=Токен выдан
+last-access=Последний доступ
+last-refresh=Последнее обновление
+key-export=Экспорт ключа
+key-import=Импорт ключа
+export-saml-key=Экспорт SAML ключа
+import-saml-key=Импорт SAML ключа
+realm-certificate-alias=Синоним сертификата Realm
+realm-certificate-alias.tooltip=Это синоним сертификата. Сам сертификат Realm также сохраняется в архиве.
+signing-key=Ключ подписи
+saml-signing-key=SAML-ключ подписи.
+private-key=Приватный ключ
+generate-new-keys=Сгенерировать новые ключи
+export=Экспорт
+encryption-key=Ключ шифрования
+saml-encryption-key.tooltip=SAML ключ шифрования.
+service-accounts=Учетные записи сервиса
+service-account.available-roles.tooltip=Роли уровня Realm, которые могут быть присвоены учетной записи сервиса.
+service-account.assigned-roles.tooltip=Роли уровня Realm, назначенные учетной записи сервиса.
+service-account-is-not-enabled-for=Учетная запись сервиса не включена для {{client}}
+create-protocol-mappers=Создать сопоставление протокола
+create-protocol-mapper=Создать сопоставление протокола
+protocol=Протокол
+protocol.tooltip=Протокол...
id=ID
-mapper.name.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F.
-mapper.consent-required.tooltip=\u041D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u043B\u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043D\u0430 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u044D\u0442\u043E\u0439 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0443?
-consent-text=\u0422\u0435\u043A\u0441\u0442 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u044F
-consent-text.tooltip=\u0422\u0435\u043A\u0441\u0442, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u043A\u0430\u0437\u0430\u043D \u043D\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u044F.
-mapper-type=\u0422\u0438\u043F \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F
-mapper-type.tooltip=\u0422\u0438\u043F \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F
+mapper.name.tooltip=Наименование сопоставления.
+mapper.consent-required.tooltip=Необходимо ли согласие пользователя на предоставление этой информации клиенту?
+consent-text=Текст согласования
+consent-text.tooltip=Текст, который будет показан пользователю на странице согласования.
+mapper-type=Тип сопоставления
+mapper-type.tooltip=Тип сопоставления
# realm identity providers
-identity-providers=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0438 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-table-of-identity-providers=\u0422\u0430\u0431\u043B\u0438\u0446\u0430 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-add-provider.placeholder=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430...
-provider=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A
-gui-order=\u041E\u0447\u0435\u0440\u0435\u0434\u043D\u043E\u0441\u0442\u044C \u0432 GUI
-first-broker-login-flow=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430
-post-broker-login-flow=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u043F\u043E\u0441\u043B\u0435 \u0432\u0445\u043E\u0434\u0430
-redirect-uri=URI \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F
-redirect-uri.tooltip=\u042D\u0442\u043E\u0442 uri \u043F\u0435\u0440\u0435\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0432 \u0441\u043B\u0443\u0447\u0430\u0435, \u0435\u0441\u043B\u0438 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-alias=\u0421\u0438\u043D\u043E\u043D\u0438\u043C
-identity-provider.alias.tooltip=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u043E \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438, \u0430 \u0442\u0430\u043A\u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F \u043F\u043E\u0441\u0442\u0440\u043E\u0435\u043D\u0438\u044F \u0430\u0434\u0440\u0435\u0441\u0430 \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0438.
-identity-provider.enabled.tooltip=\u0412\u043A\u043B\u044E\u0447\u0430\u0435\u0442/\u0432\u044B\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u044D\u0442\u043E\u0433\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-authenticate-by-default=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-identity-provider.authenticate-by-default.tooltip=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044F, \u0435\u0441\u043B\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0430\u043A\u0430\u0446\u0438\u0438 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0434\u0430\u0436\u0435 \u043F\u0435\u0440\u0435\u0434 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435\u043C \u044D\u043A\u0440\u0430\u043D\u0430 \u0432\u0445\u043E\u0434\u0430.
-store-tokens=\u0425\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u043E\u0432
-identity-provider.store-tokens.tooltip=\u0412\u043A\u043B\u044E\u0447\u0435\u043D\u043E/\u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u043F\u043E\u0441\u043B\u0435 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-stored-tokens-readable=\u0421\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u044B\u0435 \u0442\u043E\u043A\u0435\u043D\u044B \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u043D\u0430 \u0447\u0442\u0435\u043D\u0438\u0435
-identity-provider.stored-tokens-readable.tooltip=\u0412\u043A\u043B\u044E\u0447\u0435\u043D\u043E/\u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0447\u0442\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u044B\u043C\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\u043C\u0438 \u043B\u044E\u0431\u044B\u0445 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u044B\u0445 \u0442\u043E\u043A\u0435\u043D\u043E\u0432. \u042D\u0442\u043E \u043D\u0430\u0437\u043D\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u0440\u043E\u043B\u044C\u044E broker.read-token.
-update-profile-on-first-login=\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u043F\u0440\u0438 \u043F\u0435\u0440\u0432\u043E\u043C \u0432\u0445\u043E\u0434\u0435
-on=\u0412\u043A\u043B
-on-missing-info=\u041F\u0440\u0438 \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u043D\u043E\u0439 \u0438\u043D\u0444\u043E
-off=\u0412\u044B\u043A
-update-profile-on-first-login.tooltip=\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u044F, \u043F\u0440\u0438 \u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0434\u043E\u043B\u0436\u043D\u044B \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0441\u0432\u043E\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0432\u043E \u0432\u0440\u0435\u043C\u044F \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430.
-trust-email=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 Email
-trust-email.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0442\u043E email, \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0439 \u044D\u0442\u0438\u043C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043D\u044B\u043C \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E \u0434\u043B\u044F realm.
-gui-order.tooltip=\u0427\u0438\u0441\u043B\u043E, \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u044E\u0449\u0435\u0435 \u043F\u043E\u0440\u044F\u0434\u043E\u043A \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0432 GUI (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, \u043D\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435 \u0432\u0445\u043E\u0434\u0430).
-first-broker-login-flow.tooltip=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0441\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0435\u0442 \u043F\u043E\u0441\u043B\u0435 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430 \u0441 \u044D\u0442\u043E\u0433\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438. \u0422\u0435\u0440\u043C\u0438\u043D 'First Login' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u0435\u0449\u0435 \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 Keycloak \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u043E\u0439 \u0441 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-post-broker-login-flow.tooltip=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0441\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0435\u0442 \u043F\u043E\u0441\u043B\u0435 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0430 \u0438\u0437 \u044D\u0442\u043E\u0433\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438. \u041F\u043E\u043B\u0435\u0437\u043D\u043E, \u0435\u0441\u043B\u0438 \u0432\u044B \u0445\u043E\u0434\u0438\u0442\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0443\u044E \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F, \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u0432 \u044D\u0442\u043E\u043C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 OTP). \u041E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0435\u0441\u043B\u0438 \u043D\u0435 \u0445\u043E\u0442\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u0441\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u043B\u0438 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0430\u0443\u0442\u0435\u043D\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E\u0441\u043B\u0435 \u0432\u0445\u043E\u0434\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0441 \u044D\u0442\u0438\u043C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438. \u0422\u0430\u043A\u0436\u0435 \u043E\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043D\u0438\u043C\u0430\u043D\u0438\u0435, \u0447\u0442\u043E \u0440\u0435\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u0434\u043E\u043B\u0436\u043D\u044B \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u0430\u0433\u0430\u0442\u044C \u0447\u0442\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B ClientSession \u0442\u0430\u043A\u0436\u0435 \u043A\u0430\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u0435\u0435 \u0432 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-openid-connect-config=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F OpenID Connect
-openid-connect-config.tooltip=OIDC SP \u0438 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F \u0432\u043D\u0435\u0448\u043D\u0438\u0445 IDP.
-authorization-url=URL \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438
-authorization-url.tooltip=Url \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438.
-token-url=URL \u0442\u043E\u043A\u0435\u043D\u0430
-token-url.tooltip=URL \u0442\u043E\u043A\u0435\u043D\u0430.
-logout-url=URL \u0432\u044B\u0445\u043E\u0434\u0430
-identity-provider.logout-url.tooltip=\u041A\u043E\u043D\u0435\u0447\u043D\u0430\u044F \u0442\u043E\u0447\u043A\u0430 \u043E\u043A\u043E\u043D\u0447\u0430\u043D\u0438\u044F \u0441\u0435\u0441\u0441\u0438\u0438, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C\u0430\u044F \u0434\u043B\u044F \u0432\u044B\u0445\u043E\u0434\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0438\u0437 \u0432\u043D\u0435\u0448\u043D\u0435\u0433\u043E IDP.
+identity-providers=Поставщики идентификации
+table-of-identity-providers=Таблица поставщиков идентификации
+add-provider.placeholder=Добавить поставщика...
+provider=Поставщик
+gui-order=Очередность в GUI
+first-broker-login-flow=Сценарий первого входа
+post-broker-login-flow=Сценарий после входа
+redirect-uri=URI перенаправления
+redirect-uri.tooltip=Этот uri перенаправления используется в том случае, если сконфигурирован поставщик идентификации.
+alias=Синоним
+display-name=Отображаемое название
+identity-provider.alias.tooltip=Синоним уникально идентифицирует поставщика идентификации, а также используется для построения адреса переадресации.
+identity-provider.display-name.tooltip=Дружелюбное имя для провайдеров идентификации.
+identity-provider.enabled.tooltip=Включает/выключает этого поставщика идентификации.
+authenticate-by-default=Аутентификация по умолчанию
+identity-provider.authenticate-by-default.tooltip=Отображается, если поставщик должен быть использован для аутентифиакации по умолчанию даже перед отображением экрана входа.
+store-tokens=Хранение токенов
+identity-provider.store-tokens.tooltip=Включено/выключено хранение токенов после аутентификации пользователя.
+stored-tokens-readable=Сохраненные токены доступны на чтение
+identity-provider.stored-tokens-readable.tooltip=Включено/выключено чтение новыми пользователями любых сохраненных токенов. Это назначается ролью broker.read-token.
+disableUserInfo=Отключить информацию о пользователе
+identity-provider.disableUserInfo.tooltip=Отключить использование сервиса информации о пользователе, чтобы получить дополнительную информацию о пользователе? По умолчанию используется сервис OIDC.
+userIp=Использовать параметр userIp
+identity-provider.google-userIp.tooltip=Установить 'userIp' параметр запроса при вызове сервиса Google's. Будет использовать ip адрес пользователя. Полезно, если Google будет подавлять доступ к сервису информации о пользователе.
+update-profile-on-first-login=Обновить профиль при первом входе
+on=Вкл
+on-missing-info=При пропущенной инфо
+off=Вык
+update-profile-on-first-login.tooltip=Определить условия, при которых пользователи должны обновлять свой профиль во время первого входа.
+trust-email=Подтверждение E-mail
+trust-email.tooltip=Если включено, то E-mail, предоставленный этим поставщиком не будет подтвержденным даже если подтверждение включено для realm.
+link-only=Только связывание учетной записи
+link-only.tooltip=Если установлено, то пользователи не смогут войти через этого провайдера. Только устанавливает связь к этому провайдеру. Используется, если вы не хотите разрешать вход через этого провайдера, но хотите с этим провайдером иметь интеграцию.
+hide-on-login-page=Скрыть на странице входа
+hide-on-login-page.tooltip=Если скрыто, то вход с этим провайдером возможен только при явном вызове, например при использовании параметра 'kc_idp_hint'.
+gui-order.tooltip=Число, определяющее порядок поставщиков в GUI (например, на странице входа).
+first-broker-login-flow.tooltip=Синоним сценария аутентификации, который срабатывает после первого входа с этого поставщика идентификации. Термин 'First Login' означает, что еще не существует учетной записи Keycloak связанной с аутентифицированной учетной записью поставщика идентификации.
+post-broker-login-flow.tooltip=Синоним сценария аутентификации, который срабатывает после каждого входа из этого поставщика идентификации. Полезно, если вы ходите получить дополнительную проверку каждого пользователя, аутентифицированного в этом поставщике идентификации (например OTP). Оставьте это поле пустым, если не хотите, чтобы срабатывали дополнительные проверки аутенификации после входа пользователя с этим поставщиком идентификации. Также обратите внимание, что реализации аутентификатора должны предполагать что пользователь уже установил ClientSession также как установил ее в поставщике идентификации.
+openid-connect-config=Конфигурация OpenID Connect
+openid-connect-config.tooltip=OIDC SP и конфигурация внешних IDP.
+authorization-url=URL авторизации
+authorization-url.tooltip=Url авторизации.
+token-url=URL токена
+token-url.tooltip=URL токена.
+logout-url=URL выхода
+identity-provider.logout-url.tooltip=Конечная точка окончания сессии, используемая для выхода пользователя из внешнего IDP.
backchannel-logout=Backchannel Logout
-backchannel-logout.tooltip=\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043B\u0438 \u0432\u043D\u0435\u0448\u043D\u0438\u0439 IDP backchannel logout?
-user-info-url=URL \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438 \u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435
-user-info-url.tooltip=Url \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438 \u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435. \u042D\u0442\u043E \u043F\u043E\u043B\u0435 \u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E.
-identity-provider.client-id.tooltip=\u041A\u043B\u0438\u0435\u043D\u0442 \u0438\u043B\u0438 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-client-secret=\u0421\u0435\u043A\u0440\u0435\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-show-secret=\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0441\u0435\u043A\u0440\u0435\u0442
-hide-secret=\u0421\u043A\u0440\u044B\u0442\u044C \u0441\u0435\u043A\u0440\u0435\u0442
-client-secret.tooltip=\u041A\u043B\u0438\u0435\u043D\u0442 \u0438\u043B\u0438 \u0441\u0435\u043A\u0440\u0435\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-issuer=\u042D\u043C\u0438\u0442\u0435\u043D\u0442
-issuer.tooltip=\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u044D\u043C\u0438\u0442\u0435\u043D\u0442\u0430 \u0434\u043B\u044F \u044D\u043C\u0438\u0442\u0435\u043D\u0442\u0430 \u043E\u0442\u0432\u0435\u0442\u0430. \u0415\u0441\u043B\u0438 \u043D\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F, \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0442\u044C\u0441\u044F.
-default-scopes=\u041E\u0431\u043B\u0430\u0441\u0442\u0438 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-identity-provider.default-scopes.tooltip=\u041E\u0431\u043B\u0430\u0441\u0442\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u0441\u043B\u0430\u043D\u044B \u043F\u043E\u0441\u043B\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0430 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u042D\u0442\u043E \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u043E\u0431\u043B\u0430\u0441\u0442\u0435\u0439, \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0445 \u043F\u0440\u043E\u0431\u0435\u043B\u043E\u043C. \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 'openid'.
-prompt=\u041F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0430
-unspecified.option=\u043D\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0439
-none.option=\u043D\u0435\u0442
-consent.option=\u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435
-login.option=\u0432\u0445\u043E\u0434
-select-account.option=\u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435_\u0443\u0447\u0435\u0442\u043D\u0443\u044E_\u0437\u0430\u043F\u0438\u0441\u044C
-prompt.tooltip=\u0423\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u0437\u0430\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u043B\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0443 \u043A\u043E\u043D\u0435\u0447\u043D\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043F\u0435\u0440\u0435\u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E \u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435.
-validate-signatures=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439
-identity-provider.validate-signatures.tooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439 \u0432\u043D\u0435\u0448\u043D\u0438\u0445 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-validating-public-key=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430
-identity-provider.validating-public-key.tooltip=\u041F\u0443\u0431\u043B\u0438\u0447\u043D\u044B\u0439 \u043A\u043B\u044E\u0447 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 PEM, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u043B\u0436\u0435\u043D \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u0435\u0439 \u0432\u043D\u0435\u0448\u043D\u0438\u0445 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-import-external-idp-config=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E \u0432\u043D\u0435\u0448\u043D\u0435\u0433\u043E IDP
-import-external-idp-config.tooltip=\u041F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0432\u0430\u043C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043C\u0435\u0442\u0430\u0434\u0430\u043D\u043D\u044B\u0435 \u0432\u043D\u0435\u0448\u043D\u0435\u0433\u043E IDP \u0438\u0437 \u0444\u0430\u0439\u043B\u0430 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438\u043B\u0438 \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u0435\u0433\u043E \u0438\u0437 URL.
-import-from-url=\u0418\u043C\u043F\u043E\u0440\u0442 \u0438\u0437 URL
-identity-provider.import-from-url.tooltip=\u0418\u043C\u043F\u043E\u0440\u0442 \u043C\u0435\u0442\u0430\u0434\u0430\u043D\u043D\u044B\u0445 \u0438\u0437 \u0434\u0435\u0441\u043A\u0440\u0438\u043F\u0442\u043E\u0440\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044B\u0432\u0430\u043D\u0438\u044F \u0443\u0434\u0430\u043B\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-import-from-file=\u0418\u043C\u043F\u043E\u0440\u0442 \u0438\u0437 \u0444\u0430\u0439\u043B\u0430
-identity-provider.import-from-file.tooltip=\u0418\u043C\u043F\u043E\u0440\u0442 \u043C\u0435\u0442\u0430\u0434\u0430\u043D\u043D\u044B\u0445 \u0441\u043E \u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0433\u043E \u0434\u0435\u0441\u043A\u0440\u0438\u043F\u0442\u043E\u0440\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044B\u0432\u0430\u043D\u0438\u044F \u0443\u0434\u0430\u043B\u0435\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-saml-config=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F SAML
-identity-provider.saml-config.tooltip=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F SAML SP \u0438 \u0432\u043D\u0435\u0448\u043D\u0438\u0445 IDP.
-single-signon-service-url=\u0410\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0435\u0434\u0438\u043D\u043E\u0439 \u0442\u043E\u0447\u043A\u0438 \u0432\u0445\u043E\u0434\u0430
-saml.single-signon-service-url.tooltip=Url, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0445 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 \u043D\u0430 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E (SAML AuthnRequest).
-single-logout-service-url=\u0410\u0434\u0440\u0435\u0441\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0435\u0434\u0438\u043D\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430
-saml.single-logout-service-url.tooltip=Url, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0445 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 \u043D\u0430 \u0432\u044B\u0445\u043E\u0434.
-nameid-policy-format=\u0424\u043E\u0440\u043C\u0430\u0442 \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438 NameID
-nameid-policy-format.tooltip=\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442 \u0441\u0441\u044B\u043B\u043A\u0443 URI, \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0443\u044E \u0444\u043E\u0440\u043C\u0430\u0442\u0443 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u0438\u043C\u0435\u043D\u0438. \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E urn:oasis:names:tc:SAML:2.0:nameid-format:persistent.
-http-post-binding-response=\u0421\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442 HTTP-POST
-http-post-binding-response.tooltip=\u0423\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u0441\u043B\u0435\u0434\u0443\u0435\u0442 \u043B\u0438 \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u043D\u0430 \u0437\u0430\u043F\u0440\u043E\u0441\u044B, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044F \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 HTTP-POST. \u0415\u0441\u043B\u0438 \u043D\u0435\u0442, \u0442\u043E \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D HTTP-REDIRECT.
-http-post-binding-for-authn-request=\u0421\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u0435 HTTP-POST \u0434\u043B\u044F AuthnRequest
-http-post-binding-for-authn-request.tooltip=\u0423\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u0434\u043E\u043B\u0436\u043D\u044B \u043B\u0438 AuthnRequest \u0431\u044B\u0442\u044C \u043F\u043E\u0441\u043B\u0430\u043D\u044B, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044F \u0441\u0432\u044F\u0437\u043A\u0443 HTTP-POST. \u0415\u0441\u043B\u0438 \u043D\u0435\u0442, \u0442\u043E \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D HTTP-REDIRECT.
-want-authn-requests-signed=\u0416\u0435\u043B\u0430\u043D\u0438\u0435 \u043F\u043E\u0434\u043F\u0438\u0441\u0438 AuthnRequests
-want-authn-requests-signed.tooltip=\u0423\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u043E\u0436\u0438\u0434\u0430\u0435\u0442 \u043B\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0445 AuthnRequest.
-force-authentication=\u041F\u0440\u0438\u043D\u0443\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F
-identity-provider.force-authentication.tooltip=\u0423\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u0434\u043E\u043B\u0436\u0435\u043D \u043B\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0435\u0434\u0443\u0449\u0435\u0433\u043E \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E \u0430 \u043D\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0439 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438.
-validate-signature=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u0438
-saml.validate-signature.tooltip=\u0412\u043A\u043B\u044E\u0447\u0430\u0435\u0442/\u0432\u044B\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u043F\u043E\u0434\u043F\u0438\u0441\u0438 \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u043E\u0442 SAML.
-validating-x509-certificate=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 X509 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0432
-validating-x509-certificate.tooltip=\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 PEM, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u0438.
-saml.import-from-url.tooltip=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043C\u0435\u0442\u0430\u0434\u0430\u043D\u043D\u044B\u0435 \u0438\u0437 \u0443\u0434\u0430\u043B\u0435\u043D\u043D\u043E\u0433\u043E \u0434\u0435\u0441\u043A\u0440\u0438\u043F\u0442\u043E\u0440\u0430 \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0435\u0439 IDP SAML.
-social.client-id.tooltip=\u0418\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-social.client-secret.tooltip=\u0421\u0435\u043A\u0440\u0435\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-social.default-scopes.tooltip=\u041E\u0431\u043B\u0430\u0441\u0442\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u0441\u043B\u0430\u043D\u044B \u043F\u0440\u0438 \u0437\u0430\u043F\u0440\u043E\u0441\u0435 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0421\u043C\u043E\u0442\u0440\u0438\u0442\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u044E \u0434\u043B\u044F \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u044B\u0445 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439, \u0440\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u0435\u043B\u0435\u0439 \u0438 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E.
-key=\u041A\u043B\u044E\u0447
-stackoverflow.key.tooltip=\u041A\u043B\u044E\u0447, \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044B\u0439 \u043F\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0432 Stack Overflow.
+backchannel-logout.tooltip=Поддерживает ли внешний IDP backchannel logout?
+user-info-url=URL информации о пользователе
+user-info-url.tooltip=Url информации о пользователе. Это поле опционально.
+identity-provider.client-id.tooltip=Клиент или идентификатор клиента, зарегистрированного с помощью поставщика идентификации.
+client-secret=Секрет клиента
+show-secret=Показать секрет
+hide-secret=Скрыть секрет
+client-secret.tooltip=Клиент или секрет клиента, зарегистрированный с помощью поставщика идентификации.
+issuer=Эмитент
+issuer.tooltip=Идентификатор эмитента для эмитента ответа. Если не предоставлен, проверка не будет выполняться.
+default-scopes=Области по умолчанию
+identity-provider.default-scopes.tooltip=Области, которые будут посланы после запроса авторизации. Это может быть список областей, разделенных пробелом. По умолчанию 'openid'.
+prompt=Подсказка
+unspecified.option=неопределенный
+none.option=нет
+consent.option=согласие
+login.option=вход
+select-account.option=выберите_учетную_запись
+prompt.tooltip=Указывает, запрашивает ли сервер авторизации у конечного пользователя переаутентификацию и согласие.
+validate-signatures=Проверка подписей
+identity-provider.validate-signatures.tooltip=Включить/выключить проверку подписей внешних поставщиков идентификации.
+identity-provider.use-jwks-url.tooltip=Если включено, то публичные ключи поставщиков идентификации будет скачаны с заданного JWKS URL. Это дает дополнительную гибкость, так как новые ключи скачиваются каждый раз когда поставщик идентификации создает новую пару. Если выключено, то будут использованы публичные ключи (или сертификат) из базы данных Keycloak, и в случае изменений пары на поставщике идентификации вам будет необходимо каждый раз импортировать новые ключи в базу данных Keycloak.
+identity-provider.jwks-url.tooltip=URL по которому поставщик идентификации хранит ключи в формате JWK. Для дополнительной информации смотрите спецификацию JWK. Если Вы используете внешнего поставщика идентификации keycloak, то Вы можете использовать URL наподобие 'http://broker-keycloak:8180/auth/realms/test/protocol/openid-connect/certs' предполагая, что ваш посредник keycloak запущен на 'http://broker-keycloak:8180' и его realm 'test' .
+validating-public-key=Проверка публичного ключа
+identity-provider.validating-public-key.tooltip=Публичный ключ в формате PEM, который должен использоваться для проверки подписей внешних поставщиков идентификации.
+validating-public-key-id=Валидация Id публичного ключа
+identity-provider.validating-public-key-id.tooltip=Явный ID проверяемого публичного ключа, выданного выше, если есть key ID. Оставьте это поле пустым если ключ выше должен быть использован всегда, независимо от key ID указанного внешнего IDP; установите это если ключ должен быть использован для проверки соответсвтия key ID внешних IDP.
+import-external-idp-config=Импортировать конфигурацию внешнего IDP
+import-external-idp-config.tooltip=Позволяет вам загрузить метаданные внешнего IDP из файла конфигурации или скачать его из URL.
+import-from-url=Импорт из URL
+identity-provider.import-from-url.tooltip=Импорт метаданных из дескриптора развертывания удаленного поставщика идентификации.
+import-from-file=Импорт из файла
+identity-provider.import-from-file.tooltip=Импорт метаданных со скачанного дескриптора развертывания удаленного поставщика идентификации.
+saml-config=Конфигурация SAML
+identity-provider.saml-config.tooltip=Конфигурация SAML SP и внешних IDP.
+single-signon-service-url=Адрес сервиса единой точки входа
+saml.single-signon-service-url.tooltip=Url, который должен быть использован для отправленных запросов на аутентификацию (SAML AuthnRequest).
+single-logout-service-url=Адреса сервиса единого выхода
+saml.single-logout-service-url.tooltip=Url, который должен быть использован для отправленных запросов на выход.
+nameid-policy-format=Формат политики NameID
+nameid-policy-format.tooltip=Определяет ссылку URI, соответствующую формату идентификатора имени. По умолчанию urn:oasis:names:tc:SAML:2.0:nameid-format:persistent.
+http-post-binding-response=Привязанный ответ HTTP-POST
+http-post-binding-response.tooltip=Указывает, следует ли отвечать на запросы, используя привязку HTTP-POST. Если нет, то будет использован HTTP-REDIRECT.
+http-post-binding-for-authn-request=Привязывание HTTP-POST для AuthnRequest
+http-post-binding-for-authn-request.tooltip=Указывает, должны ли AuthnRequest быть посланы, используя привязку HTTP-POST. Если нет, то будет использован HTTP-REDIRECT.
+http-post-binding-logout=Привязывание HTTP-POST для выхода
+http-post-binding-logout.tooltip=Указывает, необходоимо ли отвечать на завпросы, используя привязку HTTP-POST. Если не задано, то будет использован HTTP-REDIRECT.
+want-authn-requests-signed=Ожидание подписи AuthnRequests
+want-authn-requests-signed.tooltip=Указывает, ожидает ли поставщик идентификации подписанных AuthnRequest.
+force-authentication=Принудительная аутентификация
+identity-provider.force-authentication.tooltip=Указывает, должен ли поставщик идентификации аутентифицировать ведущего напрямую, а не использовать предыдущий контекст безопасности.
+validate-signature=Проверка подписи
+saml.validate-signature.tooltip=Включает/выключает проверку подписи ответов от SAML.
+validating-x509-certificate=Проверка X509 сертификатов
+validating-x509-certificate.tooltip=Сертификат в формате PEM, который должен быть использован для проверки подписи.
+saml.import-from-url.tooltip=Импортировать метаданные из удаленного дескриптора сущностей IDP SAML.
+social.client-id.tooltip=Идентификатор клиента, зарегистрированный с помощью поставщика идентификации.
+social.client-secret.tooltip=Секрет клиента, зарегистрированный с помощью поставщика идентификации.
+social.default-scopes.tooltip=Области, которые будут посланы при запросе авторизации. Смотрите документацию для возможных значений, разделителей и значений по умолчанию.
+key=Ключ
+stackoverflow.key.tooltip=Ключ, полученный при регистрации клиента в Stack Overflow.
# User federation
-sync-ldap-roles-to-keycloak=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u043E\u043B\u0438 LDAP \u0441 Keycloak
-sync-keycloak-roles-to-ldap=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u043E\u043B\u0438 Keycloak \u0441 LDAP
-sync-ldap-groups-to-keycloak=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u044B LDAP \u0441 Keycloak
-sync-keycloak-groups-to-ldap=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u044B Keycloak \u0441 LDAP
+sync-ldap-roles-to-keycloak=Синхронизировать роли LDAP с Keycloak
+sync-keycloak-roles-to-ldap=Синхронизировать роли Keycloak с LDAP
+sync-ldap-groups-to-keycloak=Синхронизировать группы LDAP с Keycloak
+sync-keycloak-groups-to-ldap=Синхронизировать группы Keycloak с LDAP
realms=Realms
realm=Realm
-identity-provider-mappers=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-create-identity-provider-mapper=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439
-add-identity-provider-mapper=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439
-client.description.tooltip=\u0417\u0430\u0434\u0430\u0435\u0442 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 '\u041C\u043E\u0439 \u043A\u043B\u0438\u0435\u043D\u0442 \u0434\u043B\u044F \u0442\u0430\u0431\u0435\u043B\u044F \u0443\u0447\u0435\u0442\u0430 \u0432\u0440\u0435\u043C\u0435\u043D\u0438'. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043A\u043B\u044E\u0447\u0438 \u0434\u043B\u044F \u043B\u043E\u043A\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: ${my_client_description}
-
-expires=\u0418\u0441\u0442\u0435\u043A\u0430\u0435\u0442
-expiration=\u0418\u0441\u0442\u0435\u0447\u0435\u043D\u0438\u0435
-expiration.tooltip=\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442, \u043A\u0430\u043A \u0434\u043E\u043B\u0433\u043E \u0442\u043E\u043A\u0435\u043D \u0431\u0443\u0434\u0435\u0442 \u043E\u0441\u0442\u0430\u0432\u0430\u0442\u044C\u0441\u044F \u0432\u0430\u043B\u0438\u0434\u043D\u044B\u043C
-count=\u0421\u0447\u0435\u0442\u0447\u0438\u043A
-count.tooltip=\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442, \u043A\u0430\u043A \u043C\u043D\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0441\u043E\u0437\u0434\u0430\u043D\u043E \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u044D\u0442\u043E\u0433\u043E \u0442\u043E\u043A\u0435\u043D\u0430
-remainingCount=\u0421\u0447\u0435\u0442\u0447\u0438\u043A \u043E\u0441\u0442\u0430\u0442\u043A\u0430
-created=\u0421\u043E\u0437\u0434\u0430\u043D\u043E
-back=\u041D\u0430\u0437\u0430\u0434
-initial-access-tokens=\u0422\u043E\u043A\u0435\u043D\u044B \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-add-initial-access-tokens=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-initial-access-token=\u0422\u043E\u043A\u0435\u043D \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-initial-access.copyPaste.tooltip=\u0421\u043A\u043E\u043F\u0438\u0440\u0443\u0439\u0442\u0435/\u0432\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u0442\u043E\u043A\u0435\u043D \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u0434\u043E \u0442\u0435\u0445 \u043F\u043E\u0440, \u043A\u0430\u043A \u0443\u0439\u0434\u0435\u0442\u0435 \u0441 \u044D\u0442\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B, \u0442.\u043A. \u0432 \u0434\u0430\u043B\u044C\u043D\u0435\u0439\u0448\u0435\u043C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0435\u0433\u043E \u0431\u0443\u0434\u0435\u0442 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E
-continue=\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C
-initial-access-token.confirm.title=\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-initial-access-token.confirm.text=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u043A\u043E\u043F\u0438\u0440\u0443\u0439\u0442\u0435 \u0438 \u0432\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u0442\u043E\u043A\u0435\u043D \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u0434\u043E \u0442\u043E\u0433\u043E, \u043A\u0430\u043A \u0441\u0434\u0435\u043B\u0430\u0435\u0442\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435, \u0442.\u043A. \u0432 \u0434\u0430\u043B\u044C\u043D\u0435\u0439\u0448\u0435\u043C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0435\u0433\u043E \u0431\u0443\u0434\u0435\u0442 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E
-
-client-templates=\u0428\u0430\u0431\u043B\u043E\u043D\u044B \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-client-templates.tooltip=\u0428\u0430\u0431\u043B\u043E\u043D\u044B \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432 \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u044E\u0442 \u0432\u0430\u043C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C \u043E\u0441\u043D\u043E\u0432\u043D\u0443\u044E \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043E\u0431\u0449\u0435\u0439 \u043C\u0435\u0436\u0434\u0443 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u0438\u043C\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430\u043C\u0438
-
-groups=\u0413\u0440\u0443\u043F\u043F\u044B
-
-group.add-selected.tooltip=\u0420\u043E\u043B\u0438 Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u044B \u043D\u0430 \u044D\u0442\u0443 \u0433\u0440\u0443\u043F\u043F\u0443.
-group.assigned-roles.tooltip=\u0420\u043E\u043B\u0438 Realm, \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0435 \u043D\u0430 \u0433\u0440\u0443\u043F\u043F\u0443
-group.effective-roles.tooltip=\u0412\u0441\u0435 \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F realm \u0440\u043E\u043B\u0435\u0439. \u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0440\u043E\u043B\u0438 \u0437\u0434\u0435\u0441\u044C \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u043D\u044B \u0438\u0437 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u0438.
-group.available-roles.tooltip=\u041D\u0430\u0437\u043D\u0430\u0447\u0430\u0435\u043C\u044B\u0435 \u0440\u043E\u043B\u0438 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-group.assigned-roles-client.tooltip=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0440\u043E\u043B\u0435\u0439 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-group.effective-roles-client.tooltip=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0440\u043E\u043B\u0435\u0439 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0440\u043E\u043B\u0438 \u0437\u0434\u0435\u0441\u044C \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u043D\u044B \u0438\u0437 \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0445 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u044B\u0445 \u0440\u043E\u043B\u0435\u0439.
-
-default-roles=\u0420\u043E\u043B\u0438 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-no-realm-roles-available=\u0420\u043E\u043B\u0438 realm \u043D\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B
-
-users=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438
-user.add-selected.tooltip=\u0420\u043E\u043B\u0438 Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u044B \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E.
-user.assigned-roles.tooltip=\u0420\u043E\u043B\u0438 Realm, \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E
-user.effective-roles.tooltip=\u0412\u0441\u0435 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044F \u0440\u043E\u043B\u0435\u0439 realm. \u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0440\u043E\u043B\u0438 \u0437\u0434\u0435\u0441\u044C \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u043D\u044B \u0438\u0437 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u044B\u0445 \u0440\u043E\u043B\u0435\u0439.
-user.available-roles.tooltip=\u041D\u0430\u0437\u043D\u0430\u0447\u0430\u0435\u043C\u044B\u0435 \u0440\u043E\u043B\u0438 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-user.assigned-roles-client.tooltip=\u0421\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0440\u043E\u043B\u0435\u0439 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-user.effective-roles-client.tooltip=\u0421\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0440\u043E\u043B\u0435\u0439 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0440\u043E\u043B\u0438 \u0437\u0434\u0435\u0441\u044C \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u043D\u044B \u0438\u0437 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0445 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u044B\u0445 \u0440\u043E\u043B\u0435\u0439.
-default.available-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u044B.
-realm-default-roles=\u0420\u043E\u043B\u0438 Realm \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-realm-default-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u044B \u043D\u043E\u0432\u044B\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\u043C.
-default.available-roles-client.tooltip=\u0420\u043E\u043B\u0438 \u0438\u0437 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u044B \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E.
-client-default-roles=\u0420\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-client-default-roles.tooltip=\u0420\u043E\u043B\u0438 \u0438\u0437 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0435 \u043A\u0430\u043A \u0440\u043E\u043B\u0438 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E.
-composite.available-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0441 \u044D\u0442\u043E\u0439 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-composite.associated-roles.tooltip=\u0420\u043E\u043B\u0438 \u0443\u0440\u043E\u0432\u043D\u044F Realm, \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0441 \u044D\u0442\u043E\u0439 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-composite.available-roles-client.tooltip=\u0420\u043E\u043B\u0438 \u0438\u0437 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u044B \u0441 \u044D\u0442\u043E\u0439 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-composite.associated-roles-client.tooltip=\u0420\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0441 \u044D\u0442\u043E\u0439 \u0441\u043E\u0441\u0442\u0430\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u044C\u044E.
-partial-import=\u0427\u0430\u0441\u0442\u0438\u0447\u043D\u044B\u0439 \u0438\u043C\u043F\u043E\u0440\u0442
-partial-import.tooltip=\u0427\u0430\u0441\u0442\u0438\u0447\u043D\u044B\u0439 \u0438\u043C\u043F\u043E\u0440\u0442 \u043F\u043E\u0437\u0432\u043E\u043B\u044F\u0435\u0442 \u0438\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439, \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432, \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044B \u0438\u0437 \u0440\u0430\u043D\u0435\u0435 \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E json-\u0444\u0430\u0439\u043B\u0430.
+identity-provider-mappers=Сопоставление поставщиков идентификации
+create-identity-provider-mapper=Создать сопоставление поставщика учетных записей
+add-identity-provider-mapper=Добавить сопоставление поставщика учетных записей
+client.description.tooltip=Задает описание клиента. Например 'Мой клиент для табеля учета времени'. Поддерживает ключи для локализованных значений. Например: ${my_client_description}
+
+expires=Истекает
+expiration=Истечение
+expiration.tooltip=Определяет, как долго токен будет оставаться валидным
+count=Счетчик
+count.tooltip=Определяет, как много клиентов может быть создано с помощью этого токена
+remainingCount=Счетчик остатка
+created=Создано
+back=Назад
+initial-access-tokens=Токены первичного доступа
+add-initial-access-tokens=Добавить токен первичного доступа
+initial-access-token=Токен первичного доступа
+initial-access.copyPaste.tooltip=Необходимо скопировать/вставить токен первичного доступа до того, как покинете эту страницу, т.к. в дальнейшем получить его будет невозможно
+continue=Продолжить
+initial-access-token.confirm.title=Скопировать токен первичного доступа
+initial-access-token.confirm.text=Пожалуйста, скопируйте и вставьте токен первичного доступа до того, как сделаете подтверждение, т.к. в дальнейшем получить его будет невозможно
+no-initial-access-available=Нет доступных токенов первичного доступа
+
+client-reg-policies=Политики регистрации клиента
+client-reg-policy.name.tooltip=Отображаемое наименование политики
+anonymous-policies=Политики анонимного доступа
+anonymous-policies.tooltip=Эти политики будут использоваться, когда сервис регистрации клиента вызывается неаутентифицированным запросом. Это означает, что запрос не содержит ни токена первичного доступа ни Bearer токена.
+auth-policies=Политики аутентифицированного доступа
+auth-policies.tooltip=Эти политики будут использоваться, когда сервис регистрации клиента вызывается аутентифицированным запросом. Это означает, что запрос содержит токен первичного доступа или Bearer токен.
+policy-name=Наименование политики
+no-client-reg-policies-configured=Нет политик регистрации клиента
+trusted-hosts.label=Доверенные хосты
+trusted-hosts.tooltip=Список хостов, которым доверенно и разрешено адресоваться на сервис регистрации клиентов или/или использоваться в значения URI клиентов. Вы можете использовать имена хостов или IP адреса. Если Вы используете звезду If you use star at the beginning (for example '*.example.com' ) then whole domain example.com will be trusted.
+host-sending-registration-request-must-match.label=Хост, посылающий запрос на регистрацию клиента должен совпадать
+host-sending-registration-request-must-match.tooltip=Если включено, то любой запрос на сервис регистрации клиентов разрешен только если он передан из доверенного хоста или домена.
+client-uris-must-match.label=URI клиента должны совпадать
+client-uris-must-match.tooltip=Если включено, то все клиентские URI (URI переадресации и прочие) разрешены только если они совпадают с доверенным хостом или доменом.
+allowed-protocol-mappers.label=Разрешенные сопоставления протокола
+allowed-protocol-mappers.tooltip=Белый список разрешенных поставщиков сопоставления протокола. Если есть попытка регистрации клиента, который содержит какие-либо сопоставления протокола, которые не находятся в белом списке, то регистрация таких клиентов будет отклонена.
+consent-required-for-all-mappers.label=Требуется согласие для сопоставлений
+consent-required-for-all-mappers.tooltip=Если включено, то то все вновь зарегестрированные сопоставления протокола будут автоматически иметь включенный consentRequired. Это означает, что пользователь должен подтвердить на экране согласия. Примечание: Экран согласия отображается только если клиент имеет включенный consentRequired. Таким образом, как правило этот переключатель используется совместно с политикой согласий.
+allowed-client-templates.label=Разрешить шаблоны клиента
+allowed-client-templates.tooltip=Белый список шаблонов клиента, который может быть использован при регистрации нового клиента. Попытка зарегестрировать клиента с каким-либо шаблоном не из белого списка будет отклонена. По умолчанию, белый лист пустой, таким образом шаблоны клиентам не доступны.
+max-clients.label=Максимальное количество клиентов для Realm
+max-clients.tooltip=Не позволяет регистрировать клиентов больше установленного предельного значения.
+
+client-templates=Шаблоны клиентов
+client-templates.tooltip=Шаблоны клиентов позволяют вам определить основную конфигурацию, которая может быть общей между несколькими клиентами
+
+groups=Группы
+
+group.add-selected.tooltip=Роли Realm, которые могут быть назначены на эту группу.
+group.assigned-roles.tooltip=Роли Realm, сопоставленные на группу
+group.effective-roles.tooltip=Все сопоставления realm ролей. Некоторые роли здесь могут быть унаследованы из составной роли.
+group.available-roles.tooltip=Назначаемые роли этого клиента.
+group.assigned-roles-client.tooltip=Сопоставление ролей этого клиента.
+group.effective-roles-client.tooltip=Сопоставление ролей этого клиента. Некоторые роли здесь могут быть унаследованы из сопоставленных составных ролей.
+
+default-roles=Роли по умолчанию
+no-realm-roles-available=Роли realm не доступны
+
+users=Пользователи
+user.add-selected.tooltip=Роли Realm, которые могут быть назначены пользователю.
+user.assigned-roles.tooltip=Роли Realm, сопоставленные пользователю
+user.effective-roles.tooltip=Все соответствия ролей realm. Некоторые роли здесь могут быть унаследованы из соответствующих составных ролей.
+user.available-roles.tooltip=Доступные роли для этого клиента.
+user.assigned-roles-client.tooltip=Соответствие ролей для этого клиента.
+user.effective-roles-client.tooltip=Соответствие ролей для этого клиента. Некоторые роли здесь могут быть унаследованы из соответствующих составных ролей.
+default.available-roles.tooltip=Роли уровня Realm, которые могут быть назначены.
+realm-default-roles=Роли Realm по умолчанию
+realm-default-roles.tooltip=Роли уровня Realm, которые могут быть назначены новым пользователям.
+default.available-roles-client.tooltip=Роли из этого клиента, которые могут быть назначены по умолчанию.
+client-default-roles=Роли клиента по умолчанию
+client-default-roles.tooltip=Роли из этого клиента, назначенные как роли по умолчанию.
+composite.available-roles.tooltip=Роли уровня Realm связанные с этой составной ролью.
+composite.associated-roles.tooltip=Роли уровня Realm, связанные с этой составной ролью.
+composite.available-roles-client.tooltip=Роли из этого клиента, которые могут быть назначены с этой составной ролью.
+composite.associated-roles-client.tooltip=Роли клиента, связанные с этой составной ролью.
+partial-import=Частичный импорт
+partial-import.tooltip=Частичный импорт позволяет импортировать пользователей, клиентов, и другие ресурсы из ранее экспортированного json-файла.
file=File
-exported-json-file=\u042D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 json \u0444\u0430\u0439\u043B
-import-from-realm=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0438\u0437 realm
-import-users=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-import-groups=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u044B
-import-clients=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-import-identity-providers=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-import-realm-roles=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u043E\u043B\u0438 realm
-import-client-roles=\u0418\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-if-resource-exists=\u0415\u0441\u043B\u0438 \u0440\u0435\u0441\u0443\u0440\u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442
-fail=\u041D\u0435\u0443\u0434\u0430\u0447\u0430
-skip=\u041F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C
-overwrite=\u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C
-if-resource-exists.tooltip=\u0423\u043A\u0430\u0437\u0430\u0442\u044C, \u0447\u0442\u043E \u0441\u043B\u0435\u0434\u0443\u0435\u0442 \u0434\u0435\u043B\u0430\u0442\u044C, \u0435\u0441\u043B\u0438 \u0432\u044B \u043F\u044B\u0442\u0430\u0435\u0442\u0435\u0441\u044C \u0438\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.
-
-action=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435
-role-selector=\u0421\u0435\u043B\u0435\u043A\u0442\u043E\u0440 \u0440\u043E\u043B\u0435\u0439
-realm-roles.tooltip=\u0420\u043E\u043B\u0438 Realm, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0432\u044B\u0431\u0440\u0430\u043D\u044B.
-
-select-a-role=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u043E\u043B\u044C
-select-realm-role=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u043E\u043B\u044C realm
-client-roles.tooltip=\u0420\u043E\u043B\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0432\u044B\u0431\u0440\u0430\u043D\u044B.
-select-client-role=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u043E\u043B\u044C \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-
-client-template=\u0428\u0430\u0431\u043B\u043E\u043D \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-template.tooltip=\u0428\u0430\u0431\u043B\u043E\u043D \u043A\u043B\u0438\u0435\u043D\u0442\u0430, \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u044E\u0449\u0438\u0439 \u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u043D\u0438\u0435 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0438\u0437
-client-saml-endpoint=\u041A\u043E\u043D\u0435\u0447\u043D\u0430\u044F \u0442\u043E\u0447\u043A\u0430 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 SAML \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-add-client-template=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0448\u0430\u0431\u043B\u043E\u043D \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-
-manage=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435
-authentication=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F
-user-federation=\u0424\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-events=\u0421\u043E\u0431\u044B\u0442\u0438\u044F
-realm-settings=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 Realm
-configure=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F
-select-realm=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 realm
-add=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C
-
-client-template.name.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430. \u0414\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u043E \u0434\u043B\u044F realm
-client-template.description.tooltip=\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-template.protocol.tooltip=\u041A\u0430\u043A\u0430\u044F \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430 SSO \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0430\u043D\u0430 \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u043C \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-
-add-user-federation-provider=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u043B\u0443\u0436\u0431\u0443 \u0444\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-required-settings=\u0422\u0440\u0435\u0431\u0443\u0435\u043C\u044B\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438
-provider-id=ID \u0441\u043B\u0443\u0436\u0431\u044B
-console-display-name=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0432 \u043A\u043E\u043D\u0441\u043E\u043B\u0438
-console-display-name.tooltip=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u0438\u043C\u044F \u0441\u043B\u0443\u0436\u0431\u044B, \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u043E\u0435 \u0441 \u043A\u043E\u043D\u0441\u043E\u043B\u044C\u044E \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430.
-priority=\u041F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442
-priority.tooltip=\u041F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442 \u0441\u043B\u0443\u0436\u0431\u044B \u043F\u0440\u0438 \u043F\u043E\u0438\u0441\u043A\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F. \u0412\u043F\u0435\u0440\u0435\u0434 \u0438\u0434\u0443\u0442 \u0431\u043E\u043B\u0435\u0435 \u043D\u0438\u0437\u043A\u0438\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F.
-sync-settings=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438
-periodic-full-sync=\u041F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043F\u043E\u043B\u043D\u0430\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F
-periodic-full-sync.tooltip=\u0414\u043E\u043B\u0436\u043D\u0430 \u043B\u0438 \u043F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043F\u043E\u043B\u043D\u0430\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u0441 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0432 Keycloak \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0430 \u0438\u043B\u0438 \u043D\u0435\u0442
-full-sync-period=\u041F\u0435\u0440\u0438\u043E\u0434 \u043F\u043E\u043B\u043D\u043E\u0439 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438
-full-sync-period.tooltip=\u041F\u0435\u0440\u0438\u043E\u0434 \u0434\u043B\u044F \u043F\u043E\u043B\u043D\u043E\u0439 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u0441\u0435\u043A\u0443\u043D\u0434\u0430\u0445
-periodic-changed-users-sync=\u041F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-periodic-changed-users-sync.tooltip=\u0414\u043E\u043B\u0436\u043D\u0430 \u043B\u0438 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442\u044C \u043F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u043D\u043E\u0432\u044B\u0445 \u0438 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0441 Keycloak
-changed-users-sync-period=\u041F\u0435\u0440\u0438\u043E\u0434 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-changed-users-sync-period.tooltip=\u041F\u0435\u0440\u0438\u043E\u0434 \u0434\u043B\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043D\u044B\u0445 \u0438 \u043D\u043E\u0432\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0432 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0432 \u0441\u0435\u043A\u0443\u043D\u0434\u0430\u0445
-synchronize-changed-users=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-synchronize-all-users=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u0432\u0441\u0435\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
+exported-json-file=Экспортированный json файл
+import-from-realm=Импортировать из realm
+import-users=Импортировать пользователей
+import-groups=Импортировать группы
+import-clients=Импортировать клиентов
+import-identity-providers=Импортировать поставщиков идентификации
+import-realm-roles=Импортировать роли realm
+import-client-roles=Импортировать роли клиентов
+if-resource-exists=Если ресурс существует
+fail=Неудача
+skip=Пропустить
+overwrite=Перезаписать
+if-resource-exists.tooltip=Указать, что следует делать, если вы пытаетесь импортировать ресурс, который уже существует.
+
+action=Действие
+role-selector=Селектор ролей
+realm-roles.tooltip=Роли Realm, которые могут быть выбраны.
+
+select-a-role=Выберите роль
+select-realm-role=Выберите роль realm
+client-roles.tooltip=Роли клиента, которые могут быть выбраны.
+select-client-role=Выберите роль клиента
+
+client-template=Шаблон клиента
+client-template.tooltip=Шаблон клиента, определяющий наследование конфигурации этого клиента из
+client-saml-endpoint=Конечная точка доступа SAML клиента
+add-client-template=Добавить шаблон клиента
+
+manage=Управление
+authentication=Аутентификация
+user-federation=Федерация пользователей
+user-storage=Хранилище пользователей
+events=События
+realm-settings=Настройки Realm
+configure=Конфигурация
+select-realm=Выберите realm
+add=Добавить
+
+client-template.name.tooltip=Наименование шаблона клиента. Должно быть уникально для realm
+client-template.description.tooltip=Описание шаблона клиента
+client-template.protocol.tooltip=Какая конфигурация протокола SSO будет поддержана шаблоном клиента
+
+add-user-federation-provider=Добавить службу федерации пользователей
+add-user-storage-provider=Добавить службу хранилища пользователей
+required-settings=Требуемые настройки
+provider-id=ID службы
+console-display-name=Наименование в консоли
+console-display-name.tooltip=Отображаемое имя службы, связанное с консолью администратора.
+priority=Приоритет
+priority.tooltip=Приоритет службы при поиске пользователя. Вперед идут более низкие значения.
+sync-settings=Синхронизировать настройки
+periodic-full-sync=Периодическая полная синхронизация
+periodic-full-sync.tooltip=Должна ли периодическая полная синхронизация с поставщиком идентификации в Keycloak включена или нет
+full-sync-period=Период полной синхронизации
+full-sync-period.tooltip=Период для полной синхронизации в секундах
+periodic-changed-users-sync=Периодическая синхронизация изменений пользователей
+periodic-changed-users-sync.tooltip=Должна ли происходить периодическая синхронизация новых и измененных пользователей поставщика идентификации с Keycloak
+changed-users-sync-period=Период синхронизации измененных пользователей
+changed-users-sync-period.tooltip=Период для синхронизации измененных и новых пользователей в поставщике идентификации в секундах
+synchronize-changed-users=Синхронизация измененных пользователей
+synchronize-all-users=Синхронизация всех пользователей
+remove-imported-users=Удалить импортированных
+unlink-users=Отвязать пользователей
kerberos-realm=Kerberos Realm
-kerberos-realm.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 kerberos realm. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 FOO.ORG
-server-principal=\u041E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0441\u0435\u0440\u0432\u0435\u0440
-server-principal.tooltip=\u041F\u043E\u043B\u043D\u043E\u0435 \u0438\u043C\u044F \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043B\u044F HTTP \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0432\u043A\u043B\u044E\u0447\u0430\u044F \u0441\u0435\u0440\u0432\u0435\u0440\u043D\u043E\u0435 \u0438 \u0434\u043E\u043C\u0435\u043D\u043D\u043E\u0435 \u0438\u043C\u044F. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 HTTP/host.foo.org@FOO.ORG
+kerberos-realm.tooltip=Наименование kerberos realm. Например FOO.ORG
+server-principal=Основной сервер
+server-principal.tooltip=Полное имя основного сервера для HTTP сервиса, включая серверное и доменное имя. Например HTTP/host.foo.org@FOO.ORG
keytab=KeyTab
-keytab.tooltip=\u041C\u0435\u0441\u0442\u043E\u043F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0444\u0430\u0439\u043B\u0430 KeyTab \u0432 Kerberos, \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0433\u043E \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 /etc/krb5.keytab
-debug=\u041E\u0442\u043B\u0430\u0434\u0447\u0438\u043A
-debug.tooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043E\u0442\u043B\u0430\u0434\u043E\u0447\u043D\u044B\u0435 \u043B\u043E\u0433\u0438 \u0432 \u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u044B\u0439 \u0432\u044B\u0432\u043E\u0434 \u0434\u043B\u044F Krb5LoginModule.
-allow-password-authentication=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E \u043F\u043E \u043F\u0430\u0440\u043E\u043B\u044E
-allow-password-authentication.tooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E \u0438\u043C\u0435\u043D\u0438/\u043F\u0430\u0440\u043E\u043B\u044F \u0432\u043E\u043F\u0440\u0435\u043A\u0438 \u0431\u0430\u0437\u0435 \u0434\u0430\u043D\u043D\u044B\u0445 Kerberos
-edit-mode=\u0420\u0435\u0436\u0438\u043C \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F
-edit-mode.tooltip=READ_ONLY \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F \u043D\u0435 \u0434\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0432\u0441\u0435\u0433\u0434\u0430 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0441\u044F \u0441 \u043F\u0430\u0440\u043E\u043B\u0435\u043C Kerberos. UNSYNCED \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u043C\u043E\u0436\u0435\u0442 \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u0441\u0432\u043E\u0439 \u043F\u0430\u0440\u043E\u043B\u044C \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043D\u043D\u044B\u0445 Keycloak \u0438 \u0442\u043E\u0433\u0434\u0430 \u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0432\u043C\u0435\u0441\u0442\u043E \u043F\u0430\u0440\u043E\u043B\u044F Kerberos
-ldap.edit-mode.tooltip=READ_ONLY \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 \u0447\u0442\u0435\u043D\u0438\u0435 \u0438\u0437 LDAP. WRITABLE \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u0434\u0430\u043D\u043D\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u043E\u0431\u0440\u0430\u0442\u043D\u043E \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u044B \u0432 LDAP \u043F\u043E \u0437\u0430\u044F\u0432\u043A\u0435. UNSYNCED \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0431\u0443\u0434\u0443\u0442 \u0438\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u044B, \u043D\u043E \u043D\u0435 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u044B \u043E\u0431\u0440\u0430\u0442\u043D\u043E \u0432 LDAP.
-update-profile-first-login=\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u043F\u0440\u0438 \u043F\u0435\u0440\u0432\u043E\u043C \u0432\u0445\u043E\u0434\u0435
-update-profile-first-login.tooltip=\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u043F\u0440\u0438 \u043F\u0435\u0440\u0432\u043E\u043C \u0432\u0445\u043E\u0434\u0435
-sync-registrations=\u0421\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438
-ldap.sync-registrations.tooltip=\u0414\u043E\u043B\u0436\u043D\u044B \u043B\u0438 \u0432\u043D\u043E\u0432\u044C \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0431\u044B\u0442\u044C \u0441\u043E\u0437\u0434\u0430\u043D\u044B \u0432 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 LDAP? \u041F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442 \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442 \u043A\u0430\u043A\u043E\u0439 \u0438\u0437 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0431\u0443\u0434\u0435\u0442 \u0432\u044B\u0431\u0440\u0430\u043D \u0434\u043B\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-vendor=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A
-ldap.vendor.tooltip=LDAP \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A (\u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440)
-username-ldap-attribute=\u0410\u0442\u0440\u0438\u0431\u0443\u0442 Username \u0432 LDAP
-ldap-attribute-name-for-username=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 LDAP \u0434\u043B\u044F \u0438\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-username-ldap-attribute.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 LDAP \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044F \u043A\u0430\u043A \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0432 Keycloak. \u0414\u043B\u044F \u043C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u0432 LDAP \u044D\u0442\u043E \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C 'uid'. \u0414\u043B\u044F Active directory \u044D\u0442\u043E \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C 'sAMAccountName' \u0438\u043B\u0438 'cn'. \u0410\u0442\u0440\u0438\u0431\u0443\u0442 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D \u0434\u043B\u044F \u0432\u0441\u0435\u0445 LDAP \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0438\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0438\u0437 LDAP \u0432 Keycloak.
-rdn-ldap-attribute=\u0410\u0442\u0440\u0438\u0431\u0443\u0442 RDN \u0432 LDAP
-ldap-attribute-name-for-user-rdn=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 LDAP \u0434\u043B\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 RDN
-rdn-ldap-attribute.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043E\u0432 LDAP, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u043A\u0430\u043A RDN (\u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442) \u043E\u0431\u044B\u0447\u043D\u043E\u0433\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F DN. \u041E\u0431\u044B\u0447\u043D\u043E \u043E\u043D\u043E \u0442\u0430\u043A\u043E\u0435 \u0436\u0435, \u043A\u0430\u043A \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u0438\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F LDAP, \u043E\u0434\u043D\u0430\u043A\u043E \u043E\u043D \u043D\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u0435\u043D. \u0414\u043B\u044F \u043F\u0440\u0438\u043C\u0435\u0440\u0430, \u0434\u043B\u044F Active directory \u043E\u0431\u044B\u0447\u043D\u043E \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F 'cn' \u043A\u0430\u043A \u0430\u0442\u0440\u0438\u0431\u0443\u0442 RDN, \u0432 \u0442\u043E \u0432\u0440\u0435\u043C\u044F \u043A\u0430\u043A \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u0438\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C 'sAMAccountName'.
-uuid-ldap-attribute=\u0410\u0442\u0440\u0438\u0431\u0443\u0442 UUID \u0432 LDAP
-ldap-attribute-name-for-uuid=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 LDAP \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430 \u0434\u043B\u044F UUID
-uuid-ldap-attribute.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 LDAP \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430,\u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u043A\u0430\u043A \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u0432 (UUID) \u0432 LDAP. \u0414\u043B\u044F \u043C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u0430 LDAP \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u0432 \u044D\u0442\u043E 'entryUUID' \u043E\u0434\u043D\u0430\u043A\u043E \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u043E\u0442\u043B\u0438\u0447\u0430\u0442\u044C\u0441\u044F. \u0414\u043B\u044F \u043F\u0440\u0438\u043C\u0435\u0440\u0430, \u0434\u043B\u044F Active directory \u043E\u043D \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C 'objectGUID'. \u0415\u0441\u043B\u0438 \u0432\u0430\u0448 LDAP \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043F\u043E\u043D\u044F\u0442\u0438\u0435 UUID, \u0432\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043B\u044E\u0431\u043E\u0439 \u0434\u0440\u0443\u0433\u043E\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0443\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u043C \u0441\u0440\u0435\u0434\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0432 \u0434\u0435\u0440\u0435\u0432\u0435 LDAP. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 'uid' \u0438\u043B\u0438 'entryDN'.
-user-object-classes=\u041A\u043B\u0430\u0441\u0441\u044B \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u0432 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-ldap-user-object-classes.placeholder=\u041A\u043B\u0430\u0441\u0441\u044B \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u0432 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F LDAP (\u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0435 \u0437\u0430\u043F\u044F\u0442\u043E\u0439)
-
-ldap-connection-url=URL \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F \u0441 LDAP
-ldap-users-dn=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 DN LDAP
-ldap-bind-dn=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 DN LDAP
-ldap-bind-credentials=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 LDAP
-ldap-filter=LDAP \u0444\u0438\u043B\u044C\u0442\u0440
-ldap.user-object-classes.tooltip=\u0412\u0441\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u0438\u0437 LDAP objectClass \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043E\u0432 \u0434\u043B\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0432 LDAP, \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0435 \u0437\u0430\u043F\u044F\u0442\u043E\u0439. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: 'inetOrgPerson, organizationalPerson' . \u0412\u043D\u043E\u0432\u044C \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 Keycloak \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u044B \u0432 LDAP \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u044D\u0442\u0438\u043C\u0438 \u043A\u043B\u0430\u0441\u0441\u0430\u043C\u0438 \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u0432, \u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u0437\u0430\u043F\u0438\u0441\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 LDAP \u0431\u0443\u0434\u0443\u0442 \u043D\u0430\u0439\u0434\u0435\u043D\u044B \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442 \u0432\u0441\u0435 \u044D\u0442\u0438 \u043A\u043B\u0430\u0441\u0441\u044B \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u0432.
-
-connection-url=URL \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F
-ldap.connection-url.tooltip=URL \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F \u0441 \u0432\u0430\u0448\u0438\u043C \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u043C LDAP
-test-connection=\u0422\u0435\u0441\u0442 \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F
-users-dn=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 DN
-ldap.users-dn.tooltip=\u041F\u043E\u043B\u043D\u044B\u0439 DN \u0438\u0437 \u0434\u0435\u0440\u0435\u0432\u0430 LDAP \u0433\u0434\u0435 \u043F\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0442 \u0432\u0430\u0448\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438. \u042D\u0442\u043E\u0442 DN \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 LDAP. \u041E\u043D \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C, \u0434\u043B\u044F \u043F\u0440\u0438\u043C\u0435\u0440\u0430 'ou=users,dc=example,dc=com' \u043F\u0440\u0438 \u0443\u0441\u043B\u043E\u0432\u0438\u0438, \u0447\u0442\u043E \u0432\u0430\u0448 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C DN \u043F\u043E\u0445\u043E\u0436\u0438\u0439 \u043D\u0430'uid=john,ou=users,dc=example,dc=com'
-authentication-type=\u0422\u0438\u043F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-ldap.authentication-type.tooltip=\u0422\u0438\u043F LDAP \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438. \u0421\u0435\u0439\u0447\u0430\u0441 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0442\u043E\u043B\u044C\u043A\u043E \u043C\u0435\u0445\u0430\u043D\u0438\u0437\u043C\u044B 'none' (\u0430\u043D\u043E\u043D\u0438\u043C\u043D\u0430\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F LDAP) \u0438\u043B\u0438 'simple' (\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u043F\u043E \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u043C \u043B\u043E\u0433\u0438\u043D\u0443 \u0438 \u043F\u0430\u0440\u043E\u043B\u044E)
-bind-dn=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 DN
-ldap.bind-dn.tooltip=DN \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 LDAP, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u044B Keycloak \u0434\u043B\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 LDAP
-bind-credential=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445
-ldap.bind-credential.tooltip=\u041F\u0430\u0440\u043E\u043B\u044C \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 LDAP
-test-authentication=\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-custom-user-ldap-filter=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0439 \u0424\u0438\u043B\u044C\u0442\u0440 LDAP \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-ldap.custom-user-ldap-filter.tooltip=\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 \u0444\u0438\u043B\u044C\u0442\u0440 LDAP \u0434\u043B\u044F \u0444\u0438\u043B\u044C\u0442\u0440\u0430\u0446\u0438\u0438 \u0438\u0441\u043A\u043E\u043C\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439. \u041E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u043F\u043E\u043B\u0435 \u043F\u0443\u0441\u0442\u044B\u043C, \u0435\u0441\u043B\u0438 \u043D\u0435 \u043D\u0443\u0436\u0434\u0430\u0435\u0442\u0435\u0441\u044C \u0432 \u0434\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u043C \u0444\u0438\u043B\u044C\u0442\u0440\u0435. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C, \u0447\u0442\u043E \u043E\u043D \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0441 '(' \u0438 \u0437\u0430\u043A\u0430\u043D\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044F ')'
-search-scope=\u041F\u043E\u0438\u0441\u043A \u043E\u0431\u043B\u0430\u0441\u0442\u0438
-ldap.search-scope.tooltip=\u0414\u043B\u044F \u043E\u0434\u043D\u043E\u0433\u043E \u0443\u0440\u043E\u0432\u043D\u044F \u043C\u044B \u0438\u0449\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 DN, \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0445 \u043A\u0430\u043A \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 DN. \u0414\u043B\u044F \u043F\u043E\u0434\u0434\u0435\u0440\u0435\u0432\u044C\u0435\u0432 \u043C\u044B \u0438\u0449\u0435\u043C \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E \u0432 \u0438\u0445 \u043F\u043E\u0434\u0434\u0435\u0440\u0435\u0432\u044C\u044F\u0445. \u0421\u043C\u043E\u0442\u0440\u0438\u0442\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u044E LDAP \u0434\u043B\u044F \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u044B\u0445 \u0434\u0435\u0442\u0430\u043B\u0435\u0439
-use-truststore-spi=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0445 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0432 SPI
-ldap.use-truststore-spi.tooltip=\u041E\u043F\u0440\u0435\u0434\u0435\u043B\u044F\u0435\u0442, \u0431\u0443\u0434\u0435\u0442 \u043B\u0438 \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u0441 LDAP \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0445 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0432 SPI \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0430\u043C\u0438, \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u043C\u0438 \u0432 keycloak-server.json. '\u0412\u0441\u0435\u0433\u0434\u0430' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u043E\u043D\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u0432\u0441\u0435\u0433\u0434\u0430. '\u041D\u0438\u043A\u043E\u0433\u0434\u0430' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u043E\u043D\u0438 \u043D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u044B. '\u0422\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F ldap`\u043E\u0432' \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u043E\u043D\u0438 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u044B \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0432\u0430\u0448\u0438\u043C\u0438 \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F\u043C\u0438 \u043A ldap \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u043C. \u041E\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043D\u0438\u043C\u0430\u043D\u0438\u0435, \u0447\u0442\u043E \u0435\u0441\u043B\u0438 keycloak-server.json \u043D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D, \u0442\u043E \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E Java \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C cacerts \u0438\u043B\u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442, \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043D\u044B\u0439 \u0432 'javax.net.ssl.trustStore'.
-connection-pooling=\u041F\u0443\u043B \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0439
-ldap.connection-pooling.tooltip=\u0414\u043E\u043B\u0436\u0435\u043D \u043B\u0438 Keycloak \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043F\u0443\u043B \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0439 \u0434\u043B\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A LDAP \u0441\u0435\u0440\u0432\u0435\u0440\u0443
-ldap.pagination.tooltip=\u0414\u043E\u043B\u0436\u0435\u043D \u043B\u0438 LDAP \u0441\u0435\u0440\u0432\u0435\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044C \u043F\u043E\u0441\u0442\u0440\u0430\u043D\u0438\u0447\u043D\u044B\u0439 \u0432\u044B\u0432\u043E\u0434.
-kerberos-integration=\u0418\u043D\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044F \u0441 Kerberos
-allow-kerberos-authentication=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E Kerberos
-ldap.allow-kerberos-authentication.tooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044E HTTP \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0441 \u0442\u043E\u043A\u0435\u043D\u0430\u043C\u0438 SPNEGO/Kerberos. \u0414\u0430\u043D\u043D\u044B\u0435 \u043E\u0431 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\u0445 \u0431\u0443\u0434\u0443\u0442 \u043F\u0440\u0435\u0434\u0443\u0441\u043C\u043E\u0442\u0440\u0435\u043D\u044B \u0438\u0437 \u044D\u0442\u043E\u0433\u043E LDAP \u0441\u0435\u0440\u0432\u0435\u0440\u0430
-use-kerberos-for-password-authentication=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C Kerberos \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E \u043F\u0430\u0440\u043E\u043B\u044E
-ldap.use-kerberos-for-password-authentication.tooltip=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043C\u043E\u0434\u0443\u043B\u044C \u0432\u0445\u043E\u0434\u0430 Kerberos \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E \u043B\u043E\u0433\u0438\u043D/\u043F\u0430\u0440\u043E\u043B\u044C \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 Kerberos \u0432\u043C\u0435\u0441\u0442\u043E \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 LDAP \u0441 Directory Service API
-batch-size=\u0420\u0430\u0437\u043C\u0435\u0440 \u043F\u0430\u0447\u043A\u0438
-ldap.batch-size.tooltip=\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 LDAP, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043C\u043F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u044B \u0432 Keycloak \u0437\u0430 \u043E\u0434\u043D\u0443 \u0442\u0440\u0430\u043D\u0437\u0430\u043A\u0446\u0438\u044E.
-ldap.periodic-full-sync.tooltip=\u0414\u043E\u043B\u0436\u043D\u0430 \u043B\u0438 \u0431\u044B\u0442\u044C \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0430 \u043F\u043E\u043B\u043D\u0430\u044F \u043F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 LDAP \u0432 Keycloak \u0438\u043B\u0438 \u043D\u0435\u0442
-ldap.periodic-changed-users-sync.tooltip=\u0414\u043E\u043B\u0436\u043D\u0430 \u043B\u0438 \u0431\u044B\u0442\u044C \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0430 \u043F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u044F \u043D\u043E\u0432\u044B\u0445 \u0438 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 LDAP \u0432 Keycloak \u0438\u043B\u0438 \u043D\u0435\u0442
-ldap.changed-users-sync-period.tooltip=\u041F\u0435\u0440\u0438\u043E\u0434 \u0434\u043B\u044F \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043D\u044B\u0445 \u0438\u043B\u0438 \u0432\u043D\u043E\u0432\u044C \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 LDAP \u0432 \u0441\u0435\u043A\u0443\u043D\u0434\u0430\u0445
-user-federation-mappers=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0444\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-create-user-federation-mapper=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u0444\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-add-user-federation-mapper=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u0444\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-provider-name=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430
-no-user-federation-providers-configured=\u041D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D \u0444\u0435\u0434\u0435\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-add-identity-provider=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-add-identity-provider-link=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-identity-provider=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-identity-provider-user-id=ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-identity-provider-user-id.tooltip=\u0423\u043D\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0439 ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043D\u0430 \u0441\u0442\u043E\u0440\u043E\u043D\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-identity-provider-username=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-identity-provider-username.tooltip=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043D\u0430 \u0441\u0442\u043E\u0440\u043E\u043D\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-pagination=\u041F\u043E\u0441\u0442\u0440\u0430\u043D\u0438\u0447\u043D\u044B\u0439 \u0432\u044B\u0432\u043E\u0434
-
-browser-flow=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430
-browser-flow.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440.
-registration-flow=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438
-registration-flow.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-direct-grant-flow=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439 Direct Grant Flow
-direct-grant-flow.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 direct grant.
-reset-credentials=\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435
-reset-credentials.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u043A\u043E\u0433\u0434\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0437\u0430\u0431\u044B\u043B \u0441\u0432\u043E\u0438 \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435.
-client-authentication=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-client-authentication.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432.
-new=\u0421\u043E\u0437\u0434\u0430\u0442\u044C
-copy=\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C
-add-execution=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435
-add-flow=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439
-auth-type=\u0422\u0438\u043F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-requirement=\u0422\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F
-config=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F
-no-executions-available=\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0439
-authentication-flows=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-create-authenticator-config=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430
-authenticator.alias.tooltip=\u041D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
-otp-type=\u0422\u0438\u043F \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u043E\u0433\u043E \u043F\u0430\u0440\u043E\u043B\u044F OTP
-time-based=\u041E\u0441\u043D\u043E\u0432\u0430\u043D \u043D\u0430 \u0432\u0440\u0435\u043C\u0435\u043D\u0438
-counter-based=\u041E\u0441\u043D\u043E\u0432\u0430\u043D \u043D\u0430 \u0441\u0447\u0435\u0442\u0447\u0438\u043A\u0435
-otp-type.tooltip=totp \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0412\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u043C \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u043C \u043F\u0430\u0440\u043E\u043B\u0435\u043C. 'hotp' \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u043D\u0430 \u0441\u0447\u0435\u0442\u0447\u0438\u043A\u0435 \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C \u0432 \u043A\u043E\u0442\u043E\u0440\u043E\u043C \u0441\u0435\u0440\u0432\u0435\u0440 \u0445\u0440\u0430\u043D\u0438\u0442 \u0441\u0447\u0435\u0442\u0447\u0438\u043A \u0445\u0435\u0448\u0430.
-otp-hash-algorithm=\u0410\u043B\u0433\u043E\u0440\u0438\u0442\u043C \u0445\u0435\u0448\u0430 OTP
-otp-hash-algorithm.tooltip=\u041A\u0430\u043A\u043E\u0439 \u0430\u043B\u0433\u043E\u0440\u0438\u0442\u043C \u0445\u0435\u0448\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 OTP.
-number-of-digits=\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0446\u0438\u0444\u0440
-otp.number-of-digits.tooltip=\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u0446\u0438\u0444\u0440 \u0434\u043E\u043B\u0436\u0435\u043D \u0438\u043C\u0435\u0442\u044C OTP?
-look-ahead-window=\u041E\u043A\u043D\u043E \u0432\u043F\u0435\u0440\u0435\u0434
-otp.look-ahead-window.tooltip=\u041A\u0430\u043A \u0434\u0430\u043B\u0435\u043A\u043E \u0432\u043F\u0435\u0440\u0435\u0434 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043E\u043B\u0436\u0435\u043D \u0432\u044B\u0433\u043B\u044F\u0434\u0435\u0442\u044C \u0432 \u0441\u043B\u0443\u0447\u0430\u0435 \u0435\u0441\u043B\u0438 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0442\u043E\u043A\u0435\u043D \u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043D\u0435 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u043D\u044B \u0441 \u0432\u0440\u0435\u043C\u0435\u043D\u0435\u043C \u0438\u043B\u0438 \u0441\u0447\u0435\u0442\u0447\u0438\u043A\u043E\u043C
-initial-counter=\u041D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0441\u0447\u0435\u0442\u0447\u0438\u043A\u0430
-otp.initial-counter.tooltip=\u041A\u0430\u043A\u0438\u043C \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0430\u0447\u0430\u043B\u044C\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0441\u0447\u0435\u0442\u0447\u0438\u043A\u0430?
-otp-token-period=\u041F\u0435\u0440\u0438\u043E\u0434 \u0442\u043E\u043A\u0435\u043D\u0430 OTP
-otp-token-period.tooltip=\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0435\u043A\u0443\u043D\u0434 \u0442\u043E\u043A\u0435\u043D OTP \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u0435\u043D? \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 30 \u0441\u0435\u043A\u0443\u043D\u0434.
-table-of-password-policies=\u0422\u0430\u0431\u043B\u0438\u0446\u0430 \u043F\u043E\u043B\u0438\u0442\u0438\u043A \u043F\u0430\u0440\u043E\u043B\u044F
-add-policy.placeholder=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0443...
-policy-type=\u0422\u0438\u043F \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438
-policy-value=\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438
-admin-events=\u0421\u043E\u0431\u044B\u0442\u0438\u044F \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430
-admin-events.tooltip=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u044B\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u0432 \u044D\u0442\u043E\u043C realm. \u0421\u043E\u0431\u044B\u0442\u0438\u044F, \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0441 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 realm. \u0427\u0442\u043E\u0431\u044B \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u0439, \u043F\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E.
-login-events=\u0421\u043E\u0431\u044B\u0442\u0438\u044F \u0432\u0445\u043E\u0434\u0430
-filter=\u0424\u0438\u043B\u044C\u0442\u0440
-update=\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C
-reset=\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C
-operation-types=\u0422\u0438\u043F\u044B \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0439
-select-operations.placeholder=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438...
-resource-path=\u041F\u0443\u0442\u044C \u043A \u0440\u0435\u0441\u0443\u0440\u0441\u0443
-resource-path.tooltip=\u0424\u0438\u043B\u044C\u0442\u0440 \u043F\u043E \u043F\u0443\u0442\u0438 \u043A \u0440\u0435\u0441\u0443\u0440\u0441\u0443. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043F\u043E\u0434\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0443 '*' \u0434\u043B\u044F \u0441\u043E\u0432\u043F\u0430\u0434\u0435\u043D\u0438\u044F \u043E\u0434\u043D\u043E\u0439 \u0447\u0430\u0441\u0442\u0438 \u043F\u0443\u0442\u0438 \u0438 '**' \u0441\u043E\u0432\u043F\u0430\u0434\u0435\u043D\u0438\u0435 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u0438\u0445 \u0447\u0430\u0441\u0442\u0435\u0439. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 'realms/*/clients/asbc' \u0432\u044B\u0431\u0435\u0440\u0435\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u0441 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u043E\u043C asbc \u0432 \u043B\u044E\u0431\u043E\u043C realm, \u0432 \u0442\u043E \u0432\u0440\u0435\u043C\u044F \u043A\u0430\u043A 'realms/master/**' \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u0442 \u043D\u0438\u0447\u0435\u0433\u043E \u0432 master realm.
-date-(from)=\u0414\u0430\u0442\u0430 (\u0421)
-date-(to)=\u0414\u0430\u0442\u0430 (\u041F\u043E)
-authentication-details=\u0414\u0435\u0442\u0430\u043B\u0438 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-ip-address=IP \u0430\u0434\u0440\u0435\u0441
-time=\u0412\u0440\u0435\u043C\u044F
-operation-type=\u0422\u0438\u043F \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438
-auth=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F
-representation=\u041F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435
-register=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F
-required-action=\u0422\u0440\u0435\u0431\u0443\u0435\u043C\u043E\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435
-default-action=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-auth.default-action.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0442\u043E \u043B\u044E\u0431\u043E\u043C\u0443 \u043D\u043E\u0432\u043E\u043C\u0443 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u0431\u0443\u0434\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043E \u0442\u0440\u0435\u0431\u0443\u0435\u043C\u043E\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435.
-no-required-actions-configured=\u0422\u0440\u0435\u0431\u0443\u0435\u043C\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u043D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u044B
-defaults-to-id=\u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E id
-flows=\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0438
-bindings=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F
-required-actions=\u0422\u0440\u0435\u0431\u0443\u0435\u043C\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F
-password-policy=\u041F\u043E\u043B\u0438\u0442\u0438\u043A\u0438 \u043F\u0430\u0440\u043E\u043B\u044F
-otp-policy=\u041F\u043E\u043B\u0438\u0442\u0438\u043A\u0438 OTP
-user-groups=\u0413\u0440\u0443\u043F\u043F\u044B \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-default-groups=\u0413\u0440\u0443\u043F\u043F\u044B \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E
-groups.default-groups.tooltip=\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0435\u0442 \u0433\u0440\u0443\u043F\u043F\u044B, \u0432 \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043D\u043E\u0432\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u044B \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.
-cut=\u0412\u044B\u0440\u0435\u0437\u0430\u0442\u044C
-paste=\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C
-
-create-group=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u0443
-create-authenticator-execution=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430
-create-form-action-execution=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0444\u043E\u0440\u043C\u0443 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F
-create-top-level-form=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0432\u0435\u0440\u0445\u043D\u0435\u0443\u0440\u043E\u0432\u043D\u0435\u0432\u0443\u044E \u0444\u043E\u0440\u043C\u0443
-flow.alias.tooltip=\u0417\u0430\u0434\u0430\u0435\u0442 \u043E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u043C\u043E\u0435 \u0438\u043C\u044F \u0434\u043B\u044F \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F.
+keytab.tooltip=Местоположение файла KeyTab в Kerberos, содержащего учетные данные основного сервера. Например /etc/krb5.keytab
+debug=Отладчик
+debug.tooltip=Включить/выключить отладочные логи в стандартный вывод для Krb5LoginModule.
+allow-password-authentication=Разрешить аутентификацию по паролю
+allow-password-authentication.tooltip=Включить/выключить возможность аутентификации по имени/пароля вопреки базе данных Kerberos
+edit-mode=Режим редактирования
+edit-mode.tooltip=READ_ONLY означает, что обновление пароля не допускается и пользователь всегда аутентифицируется с паролем Kerberos. UNSYNCED означает, что пользователь может изменить свой пароль в базе данных Keycloak и тогда он будет использован вместо пароля Kerberos
+ldap.edit-mode.tooltip=READ_ONLY означает доступ только на чтение из LDAP. WRITABLE означает, что данные будут обратно синхронизированы в LDAP по заявке. UNSYNCED означает, что данные пользователя будут импортированы, но не синхронизированы обратно в LDAP.
+update-profile-first-login=Обновить профиль при первом входе
+update-profile-first-login.tooltip=Обновить профиль при первом входе
+sync-registrations=Синхронизировать регистрации
+ldap.sync-registrations.tooltip=Должны ли вновь созданные пользователи быть созданы в хранилище LDAP? Приоритет определяет какой из поставщиков будет выбран для синхронизации нового пользователя.
+import-enabled=Импортировать пользователей
+ldap.import-enabled.tooltip=Если включено, пользователи LDAP будут импортированы в базу данных Keycloak и синхронизированы через сконфигурированные политики синхронизации.
+vendor=Поставщик
+ldap.vendor.tooltip=LDAP поставщик (провайдер)
+username-ldap-attribute=Атрибут Username в LDAP
+ldap-attribute-name-for-username=Наименование атрибута LDAP для имени пользователя
+username-ldap-attribute.tooltip=Наименование LDAP атрибута, которое отображается как имя пользователя в Keycloak. Для множества серверов LDAP это может быть 'uid'. Для Active directory это может быть 'sAMAccountName' или 'cn'. Атрибут должен быть заполнен для всех LDAP записей пользователей, которые вы хотите импортировать из LDAP в Keycloak.
+rdn-ldap-attribute=Атрибут RDN в LDAP
+ldap-attribute-name-for-user-rdn=Наименование атрибута LDAP для пользователей RDN
+rdn-ldap-attribute.tooltip=Наименование атрибутов LDAP, которое используется как RDN (верхний атрибут) обычного пользователя DN. Обычно оно такое же, как атрибут имени пользователя LDAP, однако он не обязателен. Для примера, для Active directory обычно используется 'cn' как атрибут RDN, в то время как атрибут имени пользователя может быть 'sAMAccountName'.
+uuid-ldap-attribute=Атрибут UUID в LDAP
+ldap-attribute-name-for-uuid=Наименование LDAP атрибута для UUID
+uuid-ldap-attribute.tooltip=Наименование LDAP атрибута,которое используется как уникальный идентификатор объектов (UUID) в LDAP. Для множества LDAP серверов это 'entryUUID' однако некоторые могут отличаться. Для примера, для Active directory он должен быть 'objectGUID'. Если ваш LDAP сервер действительно не поддерживает понятие UUID, вы можете использовать любой другой атрибут, который должен быть уникальным среди пользователей в дереве LDAP. Например 'uid' или 'entryDN'.
+user-object-classes=Классы объектов пользователя
+ldap-user-object-classes.placeholder=Классы объектов пользователя LDAP (разделенные запятой)
+
+ldap-connection-url=URL соединения с LDAP
+ldap-users-dn=Пользователи DN LDAP
+ldap-bind-dn=Сопоставление DN LDAP
+ldap-bind-credentials=Сопоставление учетных данных LDAP
+ldap-filter=LDAP фильтр
+ldap.user-object-classes.tooltip=Все значения из LDAP objectClass атрибутов для пользователей в LDAP, разделенные запятой. Например: 'inetOrgPerson, organizationalPerson' . Вновь созданные пользователи Keycloak будут записаны в LDAP вместе с этими классами объектов, а существующие записи пользователей LDAP будут найдены только если они содержат все эти классы объектов.
+
+connection-url=URL соединения
+ldap.connection-url.tooltip=URL соединения с вашим сервером LDAP
+test-connection=Тест соединения
+users-dn=Пользователи DN
+ldap.users-dn.tooltip=Полный DN из дерева LDAP где присутствуют ваши пользователи. Этот DN является родителем пользователей LDAP. Он может быть, для примера 'ou=users,dc=example,dc=com' при условии, что ваш обычный пользователь будет иметь DN похожий на'uid=john,ou=users,dc=example,dc=com'
+authentication-type=Тип аутентификации
+ldap.authentication-type.tooltip=Тип LDAP аутентификации. Сейчас доступны только механизмы 'none' (анонимная аутентификация LDAP) или 'simple' (Аутентификация по сопоставленным логину и паролю)
+bind-dn=Сопоставление DN
+ldap.bind-dn.tooltip=DN администратора LDAP, которые будут использованы Keycloak для доступа на сервер LDAP
+bind-credential=Сопоставление учетных данных
+ldap.bind-credential.tooltip=Пароль администратора LDAP
+test-authentication=Проверка аутентификации
+custom-user-ldap-filter=Пользовательский Фильтр LDAP пользователей
+ldap.custom-user-ldap-filter.tooltip=Дополнительный фильтр LDAP для фильтрации искомых пользователей. Оставьте поле пустым, если не нуждаетесь в дополнительном фильтре. Убедитесь, что он начинается с '(' и заканчивается ')'
+search-scope=Поиск области
+ldap.search-scope.tooltip=Для одного уровня мы ищем пользователей только в DN, определенных как пользовательские DN. Для поддеревьев мы ищем полностью в их поддеревьях. Смотрите документацию LDAP для подробных деталей
+use-truststore-spi=Использование доверенных сертификатов SPI
+ldap.use-truststore-spi.tooltip=Определяет, будет ли соединение с LDAP использовать хранилище доверенных сертификатов SPI вместе с сертификатами, сконфигурированными в keycloak-server.json. 'Всегда' означает, что они будут использоваться всегда. 'Никогда' означает, что они никогда не будут использованы. 'Только для ldap`ов' означает, что они будут использованы вместе с вашими соединениями к ldap серверам. Обратите внимание, что если keycloak-server.json не сконфигурирован, то по умолчанию Java будет использовать cacerts или сертификат, определенный в 'javax.net.ssl.trustStore'.
+connection-pooling=Пул соединений
+ldap-connection-timeout=Таймаут соединения
+ldap.connection-timeout.tooltip=Таймаут соединения с LDAP в миллисекундах
+ldap-read-timeout=Таймаут чтения
+ldap.read-timeout.tooltip=Таймаут чтения из LDAP в миллисекундах. Этот таймаут применяется к операциям чтения из LDAP
+ldap.connection-pooling.tooltip=Должен ли Keycloak использовать пул соединений для доступа к LDAP серверу
+ldap.pagination.tooltip=Должен ли LDAP сервер поддерживать постраничный вывод.
+kerberos-integration=Интеграция с Kerberos
+allow-kerberos-authentication=Разрешить аутентификацию Kerberos
+ldap.allow-kerberos-authentication.tooltip=Включить/выключить аутентификацию HTTP пользователей с токенами SPNEGO/Kerberos. Данные об аутентифицированых пользователях будут предусмотрены из этого LDAP сервера
+use-kerberos-for-password-authentication=Использовать Kerberos для аутентификации по паролю
+ldap.use-kerberos-for-password-authentication.tooltip=Использовать модуль входа Kerberos для аутентификации по логин/пароль с сервера Kerberos вместо аутентификации на сервере LDAP с Directory Service API
+batch-size=Размер пачки
+ldap.batch-size.tooltip=Количество пользователей LDAP, которые будут импортированы в Keycloak за одну транзакцию.
+ldap.periodic-full-sync.tooltip=Должна ли быть включена полная периодическая синхронизация пользователей LDAP в Keycloak или нет
+ldap.periodic-changed-users-sync.tooltip=Должна ли быть включена периодическая синхронизация новых и измененных пользователей LDAP в Keycloak или нет
+ldap.changed-users-sync-period.tooltip=Период для синхронизации измененных или вновь созданных пользователей LDAP в секундах
+user-federation-mappers=Сопоставления федераций пользователей
+create-user-federation-mapper=Создать отображение федерации пользователей
+add-user-federation-mapper=Добавить отображение федерации пользователей
+provider-name=Наименование поставщика
+no-user-federation-providers-configured=Не сконфигурирован федеративный поставщик идентификации
+no-user-storage-providers-configured=Не сконфигурирован поставщик хранилища учетных записей
+add-identity-provider=Добавить поставщика идентификации
+add-identity-provider-link=Добавить ссылку на поставщика идентификации
+identity-provider=Поставщик идентификации
+identity-provider-user-id=ID пользователя поставщика идентификации
+identity-provider-user-id.tooltip=Уникальный ID пользователя на стороне поставщика идентификации
+identity-provider-username=Имя пользователя поставщика идентификации
+identity-provider-username.tooltip=Имя пользователя на стороне поставщика идентификации
+pagination=Постраничный вывод
+
+browser-flow=Сценарий браузера
+browser-flow.tooltip=Выберите сценарий, который вы хотите использовать для аутентификации через браузер.
+registration-flow=Сценарий регистрации
+registration-flow.tooltip=Выберите сценарий, который вы хотите использовать для регистрации пользователя.
+direct-grant-flow=Сценарий Direct Grant Flow
+direct-grant-flow.tooltip=Выберите сценарий, который вы хотите использоваться для аутентификации direct grant.
+reset-credentials=Сбросить учетные данные
+reset-credentials.tooltip=Выберите сценарий, который вы хотите использовать когда пользователь забыл свои учетные данные.
+client-authentication=Аутентификация клиента
+client-authentication.tooltip=Выберите сценарий, который вы хотите использовать для аутентификации клиентов.
+new=Создать
+copy=Копировать
+add-execution=Добавить исполнение
+add-flow=Добавить сценарий
+auth-type=Тип аутентификации
+requirement=Требования
+config=Конфигурация
+no-executions-available=Нет доступных выполнений
+authentication-flows=Сценарии аутентификации
+create-authenticator-config=Создать конфигурацию аутентификатора
+authenticator.alias.tooltip=Наименование конфигурации
+otp-type=Тип одноразового пароля OTP
+time-based=Основан на времени
+counter-based=Основан на счетчике
+otp-type.tooltip=totp является Временным одноразовым паролем. 'hotp' основанный на счетчике одноразовый пароль в котором сервер хранит счетчик хеша.
+otp-hash-algorithm=Алгоритм хеша OTP
+otp-hash-algorithm.tooltip=Какой алгоритм хеширования должен быть использован для генерации OTP.
+number-of-digits=Количество цифр
+otp.number-of-digits.tooltip=Сколько цифр должен иметь OTP?
+look-ahead-window=Окно вперед
+otp.look-ahead-window.tooltip=Как далеко вперед сервер должен выглядеть в случае если сгенерированный токен и сервер не синхронизированы с временем или счетчиком
+initial-counter=Начальное значение счетчика
+otp.initial-counter.tooltip=Каким должно быть начальное значение счетчика?
+otp-token-period=Период токена OTP
+otp-token-period.tooltip=Сколько секунд токен OTP должен быть действителен? По умолчанию 30 секунд.
+table-of-password-policies=Таблица политик пароля
+add-policy.placeholder=Добавить политику...
+policy-type=Тип политики
+policy-value=Значение политики
+admin-events=События администратора
+admin-events.tooltip=Отображает сохраненные события администратора в этом realm. События, связанные с учетной записью администратора, например создание realm. Чтобы включить сохранение событий, перейдите в конфигурацию.
+login-events=События входа
+filter=Фильтр
+update=Обновить
+reset=Сбросить
+operation-types=Типы операций
+resource-types=Типы ресурсов
+select-operations.placeholder=Выберите операции...
+select-resource-types.placeholder=Выберите типы ресурсов...
+resource-path=Путь к ресурсу
+resource-path.tooltip=Фильтр по пути к ресурсу. Поддерживает подстановку '*' для совпадения одной части пути и '**' совпадение нескольких частей. Например 'realms/*/clients/asbc' выберет клиента с идентификатором asbc в любом realm, в то время как 'realms/master/**' не найдет ничего в master realm.
+date-(from)=Дата (С)
+date-(to)=Дата (По)
+authentication-details=Детали аутентификации
+ip-address=IP адрес
+time=Время
+operation-type=Тип операции
+resource-type=Тип ресурса
+auth=Аутентификация
+representation=Представление
+register=Регистрация
+required-action=Требуемое действие
+default-action=Действие по умолчанию
+auth.default-action.tooltip=Если включено, то любому новому пользователю будет будет назначено требуемое действие.
+no-required-actions-configured=Требуемые действия не сконфигурированы
+defaults-to-id=По умолчанию id
+flows=Сценарии
+bindings=Сопоставления
+required-actions=Требуемые действия
+password-policy=Политики пароля
+otp-policy=Политики OTP
+user-groups=Группы пользователей
+default-groups=Группы по умолчанию
+groups.default-groups.tooltip=Устанавливает группы, в которые новые пользователи будут включены автоматически.
+cut=Вырезать
+paste=Вставить
+
+create-group=Создать группу
+create-authenticator-execution=Создать исполнение аутентификатора
+create-form-action-execution=Создать форму действия исполнения
+create-top-level-form=Создать верхнеуровневую форму
+flow.alias.tooltip=Задает отображаемое имя для сценария.
top-level-flow-type=Top Level Flow Type
-flow.generic=\u043E\u0431\u0449\u0438\u0439
-flow.client=\u043A\u043B\u0438\u0435\u043D\u0442
-top-level-flow-type.tooltip=\u041A\u0430\u043A\u043E\u0439 \u044D\u0442\u043E \u0442\u0438\u043F \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \u0432\u0435\u0440\u0445\u043D\u0435\u0433\u043E \u0443\u0440\u043E\u0432\u043D\u044F? \u0422\u0438\u043F "\u043A\u043B\u0438\u0435\u043D\u0442" \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432 (\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439), \u043A\u043E\u0433\u0434\u0430 "\u043E\u0431\u0449\u0438\u0439" \u0434\u043B\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0438 \u0432\u0441\u0435\u0433\u043E \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0433\u043E
-create-execution-flow=\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F
-flow-type=\u0422\u0438\u043F \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F
-flow.form.type=\u0444\u043E\u0440\u043C\u0430
-flow-type.tooltip=\u041A\u0430\u043A\u043E\u0433\u043E \u0442\u0438\u043F\u0430 \u044D\u0442\u0430 \u0444\u043E\u0440\u043C\u0430
-form-provider=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0444\u043E\u0440\u043C\u044B
-default-groups.tooltip=\u0412\u043D\u043E\u0432\u044C \u0441\u043E\u0437\u0434\u0430\u043D\u043D\u044B\u0435 \u0438\u043B\u0438 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u0431\u0443\u0434\u0443\u0442 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u043A \u044D\u0442\u0438\u043C \u0433\u0440\u0443\u043F\u043F\u0430\u043C
-select-a-type.placeholder=\u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043F
-available-groups=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0433\u0440\u0443\u043F\u043F\u044B
-available-groups.tooltip=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u0440\u0443\u043F\u043F\u0443, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0432\u044B \u0445\u043E\u0442\u0438\u0442\u0435 \u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E.
-value=\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435
-table-of-group-members=\u0422\u0430\u0431\u043B\u0438\u0446\u0430 \u0447\u043B\u0435\u043D\u043E\u0432 \u0433\u0440\u0443\u043F\u043F\u044B
-last-name=\u0424\u0430\u043C\u0438\u043B\u0438\u044F
-first-name=\u0418\u043C\u044F
-email=Email
-toggle-navigation=\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044E
-manage-account=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E
-sign-out=\u0412\u044B\u0445\u043E\u0434
-server-info=\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E \u0441\u0435\u0440\u0432\u0435\u0440\u0435
-resource-not-found=\u0420\u0435\u0441\u0443\u0440\u0441 <strong>\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D</strong>...
-resource-not-found.instruction=\u041C\u044B \u043D\u0435 \u0441\u043C\u043E\u0433\u043B\u0438 \u043D\u0430\u0439\u0442\u0438 \u0440\u0435\u0441\u0443\u0440\u0441, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u044B \u0438\u0449\u0435\u0442\u0435. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C, \u0447\u0442\u043E \u0432\u044B \u0432\u0432\u0435\u043B\u0438 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 URL.
-go-to-the-home-page=\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043D\u0430 \u0434\u043E\u043C\u0430\u0448\u043D\u044E\u044E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 »
-page-not-found=\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430 <strong>\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430</strong>...
-page-not-found.instruction=\u041C\u044B \u043D\u0435 \u0441\u043C\u043E\u0433\u043B\u0438 \u043D\u0430\u0439\u0442\u0438 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0432\u044B \u0438\u0449\u0435\u0442\u0435. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044C, \u0447\u0442\u043E URL-\u0430\u0434\u0440\u0435\u0441 \u0432\u0432\u0435\u0434\u0435\u043D \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E.
-events.tooltip=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u044B\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0434\u043B\u044F realm. \u0421\u043E\u0431\u044B\u0442\u0438\u044F, \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0441 \u0443\u0447\u0435\u0442\u043D\u044B\u043C\u0438 \u0437\u0430\u043F\u0438\u0441\u044F\u043C\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, \u0432\u0445\u043E\u0434 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F. \u0414\u043B\u044F \u0442\u043E\u0433\u043E, \u0447\u0442\u043E\u0431\u044B \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u0439, \u043F\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E.
-select-event-types.placeholder=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043F \u0441\u043E\u0431\u044B\u0442\u0438\u0439...
-events-config.tooltip=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u043E\u043F\u0446\u0438\u0438 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0434\u043B\u044F \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u044F \u0441\u043E\u0431\u044B\u0442\u0438\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 \u0438 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430.
-select-an-action.placeholder=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435...
-event-listeners.tooltip=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u0441\u043B\u0443\u0448\u0430\u0442\u0435\u043B\u0435\u0439, \u043F\u043E\u043B\u0443\u0447\u0430\u044E\u0449\u0438\u0445 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0434\u043B\u044F realm.
-login.save-events.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0442\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0431\u0443\u0434\u0443\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445, \u0447\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u0435\u0442 \u0438\u0445 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u043C\u0438 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0443 \u0438 \u043A\u043E\u043D\u0441\u043E\u043B\u0438 \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E.
-clear-events.tooltip=\u0423\u0434\u0430\u043B\u044F\u0435\u0442 \u0432\u0441\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0438\u0437 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445.
-events.expiration.tooltip=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0440\u043E\u043A \u0438\u0441\u0442\u0435\u0447\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u0441\u043E\u0431\u044B\u0442\u0438\u0439. \u0418\u0441\u0442\u0435\u043A\u0448\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u043F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0438 \u0443\u0434\u0430\u043B\u044F\u044E\u0442\u0441\u044F \u0438\u0437 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445.
-admin-events-settings=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0441\u043E\u0431\u044B\u0442\u0438\u0439 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430
-save-events=\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0442\u044C \u0441\u043E\u0431\u044B\u0442\u0438\u044F
-admin.save-events.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0442\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445, \u0447\u0442\u043E \u0441\u0434\u0435\u043B\u0430\u0435\u0442 \u0438\u0445 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u043C\u0438 \u0447\u0435\u0440\u0435\u0437 \u043A\u043E\u043D\u0441\u043E\u043B\u044C \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430.
-saved-types.tooltip=\u0421\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C, \u043A\u0430\u043A\u0438\u0435 \u0442\u0438\u043F\u044B \u0441\u043E\u0431\u044B\u0442\u0438\u0439 \u0441\u043B\u0435\u0434\u0443\u0435\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0442\u044C.
-include-representation=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435
-include-representation.tooltip=\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C JSON \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0434\u043B\u044F \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 \u043D\u0430 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435.
-clear-admin-events.tooltip=\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u0441\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u0438\u0437 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445.
-server-version=\u0412\u0435\u0440\u0441\u0438\u044F \u0441\u0435\u0440\u0432\u0435\u0440\u0430
-info=\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F
-providers=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0438
-server-time=\u0412\u0440\u0435\u043C\u044F \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435
-server-uptime=\u0410\u043F\u0442\u0430\u0439\u043C \u0441\u0435\u0440\u0432\u0435\u0440\u0430
-memory=\u041F\u0430\u043C\u044F\u0442\u044C
-total-memory=\u0412\u0441\u0435\u0433\u043E \u043F\u0430\u043C\u044F\u0442\u0438
-free-memory=\u0421\u0432\u043E\u0431\u043E\u0434\u043D\u043E \u043F\u0430\u043C\u044F\u0442\u0438
-used-memory=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u043E \u043F\u0430\u043C\u044F\u0442\u0438
-system=\u0421\u0438\u0441\u0442\u0435\u043C\u0430
-current-working-directory=\u0422\u0435\u043A\u0443\u0449\u0430\u044F \u0440\u0430\u0431\u043E\u0447\u0430\u044F \u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0438\u044F
-java-version=\u0412\u0435\u0440\u0441\u0438\u044F Java
-java-vendor=\u041F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A Java
+flow.generic=общий
+flow.client=клиент
+top-level-flow-type.tooltip=Какой это тип сценария верхнего уровня? Тип "клиент" используется для аутентификации клиентов (приложений), когда "общий" для пользователей и всего остального
+create-execution-flow=Создать сценарий исполнения
+flow-type=Тип сценария
+flow.form.type=форма
+flow.generic.type=общий
+flow-type.tooltip=Какого типа эта форма
+form-provider=Поставщик формы
+default-groups.tooltip=Вновь созданные или зарегистрированные пользователи будут автоматически добавлены к этим группам
+select-a-type.placeholder=выберите тип
+available-groups=Доступные группы
+available-groups.tooltip=Выберите группу, которые вы хотите добавить по умолчанию.
+value=Значение
+table-of-group-members=Таблица членов группы
+last-name=Фамилия
+first-name=Имя
+email=E-mail
+toggle-navigation=Переключить навигацию
+manage-account=Управление учетной записью
+sign-out=Выход
+server-info=Информация о сервере
+resource-not-found=Ресурс <strong>не найден</strong>...
+resource-not-found.instruction=Мы не смогли найти ресурс, который вы ищете. Пожалуйста, убедитесь, что вы ввели корректный URL.
+go-to-the-home-page=Перейти на домашнюю страницу »
+page-not-found=Страница <strong>не найдена</strong>...
+page-not-found.instruction=Мы не смогли найти страницу, которую вы ищете. Пожалуйста, убедитесь, что URL-адрес введен правильно.
+events.tooltip=Отображает сохраненные события для realm. События, связанные с учетными записями пользователей, например, вход пользователя. Для того, чтобы включить сохранение событий, перейдите в конфигурацию.
+select-event-types.placeholder=Выберите тип событий...
+events-config.tooltip=Отображает опции конфигурации для включения сохранения событий пользователей и администратора.
+select-an-action.placeholder=Выберите действие...
+event-listeners.tooltip=Настройка слушателей, получающих события для realm.
+login.save-events.tooltip=Если включено, то события будут сохранены в базу данных, что сделает их доступными администратору и консоли управления учетной записью.
+clear-events.tooltip=Удаляет все события из базы данных.
+events.expiration.tooltip=Установить срок истечения для событий. Истекшие события периодически удаляются из базы данных.
+admin-events-settings=Настройки событий администратора
+save-events=Сохранять события
+admin.save-events.tooltip=Если включено, то события администратора будет сохранены в базу данных, что сделает их доступными через консоль администратора.
+saved-types.tooltip=Сконфигурировать, какие типы событий следует сохранять.
+include-representation=Включить представление
+include-representation.tooltip=Включить JSON представление для запросов на создание и обновление.
+clear-admin-events.tooltip=Удалить все события администратора из базы данных.
+server-version=Версия сервера
+server-profile=Профиль сервера
+server-disabled=Отключенные функции сервера
+info=Информация
+providers=Поставщики
+server-time=Время на сервере
+server-uptime=Аптайм сервера
+memory=Память
+total-memory=Всего памяти
+free-memory=Свободно памяти
+used-memory=Использовано памяти
+system=Система
+current-working-directory=Текущая рабочая директория
+java-version=Версия Java
+java-vendor=Поставщик Java
java-runtime=Java Runtime
java-vm=Java VM
-java-vm-version=\u0412\u0435\u0440\u0441\u0438\u044F Java VM
+java-vm-version=Версия Java VM
java-home=Java Home
-user-name=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-user-timezone=\u0422\u0430\u0439\u043C\u0437\u043E\u043D\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-user-locale=\u042F\u0437\u044B\u043A \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-system-encoding=\u0421\u0438\u0441\u0442\u0435\u043C\u043D\u0430\u044F \u043A\u043E\u0434\u0438\u0440\u043E\u0432\u043A\u0430
-operating-system=\u041E\u043F\u0435\u0440\u0430\u0446\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0438\u0441\u0442\u0435\u043C\u0430
-os-architecture=\u0410\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430 OS
+user-name=Имя пользователя
+user-timezone=Таймзона пользователя
+user-locale=Язык пользователя
+system-encoding=Системная кодировка
+operating-system=Операционная система
+os-architecture=Архитектура OS
spi=SPI
-granted-roles=\u041F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0435 \u0440\u043E\u043B\u0438
-granted-protocol-mappers=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B\u0430 \u043F\u0440\u0435\u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u044F
-additional-grants=\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u043E\u043B\u043D\u043E\u043C\u043E\u0447\u0438\u044F
-revoke=\u041E\u0442\u043E\u0431\u0440\u0430\u0442\u044C
-new-password=\u041D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C
-password-confirmation=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F
-reset-password=\u0421\u0431\u0440\u043E\u0441 \u043F\u0430\u0440\u043E\u043B\u044F
-credentials.temporary.tooltip=\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0441\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C \u043F\u0440\u0438 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C \u0432\u0445\u043E\u0434\u0435
-remove-totp=\u0423\u0434\u0430\u043B\u0438\u0442\u044C TOTP
-credentials.remove-totp.tooltip=\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0433\u0435\u043D\u0435\u0440\u0430\u0442\u043E\u0440 \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0445 \u043F\u0430\u0440\u043E\u043B\u0435\u0439 \u0438\u0437 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-reset-actions=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0441\u0431\u0440\u043E\u0441\u0430
-credentials.reset-actions.tooltip=\u041D\u0430\u0431\u043E\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0434\u043B\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u043F\u0440\u0438 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u043F\u0438\u0441\u044C\u043C\u0430 \u0441 \u0443\u043A\u0430\u0437\u0430\u043D\u0438\u044F\u043C\u0438 \u043F\u043E \u0441\u0431\u0440\u043E\u0441\u0443 \u043F\u0430\u0440\u043E\u043B\u044F. '\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C email' \u0432\u044B\u0441\u044B\u043B\u0430\u0435\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u043F\u0438\u0441\u044C\u043C\u043E \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0435\u0433\u043E email. '\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C' \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0432\u0432\u0435\u0441\u0442\u0438 \u043D\u043E\u0432\u0443\u044E \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u0443\u044E \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E. '\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C' \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0432\u0432\u0435\u0441\u0442\u0438 \u043D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C. '\u041D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C TOTP' \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0441 \u0433\u0435\u043D\u0435\u0440\u0430\u0442\u043E\u0440\u043E\u043C \u043F\u0430\u0440\u043E\u043B\u0435\u0439.
-reset-actions-email=Email \u0441 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F\u043C\u0438 \u0434\u043B\u044F \u0441\u0431\u0440\u043E\u0441\u0430 \u043F\u0430\u0440\u043E\u043B\u044F
-send-email=\u041F\u043E\u0441\u043B\u0430\u0442\u044C \u043F\u0438\u0441\u044C\u043C\u043E
-credentials.reset-actions-email.tooltip=\u041F\u043E\u0441\u044B\u043B\u0430\u0435\u0442 \u043F\u0438\u0441\u044C\u043C\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u0441\u043E \u0432\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u043E\u0439 \u0441\u0441\u044B\u043B\u043A\u043E\u0439. \u041A\u043B\u0438\u043A\u043D\u0443\u0432 \u043D\u0430 \u0441\u0441\u044B\u043B\u043A\u0443, \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u043E \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0434\u043B\u044F \u0441\u0431\u0440\u043E\u0441\u0430. \u041E\u043D\u0438 \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u044B \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u0432\u0445\u043E\u0434\u0438\u0442\u044C \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443. \u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0430\u0440\u043E\u043B\u044F, \u0449\u0435\u043B\u043A\u043D\u0438\u0442\u0435 \u043F\u043E \u044D\u0442\u043E\u0439 \u043A\u043D\u043E\u043F\u043A\u0435, \u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442 \u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u0441\u043C\u0435\u043D\u0438\u0442\u044C \u0441\u0432\u043E\u0439 \u043F\u0430\u0440\u043E\u043B\u044C \u0431\u0435\u0437 \u0432\u0445\u043E\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443.
-add-user=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-created-at=\u0421\u043E\u0437\u0434\u0430\u043D
-user-enabled=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-user-enabled.tooltip=\u041E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u043D\u044B\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u043D\u0435 \u0441\u043C\u043E\u0433\u0443\u0442 \u0432\u043E\u0439\u0442\u0438.
-user-temporarily-locked=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D
-user-temporarily-locked.tooltip=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D \u0432 \u0441\u043B\u0443\u0447\u0430\u0435 \u043C\u043D\u043E\u0433\u043E\u043A\u0440\u0430\u0442\u043D\u044B\u0445 \u043D\u0435\u0443\u0434\u0430\u0447\u043D\u044B\u0445 \u043F\u043E\u043F\u044B\u0442\u043E\u043A \u0432\u0445\u043E\u0434\u0430.
-unlock-user=\u0420\u0430\u0437\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-federation-link=\u0421\u0441\u044B\u043B\u043A\u0430 \u0444\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438
-email-verified=Email \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D
-email-verified.tooltip=\u0414\u043E\u043B\u0436\u0435\u043D \u043B\u0438 email \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0431\u044B\u0442\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D?
-required-user-actions=\u0422\u0440\u0435\u0431\u0443\u0435\u043C\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-required-user-actions.tooltip=\u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043A\u043E\u0433\u0434\u0430 \u043E\u043D \u0432\u0445\u043E\u0434\u0438\u0442. '\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C email' \u0432\u044B\u0441\u044B\u043B\u0430\u0435\u0442 \u043F\u0438\u0441\u044C\u043C\u043E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0435\u0433\u043E email. '\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C' \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0432\u0432\u0435\u0441\u0442\u0438 \u043D\u043E\u0432\u0443\u044E \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u0443\u044E \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E. '\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C' \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0432\u0432\u0435\u0441\u0442\u0438 \u043D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C. '\u041D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C TOTP' \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043F\u0430\u0440\u043E\u043B\u0435\u0439.
-locale=\u042F\u0437\u044B\u043A
-select-one.placeholder=\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435...
-impersonate=\u0418\u043C\u043F\u0435\u0440\u0441\u043E\u043D\u0438\u0440\u043E\u0432\u0430\u0442\u044C
-impersonate-user=\u0418\u043C\u043F\u0435\u0440\u0441\u043E\u043D\u0438\u0440\u043E\u0432\u0430\u0442\u044C
-impersonate-user.tooltip=\u0412\u043E\u0439\u0442\u0438 \u043A\u0430\u043A \u044D\u0442\u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C. \u0415\u0441\u043B\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0432 \u0442\u043E\u043C \u0436\u0435 \u0441\u0430\u043C\u043E\u043C realm \u0447\u0442\u043E \u0438 \u0432\u044B, \u0442\u043E \u0432\u0430\u0448\u0430 \u0442\u0435\u043A\u0443\u0449\u0430\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043B\u043E\u0433\u0438\u043D\u0435\u043D\u0430 \u043F\u0435\u0440\u0435\u0434 \u0442\u0435\u043C \u043A\u0430\u043A \u0432\u044B \u0432\u043E\u0439\u0434\u0435\u0442\u0435 \u043A\u0430\u043A \u044D\u0442\u043E\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C.
-identity-provider-alias=\u0421\u0438\u043D\u043E\u043D\u0438\u043C \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-provider-user-id=ID \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0443 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430
-provider-username=Username \u0443 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430
-no-identity-provider-links-available=\u0421\u0441\u044B\u043B\u043A\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043D\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B
-group-membership=\u0427\u043B\u0435\u043D\u0441\u0442\u0432\u043E \u0432 \u0433\u0440\u0443\u043F\u043F\u0430\u0445
-leave=\u041F\u043E\u043A\u0438\u043D\u0443\u0442\u044C
-group-membership.tooltip=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0447\u043B\u0435\u043D\u043E\u043C \u0433\u0440\u0443\u043F\u043F\u044B. \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0432 \u0441\u043F\u0438\u0441\u043A\u0435 \u0433\u0440\u0443\u043F\u043F\u0443 \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 \u041F\u043E\u043A\u0438\u043D\u0443\u0442\u044C, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u043A\u0438\u043D\u0443\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u0443.
-membership.available-groups.tooltip=\u0413\u0440\u0443\u043F\u043F\u044B, \u043A \u043A\u043E\u0442\u043E\u0440\u044B\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u043C\u043E\u0436\u0435\u0442 \u043F\u0440\u0438\u0441\u043E\u0435\u0434\u0438\u043D\u0438\u0442\u044C\u0441\u044F. \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u0440\u0443\u043F\u043F\u0443 \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 \u043F\u0440\u0438\u0441\u043E\u0435\u0434\u0438\u043D\u0438\u0442\u044C\u0441\u044F.
-table-of-realm-users=\u0422\u0430\u0431\u043B\u0438\u0446\u0430 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 Realm
-view-all-users=\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432\u0441\u0435\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-unlock-users=\u0420\u0430\u0437\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-no-users-available=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 \u043D\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B
-users.instruction=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0437\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u0441\u0442\u0440\u043E\u043A\u0443 \u043F\u043E\u0438\u0441\u043A\u0430, \u0438\u043B\u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0432\u0441\u0435\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-consents=\u0421\u043E\u0433\u043B\u0430\u0441\u0438\u044F
-started=\u041D\u0430\u0447\u0430\u0442\u043E
-logout-all-sessions=\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u0432\u0441\u0435\u0445 \u0441\u0435\u0441\u0441\u0438\u0439
-logout=\u0412\u044B\u0445\u043E\u0434
-new-name=\u041D\u043E\u0432\u043E\u0435 \u0438\u043C\u044F
-ok=\u041E\u043A
-attributes=\u0410\u0442\u0440\u0438\u0431\u0443\u0442\u044B
-role-mappings=\u0421\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0440\u043E\u043B\u0435\u0439
-members=\u0427\u043B\u0435\u043D\u044B
-details=\u0414\u0435\u0442\u0430\u043B\u0438
-identity-provider-links=\u0421\u0441\u044B\u043B\u043A\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438
-register-required-action=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0442\u0440\u0435\u0431\u0443\u0435\u043C\u043E\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435
-gender=\u041F\u043E\u043B
-address=\u0410\u0434\u0440\u0435\u0441
-phone=\u0422\u0435\u043B\u0435\u0444\u043E\u043D
-profile-url=URL \u043F\u0440\u043E\u0444\u0438\u043B\u044F
-picture-url=URL \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F
-website=\u0412\u0435\u0431\u0441\u0430\u0439\u0442
-import-keys-and-cert=\u0418\u043C\u043F\u043E\u0440\u0442 \u043A\u043B\u044E\u0447\u0435\u0439 \u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0432
-import-keys-and-cert.tooltip=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0430\u0440\u0443 \u043A\u043B\u044E\u0447\u0435\u0439 \u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442 \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-upload-keys=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043A\u043B\u044E\u0447\u0438
-download-keys-and-cert=\u0421\u043A\u0430\u0447\u0430\u0442\u044C \u043A\u043B\u044E\u0447\u0438 \u0438 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442
-no-value-assigned.placeholder=\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043E
-remove=\u0423\u0434\u0430\u043B\u0438\u0442\u044C
-no-group-members=\u0412 \u0433\u0440\u0443\u043F\u043F\u0435 \u043D\u0435\u0442 \u0447\u043B\u0435\u043D\u043E\u0432
-temporary=\u0412\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0439
-join=\u041F\u0440\u0438\u0441\u043E\u0435\u0434\u0438\u043D\u0438\u0442\u044C\u0441\u044F
-event-type=\u0422\u0438\u043F \u0441\u043E\u0431\u044B\u0442\u0438\u044F
-events-config=\u041A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044F \u0441\u043E\u0431\u044B\u0442\u0438\u0439
-event-listeners=\u0421\u043B\u0443\u0448\u0430\u0442\u0435\u043B\u0438 \u0441\u043E\u0431\u044B\u0442\u0438\u0439
-login-events-settings=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0441\u043E\u0431\u044B\u0442\u0438\u0439 \u043F\u043E \u0432\u0445\u043E\u0434\u0443
-clear-events=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0441\u043E\u0431\u044B\u0442\u0438\u044F
-saved-types=\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u043C\u044B\u0435 \u0442\u0438\u043F\u044B \u0441\u043E\u0431\u044B\u0442\u0438\u0439
-clear-admin-events=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430
-clear-changes=\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F
-error=\u041E\u0448\u0438\u0431\u043A\u0430
+granted-roles=Предоставленные роли
+granted-protocol-mappers=Сопоставления протокола предоставления
+additional-grants=Дополнительные полномочия
+consent-created-date=Создано
+consent-last-updated-date=Обновлено
+revoke=Отобрать
+new-password=Новый пароль
+password-confirmation=Подтверждение пароля
+reset-password=Сброс пароля
+credentials.temporary.tooltip=Если включено, пользователю необходимо сменить пароль при следующем входе
+remove-totp=Удалить TOTP
+credentials.remove-totp.tooltip=Удалить генератор одноразовых паролей из пользователя.
+reset-actions=Действия сброса
+credentials.reset-actions.tooltip=Набор действия для выполнения при отправке пользователю письма с указаниями по сбросу пароля. 'Подтвердить E-mail' высылает пользователю письмо для подтверждения его E-mail. 'Обновить профиль' требует от пользователя ввести новую персональную информацию. 'Обновить пароль' требует от пользователя ввести новый пароль. 'Настроить TOTP' требует установить мобильное приложение с генератором паролей.
+reset-actions-email=E-mail с действиями для сброса пароля
+send-email=Послать письмо
+credentials.reset-actions-email.tooltip=Посылает письмо пользователю со встроенной ссылкой. Кликнув на ссылку, пользователю будет разрешено выполнить действия для сброса. Они не должны для этого входить в систему. Например, установка действия обновления пароля, щелкните по этой кнопке, и пользователь получит возможность сменить свой пароль без входа в систему.
+add-user=Добавить пользователя
+created-at=Создан
+user-enabled=Пользователь включен
+user-enabled.tooltip=Отключенные пользователи не смогут войти.
+user-temporarily-locked=Пользователь временно заблокирован
+user-temporarily-locked.tooltip=Пользователь может быть заблокирован в случае многократных неудачных попыток входа.
+unlock-user=Разблокировать пользователя
+federation-link=Ссылка федерации
+email-verified=Подтверждение E-mail
+email-verified.tooltip=Должен ли пользователь подтверждать свой E-mail?
+required-user-actions=Требуемые действия от пользователя
+required-user-actions.tooltip=Требует действий от пользователя когда он входит. 'Подтвердить E-mail' высылает письмо пользователю для подтверждения его E-mail. 'Обновить профиль' требует от пользователя ввести новую персональную информацию. 'Обновить пароль' требует от пользователя ввести новый пароль. 'Настроить TOTP' требует установить мобильное приложение генерации паролей.
+locale=Язык
+select-one.placeholder=Выберите...
+impersonate=Имперсонировать
+impersonate-user=Имперсонировать
+impersonate-user.tooltip=Войти как этот пользователь. Если пользователь в том же самом realm что и вы, то ваша текущая сессия будет разлогинена перед тем как вы войдете как этот пользователь.
+identity-provider-alias=Синоним поставщика идентификации
+provider-user-id=ID пользователя у поставщика
+provider-username=Username у поставщика
+no-identity-provider-links-available=Ссылки поставщика идентификации не доступны
+group-membership=Членство в группах
+leave=Покинуть
+group-membership.tooltip=Пользователь является членом группы. Выберите в списке группу и нажмите кнопку Покинуть, чтобы покинуть группу.
+membership.available-groups.tooltip=Группы, к которым пользователь может присоединиться. Выберите группу и нажмите кнопку присоединиться.
+table-of-realm-users=Таблица пользователей Realm
+view-all-users=Показать всех пользователей
+unlock-users=Разблокировать пользователей
+no-users-available=Пользователи не доступны
+users.instruction=Пожалуйста, заполните строку поиска, или нажмите посмотреть всех пользователей
+consents=Согласия
+started=Начато
+logout-all-sessions=Выйти из всех сессий
+logout=Выход
+new-name=Новое имя
+ok=Ок
+attributes=Атрибуты
+role-mappings=Сопоставление ролей
+members=Члены
+details=Детали
+identity-provider-links=Ссылки поставщика идентификации
+register-required-action=Зарегистрировать требуемое действие
+gender=Пол
+address=Адрес
+phone=Телефон
+profile-url=URL профиля
+picture-url=URL изображения
+website=Вебсайт
+import-keys-and-cert=Импорт ключей и сертификатов
+import-keys-and-cert.tooltip=Загрузить пару ключей и сертификат клиента.
+upload-keys=Загрузить ключи
+download-keys-and-cert=Скачать ключи и сертификат
+no-value-assigned.placeholder=Значение не назначено
+remove=Удалить
+no-group-members=В группе нет членов
+temporary=Временный
+join=Присоединиться
+event-type=Тип события
+events-config=Конфигурация событий
+event-listeners=Слушатели событий
+login-events-settings=Настройки событий по входу
+clear-events=Очистить события
+saved-types=Сохраняемые типы событий
+clear-admin-events=Очистить события администратора
+clear-changes=Очистить изменения
+error=Ошибка
+
+# Authz
+# Authz Common
+authz-authorization=Авторизация
+authz-owner=Владелец
+authz-uri=URI
+authz-scopes=Области
+authz-resource=Ресурс
+authz-resource-type=Тип ресурса
+authz-resources=Ресурсы
+authz-scope=Область
+authz-authz-scopes=Области авторизации
+authz-policies=Политики
+authz-permissions=Разрешения
+authz-evaluate=Оценка
+authz-icon-uri=Иконка URI
+authz-icon-uri.tooltip=URI, указывающий на иконку.
+authz-select-scope=Выберите область
+authz-select-resource=Выберите ресурс
+authz-associated-policies=Назначенные политики
+authz-any-resource=Любой ресурс
+authz-any-scope=Любая область
+authz-any-role=Любая роль
+authz-policy-evaluation=Оценки политики
+authz-select-client=Выберите клиента
+authz-select-user=Выберите пользователя
+authz-entitlements=Права
+authz-no-resources=Нет ресурсов
+authz-result=Результат
+authz-authorization-services-enabled=Авторизация включена
+authz-authorization-services-enabled.tooltip=Включить/Выключить тонко-настраиваемую поддержку авторизации для клиента
+authz-required=Требуется
+authz-show-details=Показать детали
+authz-hide-details=Скрыть детали
+authz-associated-permissions=Назначенные разрешения
+authz-no-permission-associated=Разрешения не назначены
+
+# Authz Settings
+authz-import-config.tooltip=Импорт JSON файла, содержащего авторизационные настройки для этого сервера ресурсов.
+
+authz-policy-enforcement-mode=Режим применения политик
+authz-policy-enforcement-mode.tooltip=Режим применения политик диктует, каким образом политики применяются при оценке запросов на авторизацию. «Обязывающая» означает, что запросы запрещены по умолчанию, даже если нет никакой политики, связанной с данным ресурсом. "Разрешающая" означает, что запросы разрешены даже если не существует политика, связанная с данным ресурсом. 'Отключено' полностью отключает оценку политики и позволяет получить доступ к любому ресурсу.
+authz-policy-enforcement-mode-enforcing=Обязывающая
+authz-policy-enforcement-mode-permissive=Разрешающая
+authz-policy-enforcement-mode-disabled=Отключено
+
+authz-remote-resource-management=Удаленное управление ресурсами
+authz-remote-resource-management.tooltip=Должны ли ресурсы управляться удаленно сервером ресурсов? Если нет, то ресурсы могут управляться только через консоль администратора.
+
+authz-export-settings=Экспортировать настройки
+authz-export-settings.tooltip=Экспортировать и скачать все авторизационные настройки для этого сервера ресурсов.
+
+# Authz Resource List
+authz-no-resources-available=Нет доступных ресурсов.
+authz-no-scopes-assigned=Нет назначенных областей.
+authz-no-type-defined=Нет заданных типов.
+authz-no-uri-defined=Нет заданных URI.
+authz-no-permission-assigned=Полномочия не назначены.
+authz-no-policy-assigned=Политики не заданы.
+authz-create-permission=Создать полномочия
+
+# Authz Resource Detail
+authz-add-resource=Добавить ресурс
+authz-resource-name.tooltip=Уникальное имя для этого ресурса. Имя может быть использовано для уникальной идентификации ресурса, используется при запросах конкретных ресурсов.
+authz-resource-owner.tooltip=Владелец этого ресурса.
+authz-resource-type.tooltip=Тип этого ресурса. Может быть использовано для группировки различных экземпляров ресурса с тем же типом.
+authz-resource-uri.tooltip=URI, который также может быть использован для уникальной идентификации этого ресурса.
+authz-resource-scopes.tooltip=Области, ассоциироваанные с этим ресурсом.
+
+# Authz Scope List
+authz-add-scope=Добавить область
+authz-no-scopes-available=Нет доступных областей.
+
+# Authz Scope Detail
+authz-scope-name.tooltip=Уникальное имя для области. Имя может быть использовано для уникальной идентификации области, используется при запросах конкретных областей.
+
+# Authz Policy List
+authz-all-types=Все типы
+authz-create-policy=Создать политику
+authz-no-policies-available=Нет доступных политик.
+
+# Authz Policy Detail
+authz-policy-name.tooltip=Название этой политики.
+authz-policy-description.tooltip=Описание этой политики.
+authz-policy-logic=Логика
+authz-policy-logic-positive=Позитивная
+authz-policy-logic-negative=Негитивная
+authz-policy-logic.tooltip=Логика диктует, как политика должна применяться. Если 'Позитивная', результирующий эффект (разрешение или запрещение) полученный в ходе оценки этой политики будет использован для выполнения решения. Если 'Негативная', результирующий эффект будет отрицательным, другими словами, разрешение становится запрещением и наоборот.
+authz-policy-apply-policy=Применить политику
+authz-policy-apply-policy.tooltip=Определяем все политики, которые должны быть применены к областям, определенным этой политикой или разрешением.
+authz-policy-decision-strategy=Стратегия решения
+authz-policy-decision-strategy.tooltip=Стратегия решения диктует как политики связаны с заданными разрешениями и как формируется окончательное решение. 'Утвердительная' означает, что, по крайней мере, одна политика должна дать положительную оценку для того, чтобы окончательное решение также было положительным. 'Единогласная' означает что все политики должны дать положительную оценку для того, чтобы окончательная оценка также была положительной. 'Консенсусная' означает, что количество положительных решений должно превышать количество отрицательных решений. Если количество положительных и отрицательных решений совпадает, окончательное решение будет отрицательным.
+authz-policy-decision-strategy-affirmative=Утвердительная
+authz-policy-decision-strategy-unanimous=Единогласная
+authz-policy-decision-strategy-consensus=Консенсусная
+authz-select-a-policy=Выберите политику
+
+# Authz Role Policy Detail
+authz-add-role-policy=Добавить роль политики
+authz-no-roles-assigned=Нет назначенных ролей.
+authz-policy-role-realm-roles.tooltip=Задайте роли *realm*, допущенные этой политикой.
+authz-policy-role-clients.tooltip=Выберите клиента в порядке, необходимом для фильтра клиентских ролей, которые могут быть применены к этой политике.
+authz-policy-role-client-roles.tooltip=Задайте роли клиента, допущенные этой политикой.
+
+# Authz User Policy Detail
+authz-add-user-policy=Добавить политику пользователей
+authz-no-users-assigned=Нет назначенных пользователей.
+authz-policy-user-users.tooltip=Задайте, какие пользователи допущены этой политикой.
+
+# Authz Client Policy Detail
+authz-add-client-policy=Добавить политику клиента
+authz-no-clients-assigned=Нет назначенных клиентов.
+authz-policy-client-clients.tooltip=Задайте, какие клиенты допущеный этой политикой.
+
+# Authz Time Policy Detail
+authz-add-time-policy=Добавить политики времени
+authz-policy-time-not-before.tooltip=Определете время, до наступления которого политика НЕ ДОЛЖНА быть разрешена. Разрешено только если текущее время/дата больше или равны заданному значению.
+authz-policy-time-not-on-after=Не после
+authz-policy-time-not-on-after.tooltip=Определяет время, после которого политика НЕ ДОЛЖНА быть разрешена. Разрешено только если текущее время/дата менеьше или равны заданному значению.
+authz-policy-time-day-month=День месяца
+authz-policy-time-day-month.tooltip=Определяет день месяца, в который политика ДОЛЖНА быть разрешена. Вы также можете определить диапазон, заполнив второе поле. В этом случае разрешение выдается только если текущий день месяца равен или находится между заданными значениями.
+authz-policy-time-month=Месяц
+authz-policy-time-month.tooltip=Определяет месяц, в который политика ДОЛЖНА быть разрешена. Вы также можете определить диапазон, заполнив второе поле. В этом случае разрешение выдается только если текущий месяц равен или находится между заданными значениями.
+authz-policy-time-year=Год
+authz-policy-time-year.tooltip=Определяет год, в который политика ДОЛЖНА быть разрешена. Вы также можете определить диапазон, заполнив второе поле. В этом случае разрешение выдается только если текущий год равен или находится между заданными значениями.
+authz-policy-time-hour=Час
+authz-policy-time-hour.tooltip=Определяет час, в который политика ДОЛЖНА быть разрешена. Вы также можете определить диапазон, заполнив второе поле. В этом случае разрешение выдается только если текущий час равен или находится между заданными значениями.
+authz-policy-time-minute=Минута
+authz-policy-time-minute.tooltip=Определяет минуту, в которую политика ДОЛЖНА быть разрешена. Вы также можете определить диапазон, заполнив второе поле. В этом случае разрешение выдается только если текущая минута равна или находится между заданными значениями.
+
+# Authz Drools Policy Detail
+authz-add-drools-policy=Добавить правила политики
+authz-policy-drools-maven-artifact-resolve=Разрешить
+authz-policy-drools-maven-artifact=Maven артифакт политики
+authz-policy-drools-maven-artifact.tooltip=Maven GAV, указывающий на артифакт, из которого должны будут загружены правила. Определив GAV, Вы можете нажать *Разрешить*, чтобы заполнить *Модуль* и *Сессия* поля.
+authz-policy-drools-module=Модуль
+authz-policy-drools-module.tooltip=Модуль, используемый этой политикой. Вам необходимо предоставить модуль в порядке выбора конкретной сессии, из которой будут загружены правила.
+authz-policy-drools-session=Сессия
+authz-policy-drools-session.tooltip=Сессия, используемая этой политикой. Сессия предоставляет все правила для оценок при обработке политики.
+authz-policy-drools-update-period=Период обновлений
+authz-policy-drools-update-period.tooltip=Определите интервал для поиска обновлений артефакта.
+
+# Authz JS Policy Detail
+authz-add-js-policy=Добавить политику JavaScript
+authz-policy-js-code=Код
+authz-policy-js-code.tooltip=Код JavaScript, предоставляющий условия для этой политики.
+
+
+# Authz Aggregated Policy Detail
+authz-aggregated=Совокупная
+authz-add-aggregated-policy=Добавить совокупную политику
+
+# Authz Permission List
+authz-no-permissions-available=Нет доступных разрешений.
+
+# Authz Permission Detail
+authz-permission-name.tooltip=Имя этого разрешения.
+authz-permission-description.tooltip=Описание этого разрешения.
+
+# Authz Resource Permission Detail
+authz-add-resource-permission=Добавить разрешение ресурса
+authz-permission-resource-apply-to-resource-type=Применить к типу ресурса
+authz-permission-resource-apply-to-resource-type.tooltip=Определяет, будет ли это разрешение будет применено ко всем ресурсам с данным типом. В этом случае это разрешение будет вычисляться для всех экземпляров с заданным типом ресурса.
+authz-permission-resource-resource.tooltip=Определяет, что это разрешение должно быть применено к конкретному экземпляру ресурсов.
+authz-permission-resource-type.tooltip=Определяет, что это разрешение должно быть применено ко всем экземплярам ресурсов заданного типа.
+
+# Authz Scope Permission Detail
+authz-add-scope-permission=Добавить разрешение области
+authz-permission-scope-resource.tooltip=Ограничевает области, с которыми связан выбранный ресурс. Если не выбрано, все области будут доступны.
+authz-permission-scope-scope.tooltip=Определяет, что разрешение должно быть применено к одной или нескольким областям.
+
+# Authz Evaluation
+authz-evaluation-identity-information=Идентичность данных
+authz-evaluation-identity-information.tooltip=Доступные области для конфигурации идентичных данных будут использованы при оценке политик.
+authz-evaluation-client.tooltip=Выберите клиента, осуществляющего авторизационный запрос. Если не задан, авторизационные запросы будут основаны на том клиенте, в котором вы находитесь.
+authz-evaluation-user.tooltip=Выберите пользователя, идентификационные данные которого будут использованы для запроса разрешений с сервера.
+authz-evaluation-role.tooltip=Выберите роли, которые вы хотите связать с выбранным пользователем.
+authz-evaluation-new=Новая оценка
+authz-evaluation-re-evaluate=Переоценить
+authz-evaluation-previous=Предыдущая оценка
+authz-evaluation-contextual-info=Контекстная информация
+authz-evaluation-contextual-info.tooltip=Достуные опции для конфигурации контекстной информации, которая будет использована при оценке политик.
+authz-evaluation-contextual-attributes=Контекстные аттрибуты
+authz-evaluation-contextual-attributes.tooltip=Любой аттрибут определяется запущенным окружением или контекстом исполнения.
+authz-evaluation-permissions.tooltip=Доступные опции для конфигурации разрешений, к которым будет применяться политика.
+authz-evaluation-evaluate=Оценка
+authz-evaluation-any-resource-with-scopes=Любой ресурс с областью(ями)
+authz-evaluation-no-result=Не удалось получить какой-либо результат для данного запроса авторизации. Проверьте, связаны ли назначенные ресурс(ы) или область(и) с какой-либо политикой.
+authz-evaluation-no-policies-resource=Не найдено политик для этого ресурса.
+authz-evaluation-result.tooltip=Общий результат для этого запроса разрешений.
+authz-evaluation-scopes.tooltip=Список разрешенных областей.
+authz-evaluation-policies.tooltip=Подробнее о том, какие политики были расчитаны и их разрешения.
+authz-evaluation-authorization-data=Ответ
+authz-evaluation-authorization-data.tooltip=Предоставляет токен, содержащий авторизационные данные как результат обработки авторизационного запроса. Это представление преимущественно отображает как Keycloak отвечает на запросы клиентов об авторизации. Проверьте требования 'авторизации' для тех разрешений, которые были выданы на основе текущего авторизационного запроса.
+authz-show-authorization-data=Показать авторизационные данные
+
+
+keys=Ключи
+all=Все
+status=Статус
+keystore=Хранилище ключей
+keystores=Хранилища ключей
+add-keystore=Добавить хранилище ключей
+add-keystore.placeholder=Добавить хранилище ключей...
+view=Смотреть
+active=Активные
+
+Sunday=Воскресенье
+Monday=Понедельник
+Tuesday=Вторник
+Wednesday=Среда
+Thursday=Четверг
+Friday=Пятница
+Saturday=Суббота
+
+user-storage-cache-policy=Настройки кэширования
+userStorage.cachePolicy=Политики кэширования
+userStorage.cachePolicy.option.DEFAULT=DEFAULT
+userStorage.cachePolicy.option.EVICT_WEEKLY=EVICT_WEEKLY
+userStorage.cachePolicy.option.EVICT_DAILY=EVICT_DAILY
+userStorage.cachePolicy.option.MAX_LIFESPAN=MAX_LIFESPAN
+userStorage.cachePolicy.option.NO_CACHE=NO_CACHE
+userStorage.cachePolicy.tooltip=Политики кэширования для этого поставщика хранения. 'DEFAULT' представляет настройки по-умолчанию для глобального пользовательского кэша. 'EVICT_DAILY' время каждого дня, после которого пользовательский кэш инвалидируется. 'EVICT_WEEKLY' день и время недели после которого пользовательский кэш инвалидируется. 'MAX-LIFESPAN' время в миллисекундах, в течение которого будет существовать жизненный цикл записи в кэше.
+userStorage.cachePolicy.evictionDay=День исключения
+userStorage.cachePolicy.evictionDay.tooltip=День недели в который запись станет недействительной и будет исключена из кэша.
+userStorage.cachePolicy.evictionHour=Час исключения
+userStorage.cachePolicy.evictionHour.tooltip=Час дня, в который запись станет недействительной.
+userStorage.cachePolicy.evictionMinute=Минута исключения
+userStorage.cachePolicy.evictionMinute.tooltip=Минута дня, в которую запись станет недействительной.
+userStorage.cachePolicy.maxLifespan=Максимальное время жизни
+userStorage.cachePolicy.maxLifespan.tooltip=Максимальное время жизни записи пользовательсткого кэша в секундах.
+user-origin-link=Источник хранилища
+user-origin.tooltip=UserStorageProvider из которого был загружен пользователь
+user-link.tooltip=UserStorageProvider из которого был импортирован локально сохраненный пользователь.
+
+disable=Отключено
+disableable-credential-types=Отключаемые типы
+credentials.disableable.tooltip=Список типов учетных данных, которые Вы можете отключить
+disable-credential-types=Отключить типы учетных данных
+credentials.disable.tooltip=Нажмите кнопку для отключения типов учетных данных
+credential-types=Типы учетных данных
+manage-user-password=Управление паролями
+disable-credentials=Отключить учетные данные
+credential-reset-actions=Сброс учетных данных
+ldap-mappers=Сопоставления LDAP
+create-ldap-mapper=Создать LDAP сопоставление
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/messages_fr.properties b/themes/src/main/resources-community/theme/base/admin/messages/messages_fr.properties
index c4b632a..c4a0216 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/messages_fr.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/messages_fr.properties
@@ -1,8 +1,8 @@
-invalidPasswordMinLengthMessage=Mot de passe invalide: longueur minimale {0}.
-invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.
-invalidPasswordMinDigitsMessage=Mot de passe invalide: doit contenir au moins {0} chiffre(s).
-invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.
-invalidPasswordMinSpecialCharsMessage=Mot de passe invalide: doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
-invalidPasswordNotUsernameMessage=Mot de passe invalide: ne doit pas \u00eatre identique au nom d''utilisateur.
-invalidPasswordRegexPatternMessage=Mot de passe invalide: ne valide pas l''expression rationnelle.
-invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
+invalidPasswordMinLengthMessage=Mot de passe invalide : longueur minimale requise de {0}.
+invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.
+invalidPasswordMinDigitsMessage=Mot de passe invalide : doit contenir au moins {0} chiffre(s).
+invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.
+invalidPasswordMinSpecialCharsMessage=Mot de passe invalide : doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
+invalidPasswordNotUsernameMessage=Mot de passe invalide : ne doit pas \u00eatre identique au nom d''utilisateur.
+invalidPasswordRegexPatternMessage=Mot de passe invalide : ne valide pas l''expression rationnelle.
+invalidPasswordHistoryMessage=Mot de passe invalide : ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/messages_ru.properties b/themes/src/main/resources-community/theme/base/admin/messages/messages_ru.properties
index 556d574..efedac4 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/messages_ru.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/messages_ru.properties
@@ -1,14 +1,26 @@
-invalidPasswordMinLengthMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430 {0}.
-invalidPasswordMinDigitsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0446\u0438\u0444\u0440\u043E\u0432\u044B\u0445 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinLowerCaseCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u0432 \u043D\u0438\u0436\u043D\u0435\u043C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinUpperCaseCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u0432 \u0432\u0435\u0440\u0445\u043D\u0435\u043C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinSpecialCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u043F\u0435\u0446\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordNotUsernameMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0442\u044C \u0441 \u0438\u043C\u0435\u043D\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-invalidPasswordRegexPatternMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043D\u0435 \u043F\u0440\u043E\u0448\u0435\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u044B\u043C \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435\u043C.
-invalidPasswordHistoryMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0442\u044C \u0441 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C\u0438 {0} \u043F\u0430\u0440\u043E\u043B\u044F\u043C\u0438.
+# encoding: utf-8
+invalidPasswordMinLengthMessage=Некорректный пароль: длина пароля должна быть не менее {0} символов(а).
+invalidPasswordMinDigitsMessage=Некорректный пароль: должен содержать не менее {0} цифр(ы).
+invalidPasswordMinLowerCaseCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} символов(а) в нижнем регистре.
+invalidPasswordMinUpperCaseCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} символов(а) в верхнем регистре.
+invalidPasswordMinSpecialCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} спецсимволов(а).
+invalidPasswordNotUsernameMessage=Некорректный пароль: пароль не должен совпадать с именем пользователя.
+invalidPasswordRegexPatternMessage=Некорректный пароль: пароль не прошел проверку по регулярному выражению.
+invalidPasswordHistoryMessage=Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролем(ями).
+invalidPasswordGenericMessage=Некорректный пароль: новый пароль не соответствует правилам пароля.
-ldapErrorInvalidCustomFilter=\u0421\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C \u0444\u0438\u043B\u044C\u0442\u0440 LDAP \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u043D\u0430\u0447\u0438\u043D\u0430\u0442\u044C\u0441\u044F \u0441 "(" \u0438\u043B\u0438 \u0437\u0430\u043A\u0430\u043D\u0447\u0438\u0432\u0430\u0442\u044C\u0441\u044F \u043D\u0430 ")".
-ldapErrorMissingClientId=Client ID \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438, \u0435\u0441\u043B\u0438 \u043D\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u043C\u0430\u043F\u043F\u0438\u043D\u0433 \u0440\u043E\u043B\u0435\u0439 realm.
-ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0443\u043D\u0430\u0441\u043B\u0435\u0434\u043E\u0432\u0430\u0442\u044C \u0433\u0440\u0443\u043F\u043F\u0443 \u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0447\u043B\u0435\u043D\u0441\u0442\u0432\u043E UID \u0442\u0438\u043F\u0430 \u0432\u043C\u0435\u0441\u0442\u0435.
-ldapErrorCantWriteOnlyForReadOnlyLdap=\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0440\u0435\u0436\u0438\u043C \u0442\u043E\u043B\u044C\u043A\u043E-\u043D\u0430-\u0437\u0430\u043F\u0438\u0441\u044C, \u043A\u043E\u0433\u0434\u0430 LDAP \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u043D\u0435 \u0432 \u0440\u0435\u0436\u0438\u043C\u0435 WRITABLE
-ldapErrorCantWriteOnlyAndReadOnly=\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0440\u0435\u0436\u0438\u043C\u044B \u0442\u043E\u043B\u044C\u043A\u043E-\u043D\u0430-\u0447\u0442\u0435\u043D\u0438\u0435 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E-\u043D\u0430-\u0437\u0430\u043F\u0438\u0441\u044C \u0432\u043C\u0435\u0441\u0442\u0435
\ No newline at end of file
+ldapErrorInvalidCustomFilter=Сконфигурированный пользователем фильтр LDAP не должен начинаться с "(" или заканчиваться на ")".
+ldapErrorMissingClientId=Client ID должен быть настроен в конфигурации, если не используется сопоставление ролей в realm.
+ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Не удалось унаследовать группу и использовать членство UID типа вместе.
+ldapErrorCantWriteOnlyForReadOnlyLdap=Невозможно установить режим "только на запись", когда LDAP провайдер не в режиме WRITABLE
+ldapErrorCantWriteOnlyAndReadOnly=Невозможно одновременно установить режимы "только на чтение" и "только на запись"
+
+clientRedirectURIsFragmentError=URI перенаправления не должен содержать фрагмент URI
+clientRootURLFragmentError=Корневой URL не должен содержать фрагмент URL
+
+pairwiseMalformedClientRedirectURI=Клиент содержит некорректный URI перенаправления.
+pairwiseClientRedirectURIsMissingHost=URI перенаправления клиента должен содержать корректный компонент хоста.
+pairwiseClientRedirectURIsMultipleHosts=Без конфигурации по части идентификатора URI, URI перенаправления клиента не может содержать несколько компонентов хоста.
+pairwiseMalformedSectorIdentifierURI=Искаженная часть идентификатора URI.
+pairwiseFailedToGetRedirectURIs=Не удалось получить идентификаторы URI перенаправления из части идентификатора URI.
+pairwiseRedirectURIsMismatch=Клиент URI переадресации не соответствует URI переадресации, полученной из части идентификатора URI.
diff --git a/themes/src/main/resources-community/theme/base/email/messages/messages_de.properties b/themes/src/main/resources-community/theme/base/email/messages/messages_de.properties
index 93307a6..10599d7 100755
--- a/themes/src/main/resources-community/theme/base/email/messages/messages_de.properties
+++ b/themes/src/main/resources-community/theme/base/email/messages/messages_de.properties
@@ -1,5 +1,5 @@
emailVerificationSubject=E-Mail verifizieren
-passwordResetSubject=Passwort zur\u00FCckzusetzen
+passwordResetSubject=Passwort zur\u00FCcksetzen
emailVerificationBody=Jemand hat ein {2} Konto mit dieser E-Mail Adresse erstellt. Fall Sie das 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\u00F6nnen sie diese Nachricht ignorieren.
emailVerificationBodyHtml=<p>Jemand hat ein {2} Konto mit dieser E-Mail Adresse erstellt. Fall das Sie waren, klicken Sie auf den Link, um die E-Mail Adresse zu verifizieren.</p><p><a href="{0}">{0}</a></p><p>Dieser Link wird in {1} Minuten ablaufen.</p><p>Falls Sie dieses Konto nicht erstellt haben, dann k\u00F6nnen sie diese Nachricht ignorieren.</p>
eventLoginErrorSubject=Fehlgeschlagene Anmeldung
diff --git a/themes/src/main/resources-community/theme/base/email/messages/messages_fr.properties b/themes/src/main/resources-community/theme/base/email/messages/messages_fr.properties
old mode 100755
new mode 100644
index f8b2b97..3467dec
--- a/themes/src/main/resources-community/theme/base/email/messages/messages_fr.properties
+++ b/themes/src/main/resources-community/theme/base/email/messages/messages_fr.properties
@@ -1,21 +1,21 @@
emailVerificationSubject=V\u00e9rification du courriel
-emailVerificationBody=Quelqu''un vient de cr\u00e9er un compte "{2}" avec votre courriel. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous afin de v\u00e9rifier votre adresse de courriel\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon ignorer ce message.
-emailVerificationBodyHtml=<p>Quelqu''un vient de cr\u00e9er un compte "{2}" avec votre courriel. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous afin de v\u00e9rifier votre adresse de courriel</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon veuillez ignorer ce message.</p>
+emailVerificationBody=Quelqu''un vient de cr\u00e9er un compte "{2}" avec votre courriel. Si vous \u00eates \u00e0 l''origine de cette requ\u00eate, veuillez cliquer sur le lien ci-dessous afin de v\u00e9rifier votre adresse de courriel\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon, veuillez ignorer ce message.
+emailVerificationBodyHtml=<p>Quelqu''un vient de cr\u00e9er un compte "{2}" avec votre courriel. Si vous \u00eates \u00e0 l''origine de cette requ\u00eate, veuillez cliquer sur le lien ci-dessous afin de v\u00e9rifier votre adresse de courriel</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon, veuillez ignorer ce message.</p>
passwordResetSubject=R\u00e9initialiser le mot de passe
-passwordResetBody=Quelqu''un vient de demander une r\u00e9initialisation de mot de passe pour votre compte {2}. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous pour le mettre \u00e0 jour .\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon ignorer ce message, aucun changement ne sera effectu\u00e9 sur votre compte.
-passwordResetBodyHtml=<p>Quelqu''un vient de demander une r\u00e9initialisation de mot de passe pour votre compte {2}. Si c''est bien vous, veuillez cliquer sur le lien ci-dessous pour le mettre \u00e0 jour.</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon ignorer ce message, aucun changement ne sera effectu\u00e9 sur votre compte.</p>
+passwordResetBody=Quelqu''un vient de demander une r\u00e9initialisation de mot de passe pour votre compte {2}. Si vous \u00eates \u00e0 l''origine de cette requ\u00eate, veuillez cliquer sur le lien ci-dessous pour le mettre \u00e0 jour.\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSinon, veuillez ignorer ce message ; aucun changement ne sera effectu\u00e9 sur votre compte.
+passwordResetBodyHtml=<p>Quelqu''un vient de demander une r\u00e9initialisation de mot de passe pour votre compte {2}. Si vous \u00eates \u00e0 l''origine de cette requ\u00eate, veuillez cliquer sur le lien ci-dessous pour le mettre \u00e0 jour.</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Sinon, veuillez ignorer ce message ; aucun changement ne sera effectu\u00e9 sur votre compte.</p>
executeActionsSubject=Mettre \u00e0 jour votre compte
-executeActionsBody=Votre administrateur vient de demander une mise \u00e0 jour de votre compte {2}. Veuillez cliquer sur le lien ci-dessous afin de commencer le processus.\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSi vous n''\u00eates pas au courant de cette requ\u00eate, ignorer ce message, aucun changement ne sera effectu\u00e9 sur votre compte.
-executeActionsBodyHtml=<p>Votre administrateur vient de demander une mise \u00e0 jour de votre compte {2}. Veuillez cliquer sur le lien ci-dessous afin de commencer le processus.</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Si vous n''\u00eates pas au courant de cette requ\u00eate, ignorer ce message, aucun changement ne sera effectu\u00e9 sur votre compte.</p>
+executeActionsBody=Votre administrateur vient de demander une mise \u00e0 jour de votre compte {2}. Veuillez cliquer sur le lien ci-dessous afin de commencer le processus.\n\n{0}\n\nCe lien expire dans {1} minute(s).\n\nSi vous n''\u00eates pas \u00e0 l''origine de cette requ\u00eate, veuillez ignorer ce message ; aucun changement ne sera effectu\u00e9 sur votre compte.
+executeActionsBodyHtml=<p>Votre administrateur vient de demander une mise \u00e0 jour de votre compte {2}. Veuillez cliquer sur le lien ci-dessous afin de commencer le processus.</p><p><a href="{0}">{0}</a></p><p>Ce lien expire dans {1} minute(s).</p><p>Si vous n''\u00eates pas \u00e0 l''origine de cette requ\u00eate, veuillez ignorer ce message ; aucun changement ne sera effectu\u00e9 sur votre compte.</p>
eventLoginErrorSubject=Erreur de connexion
-eventLoginErrorBody=Une tentative de connexion a \u00e9t\u00e9 d\u00e9tect\u00e9e sur votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
-eventLoginErrorBodyHtml=<p>Une tentative de connexion a \u00e9t\u00e9 d\u00e9tect\u00e9e sur votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
+eventLoginErrorBody=Une tentative de connexion a \u00e9t\u00e9 d\u00e9tect\u00e9e sur votre compte {0} depuis {1}. Si vous n''\u00eates pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.
+eventLoginErrorBodyHtml=<p>Une tentative de connexion a \u00e9t\u00e9 d\u00e9tect\u00e9e sur votre compte {0} depuis {1}. Si vous n''\u00eates pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.</p>
eventRemoveTotpSubject=Suppression du TOTP
-eventRemoveTotpBody=Le TOTP a \u00e9t\u00e9 supprim\u00e9 de votre compte {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
-eventRemoveTotpBodyHtml=<p>Le TOTP a \u00e9t\u00e9 supprim\u00e9 de votre compte {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
+eventRemoveTotpBody=Le TOTP a \u00e9t\u00e9 supprim\u00e9 de votre compte {0} depuis {1}. Si vous n''\u00e9tiez pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.
+eventRemoveTotpBodyHtml=<p>Le TOTP a \u00e9t\u00e9 supprim\u00e9 de votre compte {0} depuis {1}. Si vous n''\u00e9tiez pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.</p>
eventUpdatePasswordSubject=Mise \u00e0 jour du mot de passe
-eventUpdatePasswordBody=Votre mot de passe a chang\u00e9 pour votre compte {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
-eventUpdatePasswordBodyHtml=<p>Votre mot de passe a chang\u00e9 pour votre compte {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
+eventUpdatePasswordBody=Votre mot de passe pour votre compte {0} a \u00e9t\u00e9 modifi\u00e9 depuis {1}. Si vous n''\u00e9tiez pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.
+eventUpdatePasswordBodyHtml=<p>Votre mot de passe pour votre compte {0} a \u00e9t\u00e9 modifi\u00e9 depuis {1}. Si vous n''\u00e9tiez pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.</p>
eventUpdateTotpSubject=Mise \u00e0 jour du TOTP
-eventUpdateTotpBody=Le TOTP a \u00e9t\u00e9 mis \u00e0 jour pour votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.
-eventUpdateTotpBodyHtml=<p>Le TOTP a \u00e9t\u00e9 mis \u00e0 jour pour votre compte sur {0} depuis {1}. Si ce n''est pas vous, veuillez contacter votre administrateur.</p>
+eventUpdateTotpBody=Le TOTP a \u00e9t\u00e9 mis \u00e0 jour pour votre compte {0} depuis {1}. Si vous n''\u00e9tiez pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.
+eventUpdateTotpBodyHtml=<p>Le TOTP a \u00e9t\u00e9 mis \u00e0 jour pour votre compte {0} depuis {1}. Si vous n''\u00e9tiez pas \u00e0 l''origine de cette requ\u00eate, veuillez contacter votre administrateur.</p>
diff --git a/themes/src/main/resources-community/theme/base/email/messages/messages_it.properties b/themes/src/main/resources-community/theme/base/email/messages/messages_it.properties
index e69de29..b877b91 100755
--- a/themes/src/main/resources-community/theme/base/email/messages/messages_it.properties
+++ b/themes/src/main/resources-community/theme/base/email/messages/messages_it.properties
@@ -0,0 +1,24 @@
+emailVerificationSubject=Verifica l''email
+emailVerificationBody=Qualcuno ha creato un {2} account con questo indirizzo email. Se sei stato tu, fai clic sul pulsante seguente per verificare il tuo indirizzo email\n\n{0}\n\nQuesto link scadr\u00e0 in {1} minuti.\n\nSe non sei stato tu a creare questo account, ignora questo messaggio.
+emailVerificationBodyHtml=<p> Qualcuno ha creato un {2} account con questo indirizzo email. Se sei stato tu, fai clic sul pulsante seguente per verificare il tuo indirizzo email</p><p><a href="{0}">{0}</a></p><p>Questo link scadr\u00e0 in {1} minuti.</p><p>Se non sei stato tu a creare questo account, ignora questo messaggio.</p>
+identityProviderLinkSubject=Link {0}
+identityProviderLinkBody=Qualcuno vuole associare il tuo "{1}" account con "{0}" l''account dell''utente {2} . Se sei stato tu, fai clic sul pulsante seguente per associare gli account\n\n{3}\n\nQuesto link scadr\u00e0 in {4} minuti.\n\nSe non vuoi associare l''account, ignora questo messaggio. Se associ gli account, potrai accedere a {1} attraverso {0}.
+identityProviderLinkBodyHtml=<p>Qualcuno vuole associare il tuo <b>{1}</b> account con <b>{0}</b> l''account dell''utente {2} . Se sei stato tu, fai clic sul pulsante seguente per associare gli account</p><p><a href="{3}">{3}</a></p><p>Questo link scadr\u00e0 in {4} minuti.</p><p>Se non vuoi associare l''account, ignora questo messaggio. Se associ gli account, potrai accedere a {1} attraverso {0}.</p>
+passwordResetSubject=Reimposta la password
+passwordResetBody= Qualcuno ha appena richiesto di cambiare le {2} credenziali di accesso al tuo account. Se sei stato tu, fai clic sul pulsante seguente reimpostarle.\n\n{0}\n\nQuesto link e codice scadranno in {1} minuti.\n\nSe non vuoi reimpostare le tue credenziali di accesso, ignora questo messaggio e non verr\u00e0 effettuato nessun cambio.
+passwordResetBodyHtml=<p>Qualcuno ha appena richiesto di cambiare le {2} credenziali di accesso al tuo account. Se sei stato tu, fai clic sul pulsante seguente reimpostarle.</p><p><a href="{0}">{0}</a></p><p>Questo link scadr\u00e0 in {1} minuti.</p><p>Se non vuoi reimpostare le tue credenziali di accesso, ignora questo messaggio e non verr\u00e0 effettuato nessun cambio.</p>
+executeActionsSubject=Aggiorna il tuo account
+executeActionsBody=Il tuo amministratore ha appena richiesto un aggiornamento del tuo {2} account. Fai clic sul pulsante seguente per iniziare questo processo.\n\n{0}\n\nQuesto link scadr\u00e0 in {1} minuti.\n\nSe non sei a conoscenza della richiesta del tuo amministratore, ignora questo messaggio e non verr\u00e0 effettuato nessun cambio.
+executeActionsBodyHtml=<p>Il tuo amministratore ha appena richiesto un aggiornamento del tuo {2} account. Fai clic sul pulsante seguente per iniziare questo processo.</p><p><a href="{0}">{0}</a></p><p>Questo link scadr\u00e0 in {1} minuti.</p><p>Se non sei a conoscenza della richiesta del tuo amministratore, ignora questo messaggio e non verr\u00e0 effettuato nessun cambio.</p>
+eventLoginErrorSubject=Errore di accesso
+eventLoginErrorBody=\u00c8 stato rilevato un tentativo fallito di accesso al tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.
+eventLoginErrorBodyHtml=<p>\u00c8 stato rilevato un tentativo fallito di accesso al tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.</p>
+eventRemoveTotpSubject=Rimuovi TOTP (Password temporanea valida una volta sola)
+eventRemoveTotpBody=TOTP (Password temporanea valida una volta sola) \u00e8 stata rimossa dal tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.
+eventRemoveTotpBodyHtml=<p>TOTP (Password temporanea valida una volta sola) \u00e8 stata rimossa dal tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.</p>
+eventUpdatePasswordSubject=Aggiornamento password
+eventUpdatePasswordBody=La tua password \u00e8 stata cambiata il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.
+eventUpdatePasswordBodyHtml=<p> La tua password \u00e8 stata cambiata il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.</p>
+eventUpdateTotpSubject=Aggiornamento TOTP (Password temporanea valida una volta sola)
+eventUpdateTotpBody=TOTP (Password temporanea valida una volta sola) \u00e8 stata aggiornata per il tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.
+eventUpdateTotpBodyHtml=<p>TOTP (Password temporanea valida una volta sola) \u00e8 stata aggiornata per il tuo account il {0} da {1}. Se non sei stato tu, per favore contatta l''amministratore.</p>
diff --git a/themes/src/main/resources-community/theme/base/email/messages/messages_ru.properties b/themes/src/main/resources-community/theme/base/email/messages/messages_ru.properties
index 7663fa4..1355788 100644
--- a/themes/src/main/resources-community/theme/base/email/messages/messages_ru.properties
+++ b/themes/src/main/resources-community/theme/base/email/messages/messages_ru.properties
@@ -1,24 +1,25 @@
-emailVerificationSubject=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 email
-emailVerificationBody=\u041A\u0442\u043E-\u0442\u043E \u0441\u043E\u0437\u0434\u0430\u043B \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C {2} \u0441 \u044D\u0442\u0438\u043C email. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u0432\u044B, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0441\u0441\u044B\u043B\u043A\u0443 \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0432\u0430\u0448\u0435\u0433\u043E email\n\n{0}\n\n\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {1} \u043C\u0438\u043D\u0443\u0442.\n\n\u0415\u0441\u043B\u0438 \u0412\u044B \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0432\u0430\u043B\u0438 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E.
-emailVerificationBodyHtml=<p>\u041A\u0442\u043E-\u0442\u043E \u0441\u043E\u0437\u0434\u0430\u043B \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C {2} \u0441 \u044D\u0442\u0438\u043C email. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u0412\u044B, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0432\u0430\u0448\u0435\u0433\u043E email</p><p><a href="{0}">{0}</a></p><p>\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {1} \u043C\u0438\u043D\u0443\u0442.</p><p>\u0415\u0441\u043B\u0438 \u0412\u044B \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0432\u0430\u043B\u0438 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E.</p>
-identityProviderLinkSubject=\u0421\u0441\u044B\u043B\u043A\u0430 {0}
-identityProviderLinkBody=\u041A\u0442\u043E-\u0442\u043E \u0445\u043E\u0447\u0435\u0442 \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u0432\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C "{1}" \u0441 "{0}" \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F {2} . \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u0412\u044B, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439 \u0441\u0441\u044B\u043B\u043A\u0435, \u0447\u0442\u043E\u0431\u044B \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0437\u0430\u043F\u0438\u0441\u0438\n\n{3}\n\n\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {4} \u043C\u0438\u043D\u0443\u0442.\n\n\u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u043D\u0435 \u0445\u043E\u0442\u0438\u0442\u0435 \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u044F\u0442\u044C \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0437\u0430\u043F\u0438\u0441\u0438, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E. \u041F\u043E\u0441\u043B\u0435 \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\u043E\u0439\u0442\u0438 \u0432 {1} \u0447\u0435\u0440\u0435\u0437 {0}.
-identityProviderLinkBodyHtml=<p>\u041A\u0442\u043E-\u0442\u043E \u0445\u043E\u0447\u0435\u0442 \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u0432\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C <b>{1}</b> \u0441 <b>{0}</b> \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F {2} . \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u0412\u044B, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439 \u0441\u0441\u044B\u043B\u043A\u0435, \u0447\u0442\u043E\u0431\u044B \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0437\u0430\u043F\u0438\u0441\u0438</p><p><a href="{3}">{3}</a></p><p>\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {4} \u043C\u0438\u043D\u0443\u0442.</p><p>\u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u043D\u0435 \u0445\u043E\u0442\u0438\u0442\u0435 \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u044F\u0442\u044C \u0443\u0447\u0435\u0442\u043D\u044B\u0435 \u0437\u0430\u043F\u0438\u0441\u0438, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E. \u041F\u043E\u0441\u043B\u0435 \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u0412\u044B \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\u043E\u0439\u0442\u0438 \u0432 {1} \u0447\u0435\u0440\u0435\u0437 {0}.</p>
-passwordResetSubject=\u0421\u0431\u0440\u043E\u0441 \u043F\u0430\u0440\u043E\u043B\u044F
-passwordResetBody=\u041A\u0442\u043E-\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u043B \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F \u043E\u0442 \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {2}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u0412\u044B, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0441\u0441\u044B\u043B\u043A\u0443, \u0447\u0442\u043E\u0431\u044B \u0441\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0435\u0433\u043E.\n\n{0}\n\n\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {1} \u043C\u0438\u043D\u0443\u0442.\n\n\u0415\u0441\u043B\u0438 \u0412\u044B \u043D\u0435 \u0445\u043E\u0442\u0438\u0442\u0435 \u0441\u0431\u0440\u0430\u0441\u044B\u0432\u0430\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E.
-passwordResetBodyHtml=<p>\u041A\u0442\u043E-\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u043B \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F \u043E\u0442 \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {2}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u0412\u044B, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0441\u0441\u044B\u043B\u043A\u0443, \u0447\u0442\u043E\u0431\u044B \u0441\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0435\u0433\u043E.</p><p><a href="{0}">{0}</a></p><p>\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {1} \u043C\u0438\u043D\u0443\u0442.</p><p>\u0415\u0441\u043B\u0438 \u0412\u044B \u043D\u0435 \u0445\u043E\u0442\u0438\u0442\u0435 \u0441\u0431\u0440\u0430\u0441\u044B\u0432\u0430\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E \u0438 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u0441\u044F.</p>
-executeActionsSubject=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0412\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438
-executeActionsBody=\u0412\u0430\u0448 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u043B \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E\u0441\u0442\u044C \u0412\u0430\u043C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0432\u043E\u044E \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C {2}. \u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439 \u0441\u0441\u044B\u043B\u043A\u0435 \u0447\u0442\u043E\u0431\u044B \u043D\u0430\u0447\u0430\u0442\u044C \u044D\u0442\u043E\u0442 \u043F\u0440\u043E\u0446\u0435\u0441\u0441.\n\n{0}\n\n\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {1} \u043C\u0438\u043D\u0443\u0442.\n\n\u0415\u0441\u043B\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044C \u043F\u043E\u0434\u043E\u0437\u0440\u0435\u043D\u0438\u044F, \u0447\u0442\u043E \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440 \u043D\u0435 \u043C\u043E\u0433 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0442\u0430\u043A\u043E\u0439 \u0437\u0430\u043F\u0440\u043E\u0441, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E.
-executeActionsBodyHtml=<p>\u0412\u0430\u0448 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u043B \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E\u0441\u0442\u044C \u0412\u0430\u043C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0432\u043E\u044E \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C {2}. \u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439 \u0441\u0441\u044B\u043B\u043A\u0435 \u0447\u0442\u043E\u0431\u044B \u043D\u0430\u0447\u0430\u0442\u044C \u044D\u0442\u043E\u0442 \u043F\u0440\u043E\u0446\u0435\u0441\u0441.</p><p><a href="{0}">{0}</a></p><p>\u042D\u0442\u0430 \u0441\u0441\u044B\u043B\u043A\u0430 \u0443\u0441\u0442\u0430\u0440\u0435\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 {1} \u043C\u0438\u043D\u0443\u0442.</p><p>\u0415\u0441\u043B\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044C \u043F\u043E\u0434\u043E\u0437\u0440\u0435\u043D\u0438\u044F, \u0447\u0442\u043E \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440 \u043D\u0435 \u043C\u043E\u0433 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0442\u0430\u043A\u043E\u0439 \u0437\u0430\u043F\u0440\u043E\u0441, \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0440\u043E\u0438\u0433\u043D\u043E\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E \u043F\u0438\u0441\u044C\u043C\u043E.</p>
-eventLoginErrorSubject=\u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u0445\u043E\u0434\u0430
-eventLoginErrorBody=\u041D\u0435\u0443\u0434\u0430\u0447\u043D\u0430\u044F \u043F\u043E\u043F\u044B\u0442\u043A\u0430 \u0432\u0445\u043E\u0434\u0430 \u0431\u044B\u043B\u0430 \u0437\u0430\u0444\u0438\u043A\u0441\u0438\u0440\u043E\u0432\u0430\u043D\u0430 \u0432 \u0432\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C {0} \u0441 {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.
-eventLoginErrorBodyHtml=<p>\u041D\u0435\u0443\u0434\u0430\u0447\u043D\u0430\u044F \u043F\u043E\u043F\u044B\u0442\u043A\u0430 \u0432\u0445\u043E\u0434\u0430 \u0431\u044B\u043B\u0430 \u0437\u0430\u0444\u0438\u043A\u0441\u0438\u0440\u043E\u0432\u0430\u043D\u0430 \u0432 \u0432\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C {0} \u0441 {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.</p>
-eventRemoveTotpSubject=\u0423\u0434\u0430\u043B\u0438\u0442\u044C TOTP
-eventRemoveTotpBody=TOTP \u0431\u044B\u043B \u0443\u0434\u0430\u043B\u0435\u043D \u0438\u0437 \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {0} c {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.
-eventRemoveTotpBodyHtml=<p>TOTP \u0431\u044B\u043B \u0443\u0434\u0430\u043B\u0435\u043D \u0438\u0437 \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {0} c {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.</p>
-eventUpdatePasswordSubject=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F
-eventUpdatePasswordBody=\u0412\u0430\u0448 \u043F\u0430\u0440\u043E\u043B\u044C \u0431\u044B\u043B \u0438\u0437\u043C\u0435\u043D\u0435\u043D \u0432 {0} \u0441 {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.
-eventUpdatePasswordBodyHtml=<p>\u0412\u0430\u0448 \u043F\u0430\u0440\u043E\u043B\u044C \u0431\u044B\u043B \u0438\u0437\u043C\u0435\u043D\u0435\u043D \u0432 {0} \u0441 {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.</p>
-eventUpdateTotpSubject=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 TOTP
-eventUpdateTotpBody=TOTP \u0431\u044B\u043B \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D \u0432 \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {0} \u0441 {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.
-eventUpdateTotpBodyHtml=<p>TOTP \u0431\u044B\u043B \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D \u0432 \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {0} \u0441 {1}. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0431\u044B\u043B\u0438 \u043D\u0435 \u0412\u044B, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.</p>
+# encoding: utf-8
+emailVerificationSubject=Подтверждение E-mail
+emailVerificationBody=Кто-то создал учетную запись {2} с этим E-mail. Если это были Вы, нажмите на следующую ссылку для подтверждения вашего email\n\n{0}\n\nЭта ссылка устареет через {1} минут.\n\nЕсли Вы не создавали учетную запись, просто проигнорируйте это письмо.
+emailVerificationBodyHtml=<p>Кто-то создал учетную запись {2} с этим E-mail. Если это были Вы, нажмите по ссылке для подтверждения вашего E-mail</p><p><a href="{0}">{0}</a></p><p>Эта ссылка устареет через {1} минут.</p><p>Если Вы не создавали учетную запись, просто проигнорируйте это письмо.</p>
+identityProviderLinkSubject=Ссылка {0}
+identityProviderLinkBody=Кто-то хочет связать вашу учетную запись "{1}" с "{0}" учетной записью пользователя {2} . Если это были Вы, нажмите по следующей ссылке, чтобы связать учетные записи\n\n{3}\n\nЭта ссылка устареет через {4} минут.\n\nЕсли это не хотите объединять учетные записи, просто проигнориуйте это письмо. После объединения учетных записей Вы можете войти в {1} через {0}.
+identityProviderLinkBodyHtml=<p>Кто-то хочет связать вашу учетную запись <b>{1}</b> с <b>{0}</b> учетной записью пользователя {2} . Если это были Вы, нажмите по следующей ссылке, чтобы связать учетные записи</p><p><a href="{3}">{3}</a></p><p>Эта ссылка устареет через {4} минут.</p><p>Если это не хотите объединять учетные записи, просто проигнориуйте это письмо. После объединения учетных записей Вы можете войти в {1} через {0}.</p>
+passwordResetSubject=Сброс пароля
+passwordResetBody=Кто-то только что запросил изменение пароля от Вашей учетной записи {2}. Если это были Вы, нажмите на следующую ссылку, чтобы сбросить его.\n\n{0}\n\nЭта ссылка устареет через {1} минут.\n\nЕсли Вы не хотите сбрасывать пароль, просто проигнорируйте это письмо.
+passwordResetBodyHtml=<p>Кто-то только что запросил изменение пароля от Вашей учетной записи {2}. Если это были Вы, нажмите на следующую ссылку, чтобы сбросить его.</p><p><a href="{0}">{0}</a></p><p>Эта ссылка устареет через {1} минут.</p><p>Если Вы не хотите сбрасывать пароль, просто проигнорируйте это письмо и ничего не изменится.</p>
+executeActionsSubject=Обновление Вашей учетной записи
+executeActionsBody=Администратор просит Вас обновить данные Вашей учетной записи {2}. Нажмите по следующей ссылке чтобы начать этот процесс.\n\n{0}\n\nЭта ссылка устареет через {1} минут.\n\nЕсли у вас есть подозрения, что администратор не мог сделать такой запрос, просто проигнорируйте это письмо.
+executeActionsBodyHtml=<p>Администратор просит Вас обновить данные Вашей учетной записи {2}. Нажмите по следующей ссылке чтобы начать этот процесс.</p><p><a href="{0}">{0}</a></p><p>Эта ссылка устареет через {1} минут.</p><p>Если у вас есть подозрения, что администратор не мог сделать такой запрос, просто проигнорируйте это письмо.</p>
+eventLoginErrorSubject=Ошибка входа
+eventLoginErrorBody=Была зафиксирована неудачная попытка входа в Вашу учетную запись {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.
+eventLoginErrorBodyHtml=<p>Была зафиксирована неудачная попытка входа в Вашу учетную запись {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>
+eventRemoveTotpSubject=Удалить TOTP
+eventRemoveTotpBody=TOTP был удален из вашей учетной записи {0} c {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.
+eventRemoveTotpBodyHtml=<p>TOTP был удален из вашей учетной записи {0} c {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>
+eventUpdatePasswordSubject=Обновление пароля
+eventUpdatePasswordBody=Ваш пароль был изменен в {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.
+eventUpdatePasswordBodyHtml=<p>Ваш пароль был изменен в {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>
+eventUpdateTotpSubject=Обновление TOTP
+eventUpdateTotpBody=TOTP был обновлен в вашей учетной записи {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.
+eventUpdateTotpBodyHtml=<p>TOTP был обновлен в вашей учетной записи {0} с {1}. Если это были не Вы, пожалуйста, свяжитесь с администратором.</p>
diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties
index 29fb1f7..327e99b 100755
--- a/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties
+++ b/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties
@@ -82,7 +82,7 @@ backToApplication=« Zur\u00FCck zur Applikation
temporaryEmailCode=Tempor\u00E4rer Email Code
emailInstruction=Geben Sie ihren Benutzernamen oder E-Mail Adresse ein und klicken Sie auf Absenden. Danach werden wir Ihnen eine E-Mail mit weiteren Instruktionen zusenden.
-validateResetEmailInstruction=Eine E-Mail wurde an Sie versendet. Klicken Sie auf die URL in der E-Mail, um ihr Passwort zur\u00FCckzusetzen und sich neu anzumelden. Alternativ k\u00F6nnen Sie den Tempor\u00E4ren Code aus der E-Mail in das Eingabefeld unten links eingeben und auf Absenden klicken.
+validateResetEmailInstruction=Eine E-Mail wurde an Sie versendet. Klicken Sie auf die URL in der E-Mail, um ihr Passwort zur\u00FCckzusetzen und sich neu anzumelden. Alternativ k\u00F6nnen Sie den tempor\u00E4ren Code aus der E-Mail in das Eingabefeld unten links eingeben und auf Absenden klicken.
copyCodeInstruction=Bitte kopieren Sie den folgenden Code und f\u00FCgen ihn in die Applikation ein\:
diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_fr.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_fr.properties
old mode 100755
new mode 100644
index 05dd54e..f1d4254
--- a/themes/src/main/resources-community/theme/base/login/messages/messages_fr.properties
+++ b/themes/src/main/resources-community/theme/base/login/messages/messages_fr.properties
@@ -27,13 +27,13 @@ loginProfileTitle=Mise \u00e0 jour du compte
loginTimeout=Le temps imparti pour la connexion est \u00e9coul\u00e9. Le processus de connexion red\u00e9marre depuis le d\u00e9but.
oauthGrantTitle=OAuth Grant
oauthGrantTitleHtml={0}
-errorTitle=Nous sommes d\u00e9sol\u00e9s ...
-errorTitleHtml=Nous sommes <strong>d\u00e9sol\u00e9s</strong> ...
+errorTitle=Nous sommes d\u00e9sol\u00e9s...
+errorTitleHtml=Nous sommes <strong>d\u00e9sol\u00e9s</strong>...
emailVerifyTitle=V\u00e9rification du courriel
emailForgotTitle=Mot de passe oubli\u00e9 ?
updatePasswordTitle=Mise \u00e0 jour du mot de passe
codeSuccessTitle=Code succ\u00e8s
-codeErrorTitle=Code d''erreur\: {0}
+codeErrorTitle=Code d''erreur \: {0}
termsTitle=Termes et Conditions
termsTitleHtml=Termes et Conditions
@@ -43,7 +43,7 @@ recaptchaFailed=Re-captcha invalide
recaptchaNotConfigured=Re-captcha est requis, mais il n''est pas configur\u00e9
consentDenied=Consentement refus\u00e9.
-noAccount=Nouvel utilisateur?
+noAccount=Nouvel utilisateur ?
username=Nom d''utilisateur
usernameOrEmail=Nom d''utilisateur ou courriel
firstName=Pr\u00e9nom
@@ -68,7 +68,7 @@ emailVerified=Courriel v\u00e9rifi\u00e9
gssDelegationCredential=Accr\u00e9ditation de d\u00e9l\u00e9gation GSS
loginTotpStep1=Installez <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou bien Google Authenticator sur votre mobile. Ces deux applications sont disponibles sur <a href="https://play.google.com">Google Play</a> et Apple App Store.
-loginTotpStep2=Ouvrez l''application et scanner le code barre ou entrez la cl\u00e9.
+loginTotpStep2=Ouvrez l''application et scannez le code-barres ou entrez la clef.
loginTotpStep3=Entrez le code \u00e0 usage unique fourni par l''application et cliquez sur Sauvegarder pour terminer.
loginTotpOneTime=Code \u00e0 usage unique
@@ -76,21 +76,21 @@ oauthGrantRequest=Voulez-vous accorder ces privil\u00e8ges d''acc\u00e8s ?
inResource=dans
emailVerifyInstruction1=Un courriel avec des instructions \u00e0 suivre vous a \u00e9t\u00e9 envoy\u00e9.
-emailVerifyInstruction2=Vous n''avez pas re\u00e7u de code dans le courriel?
-emailVerifyInstruction3=Pour renvoyer le courriel.
+emailVerifyInstruction2=Vous n''avez pas re\u00e7u de code dans le courriel ?
+emailVerifyInstruction3=pour renvoyer le courriel.
-emailLinkIdpTitle=Link {0}
-emailLinkIdp1=An email with instructions to link {0} account {1} with your {2} account has been sent to you.
-emailLinkIdp2=Haven''t received a verification code in your email?
-emailLinkIdp3=to re-send the email.
+emailLinkIdpTitle=Association avec {0}
+emailLinkIdp1=Un courriel avec des instructions pour associer le compte {1} sur {0} avec votre compte {2} vous a \u00e9t\u00e9 envoy\u00e9.
+emailLinkIdp2=Vous n''avez pas re\u00e7u de code dans le courriel ?
+emailLinkIdp3=pour renvoyer le courriel.
backToLogin=« Retour \u00e0 la connexion
-emailInstruction=Entrez votre nom d''utilisateur ou votre courriel, un courriel va vous \u00eatre envoy\u00e9 vous permettant de cr\u00e9er un nouveau mot de passe.
+emailInstruction=Entrez votre nom d''utilisateur ou votre courriel ; un courriel va vous \u00eatre envoy\u00e9 vous permettant de cr\u00e9er un nouveau mot de passe.
-copyCodeInstruction=Copiez le code et recopiez le dans votre application:
+copyCodeInstruction=Copiez le code et recopiez le dans votre application :
-personalInfo=Information personnelle:
+personalInfo=Information personnelle :
role_admin=Administrateur
role_realm-admin=Administrateur du domaine
role_create-realm=Cr\u00e9er un domaine
@@ -100,17 +100,17 @@ role_view-users=Voir les utilisateurs
role_view-applications=Voir les applications
role_view-clients=Voir les clients
role_view-events=Voir les \u00e9v\u00e9nements
-role_view-identity-providers=Voir les fournisseurs d''identit\u00e9s
+role_view-identity-providers=Voir les fournisseurs d''identit\u00e9
role_manage-realm=G\u00e9rer le domaine
role_manage-users=G\u00e9rer les utilisateurs
role_manage-applications=G\u00e9rer les applications
-role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9s
+role_manage-identity-providers=G\u00e9rer les fournisseurs d''identit\u00e9
role_manage-clients=G\u00e9rer les clients
role_manage-events=G\u00e9rer les \u00e9v\u00e9nements
-role_view-profile=Voir le profile
+role_view-profile=Voir le profil
role_manage-account=G\u00e9rer le compte
role_read-token=Lire le jeton d''authentification
-role_offline-access=Acc\u00e9s hors-ligne
+role_offline-access=Acc\u00e8s hors-ligne
client_account=Compte
client_security-admin-console=Console d''administration de la s\u00e9curit\u00e9
client_admin-cli=Admin CLI
@@ -120,8 +120,8 @@ client_broker=Broker
invalidUserMessage=Nom d''utilisateur ou mot de passe invalide.
invalidEmailMessage=Courriel invalide.
accountDisabledMessage=Compte d\u00e9sactiv\u00e9, contactez votre administrateur.
-accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, contactez votre administrateur ou bien r\u00e9assayer plus tard.
-expiredCodeMessage=Fin de connexion. Veuillez vous reconnecter.
+accountTemporarilyDisabledMessage=Ce compte est temporairement d\u00e9sactiv\u00e9, contactez votre administrateur ou bien r\u00e9essayez plus tard.
+expiredCodeMessage=Connexion expir\u00e9e. Veuillez vous reconnecter.
missingFirstNameMessage=Veuillez entrer votre pr\u00e9nom.
missingLastNameMessage=Veuillez entrer votre nom.
@@ -138,36 +138,36 @@ invalidTotpMessage=Le code d''authentification est invalide.
usernameExistsMessage=Le nom d''utilisateur existe d\u00e9j\u00e0.
emailExistsMessage=Le courriel existe d\u00e9j\u00e0.
-federatedIdentityEmailExistsMessage=Cet utilisateur avec ce courriel existe d\u00e9j\u00e0. Veuillez vous connect\u00e9 au gestionnaire de compte pour lier le compte.
+federatedIdentityEmailExistsMessage=Cet utilisateur avec ce courriel existe d\u00e9j\u00e0. Veuillez vous connecter au gestionnaire de compte pour lier le compte.
confirmLinkIdpTitle=Ce compte existe d\u00e9j\u00e0
-federatedIdentityConfirmLinkMessage=L''utilisateur {0} {1} existe d\u00e9j\u00e0. Que souhaitez-vous faire?
+federatedIdentityConfirmLinkMessage=L''utilisateur {0} {1} existe d\u00e9j\u00e0. Que souhaitez-vous faire ?
federatedIdentityConfirmReauthenticateMessage=Identifiez vous en tant que {0} afin de lier votre compte avec {1}
confirmLinkIdpReviewProfile=V\u00e9rifiez vos informations de profil
-confirmLinkIdpContinue=Souhaitez-vous lier {0} à votre compte existant
+confirmLinkIdpContinue=Souhaitez-vous lier {0} \u00e0 votre compte existant
configureTotpMessage=Vous devez configurer l''authentification par mobile pour activer votre compte.
-updateProfileMessage=Vous devez mettre \u00e0 jour votre profile pour activer votre compte.
+updateProfileMessage=Vous devez mettre \u00e0 jour votre profil pour activer votre compte.
updatePasswordMessage=Vous devez changer votre mot de passe pour activer votre compte.
verifyEmailMessage=Vous devez v\u00e9rifier votre courriel pour activer votre compte.
linkIdpMessage=Vous devez v\u00e9rifier votre courriel pour lier votre compte avec {0}.
emailSentMessage=Vous devriez recevoir rapidement un courriel avec de plus amples instructions.
-emailSendErrorMessage=Erreur lors de l''envoie du courriel, veuillez essayer plus tard.
+emailSendErrorMessage=Erreur lors de l''envoi du courriel, veuillez essayer plus tard.
accountUpdatedMessage=Votre compte a \u00e9t\u00e9 mis \u00e0 jour.
accountPasswordUpdatedMessage=Votre mot de passe a \u00e9t\u00e9 mis \u00e0 jour.
noAccessMessage=Aucun acc\u00e8s
-invalidPasswordMinLengthMessage=Mot de passe invalide: longueur minimale {0}.
-invalidPasswordMinDigitsMessage=Mot de passe invalide: doit contenir au moins {0} chiffre(s).
-invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.
-invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.
-invalidPasswordMinSpecialCharsMessage=Mot de passe invalide: doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
-invalidPasswordNotUsernameMessage=Mot de passe invalide: ne doit pas \u00eatre identique au nom d''utilisateur.
-invalidPasswordRegexPatternMessage=Mot de passe invalide: ne valide pas l''expression rationnelle.
-invalidPasswordHistoryMessage=Mot de passe invalide: ne doit pas \u00eatre \u00e9gal aux {0} derniers mot de passe.
+invalidPasswordMinLengthMessage=Mot de passe invalide : longueur minimale requise de {0}.
+invalidPasswordMinDigitsMessage=Mot de passe invalide : doit contenir au moins {0} chiffre(s).
+invalidPasswordMinLowerCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en minuscule.
+invalidPasswordMinUpperCaseCharsMessage=Mot de passe invalide : doit contenir au moins {0} lettre(s) en majuscule.
+invalidPasswordMinSpecialCharsMessage=Mot de passe invalide : doit contenir au moins {0} caract\u00e8re(s) sp\u00e9ciaux.
+invalidPasswordNotUsernameMessage=Mot de passe invalide : ne doit pas \u00eatre identique au nom d''utilisateur.
+invalidPasswordRegexPatternMessage=Mot de passe invalide : ne valide pas l''expression rationnelle.
+invalidPasswordHistoryMessage=Mot de passe invalide : ne doit pas \u00eatre \u00e9gal aux {0} derniers mots de passe.
failedToProcessResponseMessage=Erreur lors du traitement de la r\u00e9ponse
httpsRequiredMessage=Le protocole HTTPS est requis
@@ -177,13 +177,13 @@ failedLogout=La d\u00e9connexion a \u00e9chou\u00e9e
unknownLoginRequesterMessage=Compte inconnu du demandeur
loginRequesterNotEnabledMessage=La connexion du demandeur n''est pas active
bearerOnlyMessage=Les applications Bearer-only ne sont pas autoris\u00e9es \u00e0 initier la connexion par navigateur.
-standardFlowDisabledMessage=Le client n'est pas autoris\u00e9 \u00e0 initier un connexion avec le navigateur avec ce response_type. Le flux standard est d\u00e9sactiv\u00e9 pour le client.
-implicitFlowDisabledMessage=Le client n'est pas autoris\u00e9 \u00e0 initier un connexion avec le navigateur avec ce response_type. Le flux implicite est d\u00e9sactiv\u00e9 pour le client.
-invalidRedirectUriMessage=L''uri de redirection est invalide
+standardFlowDisabledMessage=Le client n''est pas autoris\u00e9 \u00e0 initier une connexion avec le navigateur avec ce response_type. Le flux standard est d\u00e9sactiv\u00e9 pour le client.
+implicitFlowDisabledMessage=Le client n''est pas autoris\u00e9 \u00e0 initier une connexion avec le navigateur avec ce response_type. Le flux implicite est d\u00e9sactiv\u00e9 pour le client.
+invalidRedirectUriMessage=L''URI de redirection est invalide
unsupportedNameIdFormatMessage=NameIDFormat non support\u00e9
invalidRequesterMessage=Demandeur invalide
registrationNotAllowedMessage=L''enregistrement n''est pas autoris\u00e9
-resetCredentialNotAllowedMessage=La remise \u00e0 z\u00e9ro n''est pas autoris\u00e9
+resetCredentialNotAllowedMessage=La remise \u00e0 z\u00e9ro n''est pas autoris\u00e9e
permissionNotApprovedMessage=La permission n''est pas approuv\u00e9e.
noRelayStateInResponseMessage=Aucun \u00e9tat de relais dans la r\u00e9ponse du fournisseur d''identit\u00e9.
@@ -193,7 +193,7 @@ couldNotObtainTokenMessage=Impossible de r\u00e9cup\u00e9rer le jeton du fournis
unexpectedErrorRetrievingTokenMessage=Erreur inattendue lors de la r\u00e9cup\u00e9ration du jeton provenant du fournisseur d''identit\u00e9.
unexpectedErrorHandlingResponseMessage=Erreur inattendue lors du traitement de la r\u00e9ponse provenant du fournisseur d''identit\u00e9.
identityProviderAuthenticationFailedMessage=L''authentification a \u00e9chou\u00e9e. Impossible de s''authentifier avec le fournisseur d''identit\u00e9.
-identityProviderDifferentUserMessage=Authentifi\u00e9 en tant que {0}, alors que l'authentification aurait du \u00eatre {1}
+identityProviderDifferentUserMessage=Authentifi\u00e9 en tant que {0}, alors que l''authentification aurait du \u00eatre {1}
couldNotSendAuthenticationRequestMessage=Impossible d''envoyer la requ\u00eate d''authentification vers le fournisseur d''identit\u00e9.
unexpectedErrorHandlingRequestMessage=Erreur inattendue lors du traitement de la requ\u00eate vers le fournisseur d''identit\u00e9.
invalidAccessCodeMessage=Code d''acc\u00e8s invalide.
@@ -207,8 +207,8 @@ identityProviderNotUniqueMessage=Ce domaine autorise plusieurs fournisseurs d''i
emailVerifiedMessage=Votre courriel a \u00e9t\u00e9 v\u00e9rifi\u00e9.
backToApplication=« Revenir \u00e0 l''application
-missingParameterMessage=Param\u00e8tres manquants\: {0}
+missingParameterMessage=Param\u00e8tres manquants \: {0}
clientNotFoundMessage=Client inconnu.
-invalidParameterMessage=Param\u00e8tre invalide\: {0}
-alreadyLoggedIn=You are already logged in.
+invalidParameterMessage=Param\u00e8tre invalide \: {0}
+alreadyLoggedIn=Vous \u00eates d\u00e9j\u00e0 connect\u00e9.
diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_it.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_it.properties
index 2795c0c..92c6355 100755
--- a/themes/src/main/resources-community/theme/base/login/messages/messages_it.properties
+++ b/themes/src/main/resources-community/theme/base/login/messages/messages_it.properties
@@ -2,49 +2,49 @@ doLogIn=Accedi
doRegister=Registrati
doCancel=Annulla
doSubmit=Invia
-doYes=Si
+doYes=S\u00ec
doNo=No
-doAccept=Accept
-doDecline=Decline
-doContinue=Continue
-doForgotPassword=Password Dimenticata?
+doContinue=Continua
+doAccept=Accetta
+doDecline=Nega
+doForgotPassword=Password dimenticata?
doClickHere=Clicca qui
doImpersonate=Impersonate
-bypassKerberos=Your browser is not set up for Kerberos login. Please click continue to login in through other means
-kerberosNotSetUp=Kerberos is not set up. You cannot login.
-kerberosNotConfigured=Kerberos Not Configured
-kerberosNotConfiguredTitle=Kerberos Not Configured
-recaptchaFailed=Invalid Recaptcha
-recaptchaNotConfigured=Recaptcha is required, but not configured
-consentDenied=Consent denied.
-
-registerWithTitle=Registrati come {0}
+kerberosNotConfigured=Kerberos non configurato
+kerberosNotConfiguredTitle=Kerberos non configurato
+bypassKerberosDetail=O non sei connesso via Kerberos o il tuo browser non supporta l''autenticazione a Kerberos. Fai clic su Continua per accedere in modo alternativo
+kerberosNotSetUp=Kerberos non \u00e8 configurato. Non puoi effettuare l''accesso.
+registerWithTitle=Registrati con {0}
registerWithTitleHtml={0}
loginTitle=Accedi a {0}
loginTitleHtml={0}
+impersonateTitle={0} Impersonate Utente
+impersonateTitleHtml=<strong>{0}</strong> Impersonate Utente</strong>
+realmChoice=Realm
+unknownUser=Utente Sconosciuto
loginTotpTitle=Configura Autenticazione Mobile
loginProfileTitle=Aggiorna Profilo
-loginTimeout=You took too long to login. Login process starting from beginning.
-impersonateTitle={0} Impersonate User
-impersonateTitleHtml=<strong>{0}</strong> Impersonate User</strong>
-unknownUser=Unknown user
-realmChoice=Realm
-oauthGrantTitle=OAuth Grant
+loginTimeout=Stai impiegando troppo tempo per accedere. Il processo di autenticazione verr\u00e0 riniziato di nuovo.
+oauthGrantTitle=Autenticazione concessa
oauthGrantTitleHtml={0}
-errorTitle=Siamo spiacenti...
+errorTitle=Siamo spiacenti\u2026
errorTitleHtml=Siamo <strong>spiacenti</strong> ...
-emailVerifyTitle=Verifica Email
+emailVerifyTitle=Verifica l''email
emailForgotTitle=Password Dimenticata?
-updatePasswordTitle=Modifica Password
-codeSuccessTitle=Codice di Successo
-codeErrorTitle=Codice di Errore\: {0}
+updatePasswordTitle=Aggiorna password
+codeSuccessTitle=Codice di successo
+codeErrorTitle=Codice di errore\: {0}
-noAccount=Nuovo Utente?
+recaptchaFailed=Recaptcha non valido
+recaptchaNotConfigured=Il Recaptcha \u00e8 obbligatorio, ma non configurato
+consentDenied=Permesso negato.
+
+noAccount=Nuovo utente?
username=Username
usernameOrEmail=Username o email
firstName=Nome
givenName=Nome
-fullName=Nome Completo
+fullName=Nome completo
lastName=Cognome
familyName=Cognome
email=Email
@@ -56,12 +56,12 @@ rememberMe=Ricordami
authenticatorCode=Codice One-time
address=Indirizzo
street=Via
-locality=Citta'' o Localita''
+locality=Citt\u00e0 o Localit\u00e0
region=Stato, Provincia, o Regione
postal_code=Cap
country=Paese
emailVerified=Email verificata
-gssDelegationCredential=credenziali gss delegation
+gssDelegationCredential=Credenziali GSS Delegation
loginTotpStep1=Installa <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> sul tuo dispositivo mobile
loginTotpStep2=Apri l''applicazione e scansione il barcode o scrivi la chiave
@@ -71,123 +71,146 @@ loginTotpOneTime=Codice one-time
oauthGrantRequest=Vuoi assegnare questi privilegi di accesso?
inResource=per
-emailVerifyInstruction1=Ti e'' stata inviata una email con le istruzioni per la verifica della tua email.
+emailVerifyInstruction1=Ti \u00e8 stata inviata una email con le istruzioni per la verifica della tua email.
emailVerifyInstruction2=Non hai ricevuto un codice di verifica nella tua email?
-emailVerifyInstruction3=per reinviare la mail.
+emailVerifyInstruction3=per rinviare la email.
+
+emailLinkIdpTitle=Link {0}
+emailLinkIdp1=Ti \u00e8 stata inviata una email con le istruzioni per associare {0} l''account {1} con il tuo {2} account.
+emailLinkIdp2=Non hai ricevuto un codice di verifica nella tua email?
+emailLinkIdp3=Per rinviare la email.
backToLogin=« Torna al Login
-temporaryEmailCode=Temporary Email Code
-emailInstruction=Scrivi il tuo username o indirizzo email e noi ti invieremo le istruzioni per creare una nuova password.
-validateResetEmailInstruction=You have just been sent an email. Clicking on the URL in the email will allow you to reset credentials and log in. Alternatively, you can manually enter in the temporary code provided in the email in the textbox to the left and hit submit.
+emailInstruction=Inserisci la tua username o l''indirizzo email e ti manderemo le istruzioni per creare una nuova password.
-copyCodeInstruction=Copiaquesto codice e incollalo nella tua applicazione:
+copyCodeInstruction=Copia questo codice e incollalo nella tua applicazione:
personalInfo=Informazioni personali:
-
role_admin=Admin
role_realm-admin=Realm Admin
role_create-realm=Crea realm
+role_create-client=Crea client
role_view-realm=Visualizza realm
role_view-users=Visualizza utenti
role_view-applications=Visualizza applicazioni
-role_view-clients=Visualizza client
+role_view-clients=Visualizza clients
role_view-events=Visualizza eventi
-role_view-identity-providers=Visualizza identity provider
+role_view-identity-providers=Visualizza identity providers
role_manage-realm=Gestisci realm
role_manage-users=Gestisci utenti
role_manage-applications=Gestisci applicazioni
-role_manage-identity-providers=Gestisci identity provider
-role_manage-clients=Gestisci client
+role_manage-identity-providers=Gestisci identity providers
+role_manage-clients=Gestisci clients
role_manage-events=Gestisci eventi
role_view-profile=Visualizza profilo
role_manage-account=Gestisci account
+role_read-token=Leggi il token
+role_offline-access=Accesso offline
+client_account=Account
+client_security-admin-console=Security Admin Console
+client_admin-cli=Admin CLI
+client_realm-management=Realm Management
+client_broker=Broker
invalidUserMessage=Username o password non valida.
invalidEmailMessage=Indirizzo email non valido.
-accountDisabledMessage=Account disabilitato, contatta l''admin.
-accountTemporarilyDisabledMessage=Account temporaneamente disabilitato, contatta l''admin o riprova piu'' tardi.
-expiredCodeMessage=Login timeout. Effettua login di nuovo.
-
-missingFirstNameMessage=Inserisci il nome.
-missingLastNameMessage=Inserisci il cognome.
-missingEmailMessage=Inserisci l''indirizzo email.
-missingUsernameMessage=Inserisci lo username.
-missingPasswordMessage=Inserisci la password.
-missingTotpMessage=Inserisci il codice di autenticazione.
-notMatchPasswordMessage=Le passwords non coincidono.
+accountDisabledMessage=L''account \u00e8 stato disabilitato, contatta l''amministratore.
+accountTemporarilyDisabledMessage=L''account \u00e8 stato temporaneamente disabilitato, contatta l''amministratore o riprova pi\u00f9 tardi.
+expiredCodeMessage=Login scaduto. Effettua nuovamente l''accesso.
+
+missingFirstNameMessage=Inserisci nome.
+missingLastNameMessage=Inserisci cognome.
+missingEmailMessage=Inserisci email.
+missingUsernameMessage=Inserisci username.
+missingPasswordMessage=Inserisci password.
+missingTotpMessage=Inserisci codice di autenticazione.
+notMatchPasswordMessage=Le password non coincidono.
invalidPasswordExistingMessage=Password esistente non valida.
invalidPasswordConfirmMessage=La password di conferma non coincide.
-invalidTotpMessage=Codice di autenticazione non valido.
+invalidTotpMessage=Codice di autenticazione invalido.
+
+usernameExistsMessage=Username gi\u00e0 esistente.
+emailExistsMessage=Email gi\u00e0 esistente.
-usernameExistsMessage=Username gia'' esistente.
-emailExistsMessage=Email gia'' esistente.
+federatedIdentityExistsMessage=L''utente con {0} {1} esiste gi\u00e0. Effettua il login nella gestione account per associare l''account.
-federatedIdentityEmailExistsMessage=Utente con email gia'' esistente. Effettua il login nella gestione account per associare l''account.
-federatedIdentityUsernameExistsMessage=Utente con username gia'' esistente.Effettua il login nella gestione account per associare l''account.
+confirmLinkIdpTitle=Account gi\u00e0 esistente
+federatedIdentityConfirmLinkMessage=L''utente con {0} {1} esiste gi\u00e0. Come vuoi procedere?
+federatedIdentityConfirmReauthenticateMessage=Autenticati come {0} per associare il tuo account con {1}
+confirmLinkIdpReviewProfile=Rivedi profilo
+confirmLinkIdpContinue=Aggiungi all''account esistente
configureTotpMessage=Devi impostare un Mobile Authenticator per attivare il tuo account.
-updateProfileMessage=Devi aggiornare il tuo profilo utente per attivare il tuo account.
-updatePasswordMessage=Devi cambiare la password per attivare il tuo account.
-verifyEmailMessage=Devi verificare il tuo indirizzo email per attivare il tuo account.
+updateProfileMessage=Devi aggiornare il tuo profilo utente per attivare il tuo account.
+updatePasswordMessage=Devi cambiare la password per attivare il tuo account.
+verifyEmailMessage=Devi verificare il tuo indirizzo email per attivare il tuo account.
+linkIdpMessage=Devi verificare il tuo indirizzo email per associare il tuo account con {0}.
emailSentMessage=Riceverai a breve una email con maggiori istruzioni.
-emailSendErrorMessage=Invio email fallito, riptoav piu'' tardi.
+emailSendErrorMessage=Invio email fallito, riprova pi\u00f9 tardi.
-accountUpdatedMessage=Il tuo account e'' stato aggiornato.
-accountPasswordUpdatedMessage=La tua password e'' stata aggiornata.
+accountUpdatedMessage=Il tuo account \u00e8 stato aggiornato.
+accountPasswordUpdatedMessage=La tua password \u00e8 stata aggiornata.
noAccessMessage=Nessun accesso
invalidPasswordMinLengthMessage=Password non valida: lunghezza minima {0}.
invalidPasswordMinDigitsMessage=Password non valida: deve contenere almeno {0} numeri.
invalidPasswordMinLowerCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri minuscoli.
-invalidPasswordMinUpperCaseCharsMessage=Password non valida: deve contenere almeno {0} caratteri maiuscoli.
-invalidPasswordMinSpecialCharsMessage=Password non valida: deve contenere almeno {0} caratteri speciali.
-invalidPasswordNotUsernameMessage=Password non valida: non deve essere uguale allo username.
-invalidPasswordRegexPatternMessage=Password non valida: fallito il match con una o piu'' espressioni regolari.
-invalidPasswordHistoryMessage=Password non valida: non deve ssere uguale ad una delle ultime {0} password.
+invalidPasswordMinUpperCaseCharsMessage= Password non valida: deve contenere almeno {0} caratteri maiuscoli.
+invalidPasswordMinSpecialCharsMessage= Password non valida: deve contenere almeno {0} caratteri speciali.
+invalidPasswordNotUsernameMessage=Password non valida: non deve essere uguale alla username.
+invalidPasswordRegexPatternMessage=Password non valida: fallito il match con una o pi\u00f9 espressioni regolari.
+invalidPasswordHistoryMessage=Password non valida: non deve essere uguale ad una delle ultime {0} password.
+invalidPasswordGenericMessage=Password non valida: la nuova password non rispetta le indicazioni previste.
failedToProcessResponseMessage=Fallimento nell''elaborazione della risposta
httpsRequiredMessage=HTTPS richiesto
realmNotEnabledMessage=Realm non abilitato
invalidRequestMessage=Richiesta non valida
-failedLogout=Logout failed
+failedLogout=Logout fallito
unknownLoginRequesterMessage=Richiedente di Login non riconosciuto
loginRequesterNotEnabledMessage=Richiedente di Login non abilitato
-bearerOnlyMessage=Alle applicazioni di tipo Bearer-only non e'' consentito di effettuare il login tramite browser
-directGrantsOnlyMessage=Ai client di tipo Direct-grants-only non e'' consentito di effettuare il login tramite browser
+bearerOnlyMessage=Alle applicazioni di tipo Bearer-only non \u00e8 consentito di effettuare il login tramite browser
+standardFlowDisabledMessage=Al Client non \u00e8 consentito di effettuare il login tramite browser con dato response_type. Standard flow \u00e8 stato disabilitato per il client.
+implicitFlowDisabledMessage=Al Client non \u00e8 consentito di effettuare il login tramite browser con dato response_type. Implicit flow \u00e8 stato disabilitato per il client.
invalidRedirectUriMessage=Redirect uri non valido
unsupportedNameIdFormatMessage=NameIDFormat non supportato
invalidRequesterMessage=Richiedente non valido
registrationNotAllowedMessage=Registrazione non permessa
-resetCredentialNotAllowedMessage=Reset Credential not allowed
-
+resetCredentialNotAllowedMessage=Reimpostazione della credenziale non permessa
permissionNotApprovedMessage=Permesso non approvato.
noRelayStateInResponseMessage=Nessun relay state in risposta dall''identity provider.
identityProviderAlreadyLinkedMessage=L''identita'' restituita dall''identity provider e'' gia'' associata ad un altro utente.
-insufficientPermissionMessage=Permessi insufficienti per associare le identita''.
-couldNotProceedWithAuthenticationRequestMessage=Non posso procedere con la richiesta di autenticazione all''identity provider.
-couldNotObtainTokenMessage=Non posso ottenere un token dall''identity provider.
-unexpectedErrorRetrievingTokenMessage=Errore inaspettato nella gestione del token dall''identity provider.
+insufficientPermissionMessage=Permessi insufficienti per associare le identit\u00e0.
+couldNotProceedWithAuthenticationRequestMessage=Impossibile procedere con la richiesta di autenticazione all''identity provider
+couldNotObtainTokenMessage=Non posso ottenere un token dall''identity provider.
+unexpectedErrorRetrievingTokenMessage=Errore inaspettato nel recupero del token dall''identity provider.
unexpectedErrorHandlingResponseMessage=Errore inaspettato nella gestione della risposta dall''identity provider.
identityProviderAuthenticationFailedMessage=Autenticazione fallita. Non posso effettuare l''autenticazione con l''identity provider.
-couldNotSendAuthenticationRequestMessage=Non posso inviare la richiesta di autenticazione all''identity provider.
-unexpectedErrorHandlingRequestMessage=Errore imprevisto durante l''autenticazione con identity provider.
+identityProviderDifferentUserMessage=Autenticato come {0}, ma previsto per essere autenticato come {1}
+couldNotSendAuthenticationRequestMessage=Impossibile inviare la richiesta di autenticazione all''identity provider.
+unexpectedErrorHandlingRequestMessage=Errore inaspettato nella gestione della richiesta di autenticazione all''identity provider.
invalidAccessCodeMessage=Codice di accesso non valido.
sessionNotActiveMessage=Sessione non attiva.
-invalidCodeMessage=Si e'' verificato un errore, per piacere effettua di nuovo il login nella tua applicazione.
+invalidCodeMessage=Si \u00e8 verificato un errore, effettua di nuovo il login nella tua applicazione.
identityProviderUnexpectedErrorMessage=Errore imprevisto durante l''autenticazione con identity provider
identityProviderNotFoundMessage=Non posso trovare un identity provider con l''identificativo.
+identityProviderLinkSuccess=Il tuo account \u00e8 stato associato con successo con {0} l''account {1} .
+staleCodeMessage=Questa pagina non \u00e8 pi\u00f9 valida, torna alla tua applicazione ed effettua nuovamente l''accesso
realmSupportsNoCredentialsMessage=Il Realm non supporta nessun tipo di credenziali.
-identityProviderNotUniqueMessage=Il Realm supporta piu'' di un identity provider. Non posso determinare quale identity provider con il quale autenticarti.
-emailVerifiedMessage=Il tuo indirizzo email e'' stato verificato.
+identityProviderNotUniqueMessage=Il Realm supporta pi\u00f9 di un identity provider. Impossibile determinare quale identity provider deve essere utilizzato per autenticarti.
+emailVerifiedMessage=Il tuo indirizzo email \u00e8 stato verificato.
+staleEmailVerificationLink=Il link che hai cliccato \u00e8 un link scaduto e non \u00e8 pi\u00f9 valido. Forse hai gi\u00e0 verificato la tua email?
-backToApplication=« Torna all''Applicazione
-missingParameterMessage=Parametri Mancanti\: {0}
+backToApplication=« Torna all''applicazione
+missingParameterMessage=Parametri mancanti\: {0}
clientNotFoundMessage=Client non trovato.
+clientDisabledMessage=Client disabilitato.
invalidParameterMessage=Parametro non valido\: {0}
-alreadyLoggedIn=You are already logged in.
+alreadyLoggedIn=Sei gi\u00e0 connesso.
+p3pPolicy=CP="Questa non \u00e8 una P3P policy!"
diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_ru.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_ru.properties
index be6ce7c..0d3d31a 100644
--- a/themes/src/main/resources-community/theme/base/login/messages/messages_ru.properties
+++ b/themes/src/main/resources-community/theme/base/login/messages/messages_ru.properties
@@ -1,218 +1,220 @@
-doLogIn=\u0412\u0445\u043E\u0434
-doRegister=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F
-doCancel=\u041E\u0442\u043C\u0435\u043D\u0430
-doSubmit=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C
-doYes=\u0414\u0430
-doNo=\u041D\u0435\u0442
-doContinue=\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C
-doAccept=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C
-doDecline=\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C
-doForgotPassword=\u0417\u0430\u0431\u044B\u043B\u0438 \u043F\u0430\u0440\u043E\u043B\u044C?
-doClickHere=\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u0441\u044E\u0434\u0430
-doImpersonate=\u0418\u043C\u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C\u0441\u044F
-kerberosNotConfigured=Kerberos \u043D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D
-kerberosNotConfiguredTitle=Kerberos \u043D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D
-bypassKerberosDetail=\u041B\u0438\u0431\u043E \u0432\u044B \u043D\u0435 \u0432\u043E\u0448\u043B\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E Kerberos, \u043B\u0438\u0431\u043E \u0432\u0430\u0448 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D \u0434\u043B\u044F \u0432\u0445\u043E\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 Kerberos. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u043A\u043D\u043E\u043F\u043A\u0443 '\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C' \u0434\u043B\u044F \u0432\u0445\u043E\u0434\u0430 \u0432 \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u0440\u0435\u0434\u0441\u0442\u0432
-kerberosNotSetUp=Kerberos \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D. \u0412\u044B \u043D\u0435 \u043C\u043E\u0436\u0435\u0442\u0435 \u0432\u043E\u0439\u0442\u0438.
-registerWithTitle=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u0441 {0}
+# encoding: utf-8
+doLogIn=Вход
+doRegister=Регистрация
+doCancel=Отмена
+doSubmit=Подтвердить
+doYes=Да
+doNo=Нет
+doContinue=Продолжить
+doAccept=Подтвердить
+doDecline=Отменить
+doForgotPassword=Забыли пароль?
+doClickHere=Нажмите сюда
+doImpersonate=Имперсонализироваться
+kerberosNotConfigured=Kerberos не сконфигурирован
+kerberosNotConfiguredTitle=Kerberos не сконфигурирован
+bypassKerberosDetail=Либо вы не вошли в систему с помощью Kerberos, либо ваш браузер не настроен для входа в систему Kerberos. Пожалуйста, нажмите кнопку 'Продолжить' для входа в с помощью других средств
+kerberosNotSetUp=Kerberos не настроен. Вы не можете войти.
+registerWithTitle=Зарегистрироваться с {0}
registerWithTitleHtml={0}
loginTitle=Log in to {0}
loginTitleHtml={0}
-impersonateTitle={0} \u0418\u043C\u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-impersonateTitleHtml=<strong>{0}</strong> \u0418\u043C\u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F</strong>
+impersonateTitle={0} Имперсонализация пользователя
+impersonateTitleHtml=<strong>{0}</strong> Имперсонализация пользователя</strong>
realmChoice=Realm
-unknownUser=\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C
-loginTotpTitle=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430
-loginProfileTitle=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u0438 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438
-loginTimeout=\u0412\u044B \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0434\u043E\u043B\u0433\u043E \u0431\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u043E\u0432\u0430\u043B\u0438. \u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043D\u0430\u0447\u043D\u0435\u0442\u0441\u044F \u0441 \u043D\u0430\u0447\u0430\u043B\u0430.
-oauthGrantTitle=\u0421\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F
+unknownUser=Неизвестный пользователь
+loginTotpTitle=Настройка мобильного аутентификатора
+loginProfileTitle=Обновление информации учетной записи
+loginTimeout=Вы слишком долго бездействовали. Процесс аутентификации начнется с начала.
+oauthGrantTitle=Согласовать доступ
oauthGrantTitleHtml={0}
-errorTitle=\u041C\u044B \u0441\u043E\u0436\u0430\u043B\u0435\u0435\u043C...
-errorTitleHtml=\u041C\u044B <strong>\u0441\u043E\u0436\u0430\u043B\u0435\u0435\u043C</strong> ...
-emailVerifyTitle=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0430\u0434\u0440\u0435\u0441\u0430 email
-emailForgotTitle=\u0417\u0430\u0431\u044B\u043B\u0438 \u043F\u0430\u0440\u043E\u043B\u044C?
-updatePasswordTitle=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F
-codeSuccessTitle=\u0423\u0441\u043F\u0435\u0448\u043D\u044B\u0439 \u043A\u043E\u0434
-codeErrorTitle=\u041E\u0448\u0438\u0431\u043E\u0447\u043D\u044B\u0439 \u043A\u043E\u0434\: {0}
-
-termsTitle=\u0423\u0441\u043B\u043E\u0432\u0438\u044F \u0438 \u043F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u044F
-termsTitleHtml=\u0423\u0441\u043B\u043E\u0432\u0438\u044F \u0438 \u043F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u044F
-termsText=<p>\u0423\u0441\u043B\u043E\u0432\u0438\u044F \u0438 \u043F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0434\u043E\u043B\u0436\u043D\u044B \u0431\u044B\u0442\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u044B</p>
-
-recaptchaFailed=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u0430\u044F Recaptcha
-recaptchaNotConfigured=Recaptcha \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F, \u043D\u043E \u043D\u0435 \u0441\u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u0430
-consentDenied=\u0412 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u0438\u0438 \u043E\u0442\u043A\u0430\u0437\u0430\u043D\u043E.
-
-noAccount=\u041D\u043E\u0432\u044B\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C?
-username=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
-usernameOrEmail=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0438\u043B\u0438 Email
-firstName=\u0418\u043C\u044F
-givenName=\u0412\u044B\u0434\u0430\u043D\u043D\u043E\u0435 \u0438\u043C\u044F
-fullName=\u041F\u043E\u043B\u043D\u043E\u0435 \u0438\u043C\u044F
-lastName=\u0424\u0430\u043C\u0438\u043B\u0438\u044F
-familyName=\u0424\u0430\u043C\u0438\u043B\u0438\u044F
-email=email
-password=\u041F\u0430\u0440\u043E\u043B\u044C
-passwordConfirm=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F
-passwordNew=\u041D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C
-passwordNewConfirm=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u0430\u0440\u043E\u043B\u044F
-rememberMe=\u0417\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u044C \u043C\u0435\u043D\u044F
-authenticatorCode=\u041E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043A\u043E\u0434
-address=\u0410\u0434\u0440\u0435\u0441
-street=\u0423\u043B\u0438\u0446\u0430
-locality=\u0413\u043E\u0440\u043E\u0434
-region=\u0420\u0435\u0433\u0438\u043E\u043D
-postal_code=\u041F\u043E\u0447\u0442\u043E\u0432\u044B\u0439 \u0438\u043D\u0434\u0435\u043A\u0441
-country=\u0421\u0442\u0440\u0430\u043D\u0430
-emailVerified=Email \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D
-gssDelegationCredential=\u0414\u0435\u043B\u0435\u0433\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 GSS
-
-loginTotpStep1=\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> \u0438\u043B\u0438 Google Authenticator. \u041E\u0431\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0432 <a href="https://play.google.com">Google Play</a> \u0438 Apple App Store.
-loginTotpStep2=\u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0438 \u043F\u0440\u043E\u0441\u043A\u0430\u043D\u0438\u0440\u0443\u0439\u0442\u0435 \u0431\u0430\u0440\u043A\u043E\u0434, \u043B\u0438\u0431\u043E \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043B\u044E\u0447
-loginTotpStep3=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C, \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435\u043C, \u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0434\u043B\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438
-loginTotpOneTime=\u041E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C
-
-oauthGrantRequest=\u0412\u044B \u0441\u043E\u0433\u043B\u0430\u0441\u0443\u0435\u0442\u0435 \u0434\u043E\u0441\u0442\u0443\u043F \u043A \u044D\u0442\u0438\u043C \u043F\u0440\u0438\u0432\u0435\u043B\u0435\u0433\u0438\u044F\u043C?
-inResource=\u0432
-
-emailVerifyInstruction1=\u0412\u0430\u043C \u0431\u044B\u043B\u043E \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E \u043F\u0438\u0441\u044C\u043C\u043E \u0441 \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044F\u043C\u0438 \u0434\u043B\u044F \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u0430\u0434\u0440\u0435\u0441\u0430 email.
-emailVerifyInstruction2=\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0438 \u043F\u0438\u0441\u044C\u043C\u043E \u0441 \u043A\u043E\u0434\u043E\u043C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F?
-emailVerifyInstruction3=\u0434\u043B\u044F \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0439 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u0438\u0441\u044C\u043C\u0430.
-
-emailLinkIdpTitle=\u0421\u0432\u044F\u0437\u0430\u0442\u044C {0}
-emailLinkIdp1=\u0412\u0430\u043C \u0431\u044B\u043B\u043E \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E \u043F\u0438\u0441\u044C\u043C\u043E \u0441 \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044F\u043C\u0438 \u043F\u043E \u043E\u0431\u044A\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044E {0} \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 {1} \u0441 \u0432\u0430\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E {2}.
-emailLinkIdp2=\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0438 \u043A\u043E\u0434 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u043D\u0430 \u0432\u0430\u0448 email?
-emailLinkIdp3=\u0434\u043B\u044F \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0439 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u0438\u0441\u044C\u043C\u0430.
-
-backToLogin=« \u041D\u0430\u0437\u0430\u0434 \u043A\u043E \u0432\u0445\u043E\u0434\u0443
-
-emailInstruction=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0412\u0430\u0448\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0438\u043B\u0438 email \u0438 \u043C\u044B \u0432\u044B\u0448\u043B\u0435\u043C \u0412\u0430\u043C \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u0438 \u043F\u043E \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u044E \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u0430\u0440\u043E\u043B\u044F.
-
-copyCodeInstruction=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u043A\u043E\u043F\u0438\u0440\u0443\u0439\u0442\u0435 \u044D\u0442\u043E\u0442 \u043A\u043E\u0434 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435:
-
-personalInfo=\u041F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F:
-role_admin=\u0410\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440
-role_realm-admin=\u0410\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440 realm
-role_create-realm=\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 realm
-role_create-client=\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430
-role_view-realm=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 realm
-role_view-users=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439
-role_view-applications=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0439
-role_view-clients=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043A\u043B\u0438\u0435\u043D\u0442\u043E\u0432
-role_view-events=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u0441\u043E\u0431\u044B\u0442\u0438\u0439
-role_view-identity-providers=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u0432 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439
-role_manage-realm=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 realm
-role_manage-users=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F\u043C\u0438
-role_manage-applications=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u043C\u0438
-role_manage-identity-providers=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043C\u0438 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439
-role_manage-clients=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0430\u043C\u0438
-role_manage-events=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u044F\u043C\u0438
-role_view-profile=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043F\u0440\u043E\u0444\u0438\u043B\u044F
-role_manage-account=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E
-role_read-token=\u0427\u0442\u0435\u043D\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u0430
-role_offline-access=\u041E\u0444\u0444\u043B\u0430\u0439\u043D \u0434\u043E\u0441\u0442\u0443\u043F
-client_account=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C
-client_security-admin-console=\u041A\u043E\u043D\u0441\u043E\u043B\u044C \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438
-client_admin-cli=\u041A\u043E\u043C\u0430\u043D\u0434\u043D\u044B\u0439 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430
-client_realm-management=\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 realm
-client_broker=\u0411\u0440\u043E\u043A\u0435\u0440
-
-invalidUserMessage=\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0438\u043B\u0438 \u043F\u0430\u0440\u043E\u043B\u044C.
-invalidEmailMessage=\u041D\u0435\u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u044B\u0439 email.
-accountDisabledMessage=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C.
-accountTemporarilyDisabledMessage=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C \u0438\u043B\u0438 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043F\u043E\u0437\u0436\u0435.
-expiredCodeMessage=\u0412\u0445\u043E\u0434 \u043F\u0440\u043E\u0441\u0440\u043E\u0447\u0435\u043D \u043F\u043E \u0442\u0430\u0439\u043C\u0430\u0443\u0442\u0443. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0441\u043D\u043E\u0432\u0430.
-
-missingFirstNameMessage=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043C\u044F.
-missingLastNameMessage=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0444\u0430\u043C\u0438\u043B\u0438\u044E.
-missingEmailMessage=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 email.
-missingUsernameMessage=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-missingPasswordMessage=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C.
-missingTotpMessage=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0434 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430.
-notMatchPasswordMessage=\u041F\u0430\u0440\u043E\u043B\u0438 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u044E\u0442.
-
-invalidPasswordExistingMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0439 \u043F\u0430\u0440\u043E\u043B\u044C.
-invalidPasswordConfirmMessage=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0430\u0440\u043E\u043B\u044F \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442.
-invalidTotpMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043A\u043E\u0434 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430.
-
-usernameExistsMessage=\u0418\u043C\u044F \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u0442\u043E.
-emailExistsMessage=Email \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.
-
-federatedIdentityExistsMessage=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0441 {0} {1} \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430 \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u043C\u0438 \u0437\u0430\u043F\u0438\u0441\u044F\u043C\u0438, \u0447\u0442\u043E\u0431\u044B \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u044D\u0442\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C.
-
-confirmLinkIdpTitle=\u0423\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442
-federatedIdentityConfirmLinkMessage=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u0441 {0} {1} \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0443\u0435\u0442. \u0425\u043E\u0442\u0438\u0442\u0435 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C?
-federatedIdentityConfirmReauthenticateMessage=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0439\u0442\u0435\u0441\u044C \u043A\u0430\u043A {0} \u0434\u043B\u044F \u0442\u043E\u0433\u043E, \u0447\u0442\u043E\u0431\u044B \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u0412\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C \u0441 {1}
-confirmLinkIdpReviewProfile=\u041E\u0431\u0437\u043E\u0440 \u043F\u0440\u043E\u0444\u0438\u043B\u044F
-confirmLinkIdpContinue=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0443\u044E \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C
-
-configureTotpMessage=\u0412\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u043D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0432 \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u043C \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0435, \u0447\u0442\u043E\u0431\u044B \u0430\u043A\u0442\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C.
-updateProfileMessage=\u0412\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0432\u043E\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C, \u0447\u0442\u043E\u0431\u044B \u0430\u043A\u0442\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0412\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C.
-updatePasswordMessage=\u0412\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C, \u0447\u0442\u043E\u0431\u044B \u0430\u043A\u0442\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0412\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C.
-verifyEmailMessage=\u0412\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u0412\u0430\u0448 email, \u0447\u0442\u043E\u0431\u044B \u0430\u043A\u0442\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0412\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C.
-linkIdpMessage=\u0412\u0430\u043C \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u0412\u0430\u0448 email, \u0447\u0442\u043E\u0431\u044B \u0441\u0432\u044F\u0437\u0430\u0442\u044C \u0412\u0430\u0448\u0443 \u0443\u0447\u0435\u0442\u043D\u0443\u044E \u0437\u0430\u043F\u0438\u0441\u044C \u0441 {0}.
-
-emailSentMessage=\u0412 \u0431\u043B\u0438\u0436\u0430\u0439\u0448\u0435\u0435 \u0432\u0440\u0435\u043C\u044F \u0412\u044B \u0434\u043E\u043B\u0436\u043D\u044B \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043F\u0438\u0441\u044C\u043C\u043E \u0441 \u0434\u0430\u043B\u044C\u043D\u0435\u0439\u0448\u0438\u043C\u0438 \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044F\u043C\u0438.
-emailSendErrorMessage=\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442\u0441\u044F \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u043F\u0438\u0441\u044C\u043C\u043E. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u043E\u0437\u0436\u0435.
-
-accountUpdatedMessage=\u0412\u0430\u0448\u0430 \u0443\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430.
-accountPasswordUpdatedMessage=\u0412\u0430\u0448 \u043F\u0430\u0440\u043E\u043B\u044C \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D.
-
-noAccessMessage=\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430
-
-invalidPasswordMinLengthMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430 {0}.
-invalidPasswordMinDigitsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0446\u0438\u0444\u0440\u043E\u0432\u044B\u0445 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinLowerCaseCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u0432 \u043D\u0438\u0436\u043D\u0435\u043C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinUpperCaseCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u0432 \u0432\u0435\u0440\u0445\u043D\u0435\u043C \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordMinSpecialCharsMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0441\u043F\u0435\u0446\u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 \u043D\u0435 \u043C\u0435\u043D\u0435\u0435 {0}.
-invalidPasswordNotUsernameMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0442\u044C \u0441 \u0438\u043C\u0435\u043D\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F.
-invalidPasswordRegexPatternMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043D\u0435 \u043F\u0440\u043E\u0448\u0435\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u044B\u043C \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435\u043C.
-invalidPasswordHistoryMessage=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C: \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0442\u044C \u0441 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C\u0438 {0} \u043F\u0430\u0440\u043E\u043B\u044F\u043C\u0438.
-
-failedToProcessResponseMessage=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C \u043E\u0442\u0432\u0435\u0442
-httpsRequiredMessage=\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F HTTPS
-realmNotEnabledMessage=Realm \u043D\u0435 \u0432\u043A\u043B\u044E\u0447\u0435\u043D
-invalidRequestMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441
-failedLogout=\u0412\u044B\u0439\u0442\u0438 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C
-unknownLoginRequesterMessage=\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u043A\u043B\u0438\u0435\u043D\u0442
-loginRequesterNotEnabledMessage=\u041A\u043B\u0438\u0435\u043D\u0442 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D
-bearerOnlyMessage=Bearer-only \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u043C \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044F \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u0432\u0445\u043E\u0434\u0430 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440
-standardFlowDisabledMessage=\u041A\u043B\u0438\u0435\u043D\u0442\u0443 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044F \u0438\u043D\u0438\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0445\u043E\u0434 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441 \u0434\u0430\u043D\u043D\u044B\u043C response_type. Standard flow \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-implicitFlowDisabledMessage=\u041A\u043B\u0438\u0435\u043D\u0442\u0443 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044F \u0438\u043D\u0438\u0446\u0438\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0445\u043E\u0434 \u0447\u0435\u0440\u0435\u0437 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0441 \u0434\u0430\u043D\u043D\u044B\u043C response_type. Implicit flow \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043B\u0438\u0435\u043D\u0442\u0430.
-invalidRedirectUriMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 uri \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u0438
-unsupportedNameIdFormatMessage=\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 NameIDFormat
-invalidRequesterMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u044E\u0449\u0438\u0439
-registrationNotAllowedMessage=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0430
-resetCredentialNotAllowedMessage=\u0421\u0431\u0440\u043E\u0441 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D
-
-permissionNotApprovedMessage=\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u043E.
-noRelayStateInResponseMessage=\u041D\u0435\u0442 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-insufficientPermissionMessage=\u041D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u043F\u043E\u043B\u043D\u043E\u043C\u043E\u0447\u0438\u0439 \u0434\u043B\u044F \u0441\u0432\u044F\u0437\u044B\u0432\u0430\u043D\u0438\u044F \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u043E\u0432.
-couldNotProceedWithAuthenticationRequestMessage=\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u043E\u043D\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441 \u0432 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0435 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-couldNotObtainTokenMessage=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0442\u043E\u043A\u0435\u043D \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-unexpectedErrorRetrievingTokenMessage=\u041D\u0435\u043F\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0438 \u0442\u043E\u043A\u0435\u043D\u0430 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-unexpectedErrorHandlingResponseMessage=\u041D\u0435\u043F\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0435 \u043E\u0442\u0432\u0435\u0442\u0430 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-
-identityProviderAuthenticationFailedMessage=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F \u043F\u0440\u043E\u0432\u0430\u043B\u0435\u043D\u0430. \u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u043C \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-identityProviderDifferentUserMessage=\u0410\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D \u043A\u0430\u043A {0}, \u043D\u043E \u043E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F, \u0447\u0442\u043E \u0431\u0443\u0434\u0435\u0442 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043E\u0432\u0430\u043D \u043A\u0430\u043A {1}
-couldNotSendAuthenticationRequestMessage=\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442\u0441\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0437\u0430\u043F\u0440\u043E\u0441 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043A \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0443 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-unexpectedErrorHandlingRequestMessage=\u041D\u0435\u043F\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0430 \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-invalidAccessCodeMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043A\u043E\u0434 \u0434\u043E\u0441\u0442\u0443\u043F\u0430.
-sessionNotActiveMessage=\u0421\u0435\u0441\u0441\u0438\u044F \u043D\u0435 \u0430\u043A\u0442\u0438\u0432\u043D\u0430.
-invalidCodeMessage=\u041F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430. \u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u0441\u043D\u043E\u0432\u0430 \u0447\u0435\u0440\u0435\u0437 \u0432\u0430\u0448\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435.
-identityProviderUnexpectedErrorMessage=\u041D\u0435\u043F\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043D\u043D\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430 \u043F\u0440\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0435 \u043F\u043E\u0434\u043B\u0438\u043D\u043D\u043E\u0441\u0442\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439.
-identityProviderNotFoundMessage=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043D\u0430\u0439\u0442\u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u0430 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u0441 \u0434\u0430\u043D\u043D\u044B\u043C \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u043E\u043C.
-identityProviderLinkSuccess=\u0412\u0430\u0448\u0430 \u0443\u0447\u0435\u0442\u043D\u0430\u044F \u0437\u0430\u043F\u0438\u0441\u044C \u0431\u044B\u043B\u0430 \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0430 \u0441 {0} \u0443\u0447\u0435\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u044C\u044E {1} .
-staleCodeMessage=\u042D\u0442\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u0430, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0432\u0435\u0440\u043D\u0438\u0442\u0435\u0441\u044C \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0438 \u0441\u043D\u043E\u0432\u0430 \u0432\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443.
-realmSupportsNoCredentialsMessage=Realm \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043D\u0438\u043A\u0430\u043A\u043E\u0439 \u0442\u0438\u043F \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445.
-identityProviderNotUniqueMessage=Realm \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A\u043E\u0432 \u0443\u0447\u0435\u0442\u043D\u044B\u0445 \u0437\u0430\u043F\u0438\u0441\u0435\u0439. \u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0438\u0442\u044C, \u043A\u0430\u043A\u043E\u0439 \u0438\u043C\u0435\u043D\u043D\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0449\u0438\u043A \u0434\u043E\u043B\u0436\u0435\u043D \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u0434\u043B\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u0438.
-emailVerifiedMessage=\u0412\u0430\u0448 email \u0431\u044B\u043B \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D.
-staleEmailVerificationLink=\u0421\u0441\u044B\u043B\u043A\u0430, \u043F\u043E \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0412\u044B \u043F\u0435\u0440\u0435\u0448\u043B\u0438, \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0430 \u0438 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442. \u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C, \u0432\u044B \u0443\u0436\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043B\u0438 \u0441\u0432\u043E\u0439 email?
-
-backToApplication=« \u041D\u0430\u0437\u0430\u0434 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435
-missingParameterMessage=\u041F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u043D\u044B\u0435 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B\: {0}
-clientNotFoundMessage=\u041A\u043B\u0438\u0435\u043D\u0442 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.
-invalidParameterMessage=\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\: {0}
-alreadyLoggedIn=\u0412\u044B \u0443\u0436\u0435 \u0432\u043E\u0448\u043B\u0438.
-
-p3pPolicy=CP="\u042D\u0442\u043E \u043D\u0435 \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0430 P3P!"
+errorTitle=Мы сожалеем...
+errorTitleHtml=Мы <strong>сожалеем</strong> ...
+emailVerifyTitle=Подтверждение адреса E-mail
+emailForgotTitle=Забыли пароль?
+updatePasswordTitle=Обновление пароля
+codeSuccessTitle=Успешный код
+codeErrorTitle=Ошибочный код\: {0}
+
+termsTitle=Условия и положения
+termsTitleHtml=Условия и положения
+termsText=<p>Условия и положения должны быть определены</p>
+
+recaptchaFailed=Некорректная Recaptcha
+recaptchaNotConfigured=Recaptcha требуется, но не сконфигурирована
+consentDenied=В согласовании отказано.
+
+noAccount=Новый пользователь?
+username=Имя пользователя
+usernameOrEmail=Имя пользователя или E-mail
+firstName=Имя
+givenName=Выданное имя
+fullName=Полное имя
+lastName=Фамилия
+familyName=Фамилия
+email=E-mail
+password=Пароль
+passwordConfirm=Подтверждение пароля
+passwordNew=Новый пароль
+passwordNewConfirm=Подтверждение нового пароля
+rememberMe=Запомнить меня
+authenticatorCode=Одноразовый код
+address=Адрес
+street=Улица
+locality=Город
+region=Регион
+postal_code=Почтовый индекс
+country=Страна
+emailVerified=E-mail подтвержден
+gssDelegationCredential=Делегирование учетных данных GSS
+
+loginTotpStep1=Установите <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> или Google Authenticator. Оба приложения доступны в <a href="https://play.google.com">Google Play</a> и Apple App Store.
+loginTotpStep2=Откройте приложение и просканируйте баркод, либо введите ключ
+loginTotpStep3=Введите одноразовый пароль, выданный приложением, и нажмите сохранить для завершения установки
+loginTotpOneTime=Одноразовый пароль
+
+oauthGrantRequest=Вы согласуете доступ к этим привелегиям?
+inResource=в
+
+emailVerifyInstruction1=Вам было отправлено письмо с инструкциями для подтверждения адреса E-mail.
+emailVerifyInstruction2=Не получили письмо с кодом подтверждения?
+emailVerifyInstruction3=для повторной отправки письма.
+
+emailLinkIdpTitle=Связать {0}
+emailLinkIdp1=Вам было отправлено письмо с инструкциями по объединению {0} учетной записи {1} с вашей учетной записью {2}.
+emailLinkIdp2=Не получили код подтверждения на ваш E-mail?
+emailLinkIdp3=для повторной отправки письма.
+
+backToLogin=« Назад ко входу
+
+emailInstruction=Введите Ваше имя пользователя или E-mail и мы вышлем Вам инструкции по получению нового пароля.
+
+copyCodeInstruction=Пожалуйста, скопируйте этот код в приложение:
+
+personalInfo=Персональная информация:
+role_admin=Администратор
+role_realm-admin=Администратор realm
+role_create-realm=Создание realm
+role_create-client=Создание клиента
+role_view-realm=Просмотр realm
+role_view-users=Просмотр пользователей
+role_view-applications=Просмотр приложений
+role_view-clients=Просмотр клиентов
+role_view-events=Просмотр событий
+role_view-identity-providers=Просмотр провайдеров учетных записей
+role_manage-realm=Управление realm
+role_manage-users=Управление пользователями
+role_manage-applications=Управление приложениями
+role_manage-identity-providers=Управление провайдерами учетных записей
+role_manage-clients=Управление клиентами
+role_manage-events=Управление событиями
+role_view-profile=Просмотр профиля
+role_manage-account=Управление учетной записью
+role_read-token=Чтение токена
+role_offline-access=Оффлайн доступ
+client_account=Учетная запись
+client_security-admin-console=Консоль администратора безопасности
+client_admin-cli=Командный интерфейс администратора
+client_realm-management=Управление realm
+client_broker=Брокер
+
+invalidUserMessage=Неправильное имя пользователя или пароль.
+invalidEmailMessage=Неправильный E-mail.
+accountDisabledMessage=Учетная запись заблокирована, свяжитесь с администратором.
+accountTemporarilyDisabledMessage=Учетная запись временно заблокирована, свяжитесь с администратором или попробуйте позже.
+expiredCodeMessage=Вход просрочен по таймауту. Пожалуйста, войдите снова.
+
+missingFirstNameMessage=Пожалуйста введите имя.
+missingLastNameMessage=Пожалуйста введите фамилию.
+missingEmailMessage=Пожалуйста введите E-mail.
+missingUsernameMessage=Пожалуйста введите имя пользователя.
+missingPasswordMessage=Пожалуйста введите пароль.
+missingTotpMessage=Пожалуйста введите код аутентификатора.
+notMatchPasswordMessage=Пароли не совпадают.
+
+invalidPasswordExistingMessage=Неверный существующий пароль.
+invalidPasswordConfirmMessage=Подтверждение пароля не совпадает.
+invalidTotpMessage=Неверный код аутентификатора.
+
+usernameExistsMessage=Имя пользователя уже занято.
+emailExistsMessage=E-mail уже существует.
+
+federatedIdentityExistsMessage=Пользователь с {0} {1} уже существует. Пожалуйста войдите в управление учетными записями, чтобы связать эту учетную запись.
+
+confirmLinkIdpTitle=Учетная запись уже существует
+federatedIdentityConfirmLinkMessage=Пользователь с {0} {1} уже сущестует. Хотите продолжить?
+federatedIdentityConfirmReauthenticateMessage=Аутентифицируйтесь как {0} для того, чтобы связать Вашу учетную запись с {1}
+confirmLinkIdpReviewProfile=Обзор профиля
+confirmLinkIdpContinue=Добавить в существующую учетную запись
+
+configureTotpMessage=Вам необходимо настроить аутентификатор в мобильном устройстве, чтобы активировать учетную запись.
+updateProfileMessage=Вам необходимо обновить свой профиль, чтобы активировать Вашу учетную запись.
+updatePasswordMessage=Вам необходимо изменить пароль, чтобы активировать Вашу учетную запись.
+verifyEmailMessage=Вам необходимо подтвердить Ваш E-mail, чтобы активировать Вашу учетную запись.
+linkIdpMessage=Вам необходимо подтвердить Ваш E-mail, чтобы связать Вашу учетную запись с {0}.
+
+emailSentMessage=В ближайшее время Вы должны получить письмо с дальнейшими инструкциями.
+emailSendErrorMessage=Не получается отправить письмо. Пожалуйста, повторите позже.
+
+accountUpdatedMessage=Ваша учетная запись успешно обновлена.
+accountPasswordUpdatedMessage=Ваш пароль успешно обновлен.
+
+noAccessMessage=Нет доступа
+
+invalidPasswordMinLengthMessage=Некорректный пароль: длина пароля должна быть не менее {0} символов(а).
+invalidPasswordMinDigitsMessage=Некорректный пароль: пароль должен содержать не менее {0} цифр(ы).
+invalidPasswordMinLowerCaseCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} символов(а) в нижнем регистре.
+invalidPasswordMinUpperCaseCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} символов(а) в верхнем регистре.
+invalidPasswordMinSpecialCharsMessage=Некорректный пароль: пароль должен содержать не менее {0} спецсимволов(а).
+invalidPasswordNotUsernameMessage=Некорректный пароль: пароль не должен совпадать с именем пользователя.
+invalidPasswordRegexPatternMessage=Некорректный пароль: пароль не прошел проверку по регулярному выражению.
+invalidPasswordHistoryMessage=Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролем(ями).
+invalidPasswordGenericMessage=Некорректный пароль: новый пароль не соответствует правилам пароля.
+
+failedToProcessResponseMessage=Не удалось обработать ответ
+httpsRequiredMessage=Требуется HTTPS
+realmNotEnabledMessage=Realm не включен
+invalidRequestMessage=Неверный запрос
+failedLogout=Выйти не удалось
+unknownLoginRequesterMessage=Неизвестный клиент
+loginRequesterNotEnabledMessage=Клиент отключен
+bearerOnlyMessage=Bearer-only приложениям не разрешается инициализация входа через браузер
+standardFlowDisabledMessage=Клиенту не разрешается инициировать вход через браузер с данным response_type. Standard flow отключен для этого клиента.
+implicitFlowDisabledMessage=Клиенту не разрешается инициировать вход через браузер с данным response_type. Implicit flow отключен для этого клиента.
+invalidRedirectUriMessage=Неверный uri для переадресации
+unsupportedNameIdFormatMessage=Неподдерживаемый NameIDFormat
+invalidRequesterMessage=Неверный запрашивающий
+registrationNotAllowedMessage=Регистрация не разрешена
+resetCredentialNotAllowedMessage=Сброс идентификационных данных не разрешен
+
+permissionNotApprovedMessage=Разрешение не подтверждено.
+noRelayStateInResponseMessage=Нет изменения состояния в ответе от провайдера учетных записей.
+insufficientPermissionMessage=Недостаточно полномочий для связывания идентификаторов.
+couldNotProceedWithAuthenticationRequestMessage=Невозможно обработать аутентификационный запрос в провайдере учетных записей.
+couldNotObtainTokenMessage=Не удалось получить токен от провайдера учетных записей.
+unexpectedErrorRetrievingTokenMessage=Непредвиденная ошибка при получении токена от провайдера учетных записей.
+unexpectedErrorHandlingResponseMessage=Непредвиденная ошибка при обработке ответа от провайдера учетных записей.
+identityProviderAuthenticationFailedMessage=Аутентификация провалена. Невозможно аутентифицировать с поставщиком учетных записей.
+identityProviderDifferentUserMessage=Аутентифицирован как {0}, но ожидается, что будет аутентифицирован как {1}
+couldNotSendAuthenticationRequestMessage=Не получается выполнить запрос аутентификации к поставщику учетных записей.
+unexpectedErrorHandlingRequestMessage=Непредвиденная ошибка при обработке запроса аутентификации поставщика учетных записей.
+invalidAccessCodeMessage=Неверный код доступа.
+sessionNotActiveMessage=Сессия не активна.
+invalidCodeMessage=Произошла ошибка. Пожалуйста, войдите в систему снова через ваше приложение.
+identityProviderUnexpectedErrorMessage=Непредвиденная ошибка при проверке подлинности поставщика учетных записей.
+identityProviderNotFoundMessage=Не удалось найти поставщика учетных записей с данным идентификатором.
+identityProviderLinkSuccess=Ваша учетная запись была успешно соединена с {0} учетной записью {1} .
+staleCodeMessage=Эта страница больше не действительна, пожалуйста, вернитесь в приложение и снова войдите в систему.
+realmSupportsNoCredentialsMessage=Realm не поддерживает никакой тип учетных данных.
+identityProviderNotUniqueMessage=Realm поддерживает несколько поставщиков учетных записей. Не удалось определить, какой именно поставщик должен использоваться для аутентификации.
+emailVerifiedMessage=Ваш E-mail был подтвержден.
+staleEmailVerificationLink=Ссылка, по которой Вы перешли, устарела и больше не действует. Может быть, вы уже подтвердили свой E-mail?
+
+backToApplication=« Назад в приложение
+missingParameterMessage=Пропущенные параметры\: {0}
+clientNotFoundMessage=Клиент не найден.
+clientDisabledMessage=Клиент отключен.
+invalidParameterMessage=Неверный параметр\: {0}
+alreadyLoggedIn=Вы уже вошли.
+
+p3pPolicy=CP="Это не политика P3P!"
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
index 0c96904..f779bcf 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
@@ -59,9 +59,6 @@
<transaction mode="BATCH"/>
<file-store passivation="false" purge="false"/>
</local-cache>
- <local-cache name="concurrent">
- <file-store passivation="true" purge="false"/>
- </local-cache>
</cache-container>
<cache-container name="ejb" aliases="sfsb" default-cache="passivation" module="org.wildfly.clustering.ejb.infinispan">
<local-cache name="passivation">
@@ -75,7 +72,7 @@
<file-store passivation="false" purge="false"/>
</local-cache>
</cache-container>
- <cache-container name="hibernate" module="org.hibernate.infinispan">
+ <cache-container name="hibernate" default-cache="local-query" module="org.hibernate.infinispan">
<local-cache name="entity">
<transaction mode="NON_XA"/>
<eviction strategy="LRU" max-entries="10000"/>
@@ -111,24 +108,21 @@
</cache-container>
<cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">
<transport lock-timeout="60000"/>
- <replicated-cache name="default">
+ <replicated-cache name="default" mode="SYNC">
<transaction mode="BATCH"/>
</replicated-cache>
</cache-container>
<cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan">
<transport lock-timeout="60000"/>
- <distributed-cache name="dist">
+ <distributed-cache name="dist" mode="ASYNC" l1-lifespan="0" owners="2">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
- <distributed-cache name="concurrent">
- <file-store/>
- </distributed-cache>
</cache-container>
<cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan">
<transport lock-timeout="60000"/>
- <distributed-cache name="dist">
+ <distributed-cache name="dist" mode="ASYNC" l1-lifespan="0" owners="2">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
@@ -140,7 +134,7 @@
<eviction strategy="LRU" max-entries="10000"/>
<expiration max-idle="100000"/>
</local-cache>
- <invalidation-cache name="entity">
+ <invalidation-cache name="entity" mode="SYNC">
<transaction mode="NON_XA"/>
<eviction strategy="LRU" max-entries="10000"/>
<expiration max-idle="100000"/>
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml
index f779bcf..0c96904 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan2.xml
@@ -59,6 +59,9 @@
<transaction mode="BATCH"/>
<file-store passivation="false" purge="false"/>
</local-cache>
+ <local-cache name="concurrent">
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
</cache-container>
<cache-container name="ejb" aliases="sfsb" default-cache="passivation" module="org.wildfly.clustering.ejb.infinispan">
<local-cache name="passivation">
@@ -72,7 +75,7 @@
<file-store passivation="false" purge="false"/>
</local-cache>
</cache-container>
- <cache-container name="hibernate" default-cache="local-query" module="org.hibernate.infinispan">
+ <cache-container name="hibernate" module="org.hibernate.infinispan">
<local-cache name="entity">
<transaction mode="NON_XA"/>
<eviction strategy="LRU" max-entries="10000"/>
@@ -108,21 +111,24 @@
</cache-container>
<cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">
<transport lock-timeout="60000"/>
- <replicated-cache name="default" mode="SYNC">
+ <replicated-cache name="default">
<transaction mode="BATCH"/>
</replicated-cache>
</cache-container>
<cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan">
<transport lock-timeout="60000"/>
- <distributed-cache name="dist" mode="ASYNC" l1-lifespan="0" owners="2">
+ <distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
</distributed-cache>
+ <distributed-cache name="concurrent">
+ <file-store/>
+ </distributed-cache>
</cache-container>
<cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan">
<transport lock-timeout="60000"/>
- <distributed-cache name="dist" mode="ASYNC" l1-lifespan="0" owners="2">
+ <distributed-cache name="dist">
<locking isolation="REPEATABLE_READ"/>
<transaction mode="BATCH"/>
<file-store/>
@@ -134,7 +140,7 @@
<eviction strategy="LRU" max-entries="10000"/>
<expiration max-idle="100000"/>
</local-cache>
- <invalidation-cache name="entity" mode="SYNC">
+ <invalidation-cache name="entity">
<transaction mode="NON_XA"/>
<eviction strategy="LRU" max-entries="10000"/>
<expiration max-idle="100000"/>