From b109ce14b0857043f8a6a4956897b1afdb0b4ae6 Mon Sep 17 00:00:00 2001 From: Dimitri Teleguin Date: Mon, 1 Aug 2016 20:04:31 +0300 Subject: [PATCH 01/53] KEYCLOAK-3327 Make realm attributes accessible via the RealmModel --- .../idm/RealmRepresentation.java | 10 +++ .../models/cache/infinispan/RealmAdapter.java | 60 +++++++++++++++++ .../infinispan/entities/CachedRealm.java | 37 ++++++++++- .../org/keycloak/models/jpa/RealmAdapter.java | 11 ++++ .../mongo/keycloak/adapters/RealmAdapter.java | 64 ++++++++++++++++++- .../java/org/keycloak/models/RealmModel.java | 11 ++++ .../keycloak/models/entities/RealmEntity.java | 13 +++- .../models/utils/ModelToRepresentation.java | 4 ++ .../models/utils/RepresentationToModel.java | 16 +++++ .../testsuite/admin/realm/RealmTest.java | 18 ++++++ .../test/resources/admin-test/testrealm.json | 6 ++ 11 files changed, 242 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java index 67c1678ad1..89e0c0181f 100755 --- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java @@ -123,6 +123,8 @@ public class RealmRepresentation { protected String resetCredentialsFlow; protected String clientAuthenticationFlow; + protected Map attributes; + protected String keycloakVersion; @Deprecated @@ -864,4 +866,12 @@ public class RealmRepresentation { return identityProviders != null && !identityProviders.isEmpty(); } + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public Map getAttributes() { + return attributes; + } + } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index 8256aa221d..6950ed8496 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -1465,4 +1465,64 @@ public class RealmAdapter implements RealmModel { if (isUpdated()) return updated.getComponent(id); return cached.getComponents().get(id); } + + public void setAttribute(String name, String value) { + getDelegateForUpdate(); + updated.setAttribute(name, value); + } + + @Override + public void setAttribute(String name, Boolean value) { + getDelegateForUpdate(); + updated.setAttribute(name, value); + } + + @Override + public void setAttribute(String name, Integer value) { + getDelegateForUpdate(); + updated.setAttribute(name, value); + } + + @Override + public void setAttribute(String name, Long value) { + getDelegateForUpdate(); + updated.setAttribute(name, value); + } + + @Override + public void removeAttribute(String name) { + getDelegateForUpdate(); + updated.removeAttribute(name); + } + + @Override + public String getAttribute(String name) { + if (isUpdated()) return updated.getAttribute(name); + return cached.getAttribute(name); + } + + @Override + public Integer getAttribute(String name, Integer defaultValue) { + if (isUpdated()) return updated.getAttribute(name, defaultValue); + return cached.getAttribute(name, defaultValue); + } + + @Override + public Long getAttribute(String name, Long defaultValue) { + if (isUpdated()) return updated.getAttribute(name, defaultValue); + return cached.getAttribute(name, defaultValue); + } + + @Override + public Boolean getAttribute(String name, Boolean defaultValue) { + if (isUpdated()) return updated.getAttribute(name, defaultValue); + return cached.getAttribute(name, defaultValue); + } + + @Override + public Map getAttributes() { + if (isUpdated()) return updated.getAttributes(); + return cached.getAttributes(); + } + } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java index 7eab0ebac2..e85078e4c4 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java @@ -152,6 +152,8 @@ public class CachedRealm extends AbstractRevisioned { protected MultivaluedHashMap identityProviderMappers = new MultivaluedHashMap<>(); protected Set identityProviderMapperSet; + protected Map attributes; + public CachedRealm(Long revision, RealmModel model) { super(revision, model.getId()); name = model.getName(); @@ -231,10 +233,10 @@ public class CachedRealm extends AbstractRevisioned { eventsExpiration = model.getEventsExpiration(); eventsListeners = model.getEventsListeners(); enabledEventTypes = model.getEnabledEventTypes(); - + adminEventsEnabled = model.isAdminEventsEnabled(); adminEventsDetailsEnabled = model.isAdminEventsDetailsEnabled(); - + defaultRoles = model.getDefaultRoles(); ClientModel masterAdminClient = model.getMasterAdminClient(); this.masterAdminClient = (masterAdminClient != null) ? masterAdminClient.getId() : null; @@ -285,6 +287,11 @@ public class CachedRealm extends AbstractRevisioned { components.put(component.getId(), component); } + try { + attributes = model.getAttributes(); + } catch (UnsupportedOperationException ex) { + } + } protected void cacheClientTemplates(RealmModel model) { @@ -475,7 +482,7 @@ public class CachedRealm extends AbstractRevisioned { public Set getEventsListeners() { return eventsListeners; } - + public Set getEnabledEventTypes() { return enabledEventTypes; } @@ -619,4 +626,28 @@ public class CachedRealm extends AbstractRevisioned { public Map getComponents() { return components; } + + public String getAttribute(String name) { + return attributes != null ? attributes.get(name) : null; + } + + public Integer getAttribute(String name, Integer defaultValue) { + String v = getAttribute(name); + return v != null ? Integer.parseInt(v) : defaultValue; + } + + public Long getAttribute(String name, Long defaultValue) { + String v = getAttribute(name); + return v != null ? Long.parseLong(v) : defaultValue; + } + + public Boolean getAttribute(String name, Boolean defaultValue) { + String v = getAttribute(name); + return v != null ? Boolean.parseBoolean(v) : defaultValue; + } + + public Map getAttributes() { + return attributes; + } + } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 46df41a455..a5747c6285 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -182,6 +182,7 @@ public class RealmAdapter implements RealmModel, JpaModel { em.flush(); } + @Override public void setAttribute(String name, String value) { for (RealmAttributeEntity attr : realm.getAttributes()) { if (attr.getName().equals(name)) { @@ -197,18 +198,22 @@ public class RealmAdapter implements RealmModel, JpaModel { realm.getAttributes().add(attr); } + @Override public void setAttribute(String name, Boolean value) { setAttribute(name, value.toString()); } + @Override public void setAttribute(String name, Integer value) { setAttribute(name, value.toString()); } + @Override public void setAttribute(String name, Long value) { setAttribute(name, value.toString()); } + @Override public void removeAttribute(String name) { Iterator it = realm.getAttributes().iterator(); while (it.hasNext()) { @@ -220,6 +225,7 @@ public class RealmAdapter implements RealmModel, JpaModel { } } + @Override public String getAttribute(String name) { for (RealmAttributeEntity attr : realm.getAttributes()) { if (attr.getName().equals(name)) { @@ -229,24 +235,28 @@ public class RealmAdapter implements RealmModel, JpaModel { return null; } + @Override public Integer getAttribute(String name, Integer defaultValue) { String v = getAttribute(name); return v != null ? Integer.parseInt(v) : defaultValue; } + @Override public Long getAttribute(String name, Long defaultValue) { String v = getAttribute(name); return v != null ? Long.parseLong(v) : defaultValue; } + @Override public Boolean getAttribute(String name, Boolean defaultValue) { String v = getAttribute(name); return v != null ? Boolean.parseBoolean(v) : defaultValue; } + @Override public Map getAttributes() { // should always return a copy Map result = new HashMap(); @@ -255,6 +265,7 @@ public class RealmAdapter implements RealmModel, JpaModel { } return result; } + @Override public boolean isBruteForceProtected() { return getAttribute("bruteForceProtected", false); diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index e7dc5f60ce..86a90013cc 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -1230,7 +1230,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme } updateRealm(); } - + @Override public boolean isAdminEventsEnabled() { return realm.isAdminEventsEnabled(); @@ -1240,7 +1240,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme public void setAdminEventsEnabled(boolean enabled) { realm.setAdminEventsEnabled(enabled); updateRealm(); - + } @Override @@ -1253,7 +1253,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme realm.setAdminEventsDetailsEnabled(enabled); updateRealm(); } - + @Override public ClientModel getMasterAdminClient() { MongoClientEntity appData = getMongoStore().loadEntity(MongoClientEntity.class, realm.getMasterAdminClient(), invocationContext); @@ -2181,4 +2181,62 @@ public class RealmAdapter extends AbstractMongoAdapter impleme } return null; } + + @Override + public void setAttribute(String name, String value) { + realm.getAttributes().put(name, value); + updateRealm(); + } + + @Override + public void setAttribute(String name, Boolean value) { + setAttribute(name, value.toString()); + } + + @Override + public void setAttribute(String name, Integer value) { + setAttribute(name, value.toString()); + } + + @Override + public void setAttribute(String name, Long value) { + setAttribute(name, value.toString()); + } + + @Override + public void removeAttribute(String name) { + realm.getAttributes().remove(name); + updateRealm(); + } + + @Override + public String getAttribute(String name) { + return realm.getAttributes().get(name); + } + + @Override + public Integer getAttribute(String name, Integer defaultValue) { + String v = getAttribute(name); + return v != null ? Integer.parseInt(v) : defaultValue; + } + + @Override + public Long getAttribute(String name, Long defaultValue) { + String v = getAttribute(name); + return v != null ? Long.parseLong(v) : defaultValue; + } + + @Override + public Boolean getAttribute(String name, Boolean defaultValue) { + String v = getAttribute(name); + return v != null ? Boolean.parseBoolean(v) : defaultValue; + } + + @Override + public Map getAttributes() { + Map attributes = new HashMap<>(); + attributes.putAll(realm.getAttributes()); + return attributes; + } + } diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java index 04f1476716..3223fbd8d2 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java @@ -104,6 +104,17 @@ public interface RealmModel extends RoleContainerModel { void setEditUsernameAllowed(boolean editUsernameAllowed); + void setAttribute(String name, String value); + void setAttribute(String name, Boolean value); + void setAttribute(String name, Integer value); + void setAttribute(String name, Long value); + void removeAttribute(String name); + String getAttribute(String name); + Integer getAttribute(String name, Integer defaultValue); + Long getAttribute(String name, Long defaultValue); + Boolean getAttribute(String name, Boolean defaultValue); + Map getAttributes(); + //--- brute force settings boolean isBruteForceProtected(); void setBruteForceProtected(boolean value); diff --git a/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java b/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java index c5a6ecf8fe..cccdacdaf3 100755 --- a/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java +++ b/server-spi/src/main/java/org/keycloak/models/entities/RealmEntity.java @@ -94,6 +94,8 @@ public class RealmEntity extends AbstractIdentifiableEntity { private Map smtpConfig = new HashMap(); private Map socialConfig = new HashMap(); + private Map attributes = new HashMap<>(); + private boolean eventsEnabled; private long eventsExpiration; private List eventsListeners = new ArrayList(); @@ -692,6 +694,13 @@ public class RealmEntity extends AbstractIdentifiableEntity { public void setComponentEntities(List componentEntities) { this.componentEntities = componentEntities; } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + } - - diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index e2d4799206..2d75c547e6 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -390,6 +390,10 @@ public class ModelToRepresentation { exportRequiredActions(realm, rep); exportGroups(realm, rep); } + + Map attributes = realm.getAttributes(); + rep.setAttributes(attributes); + return rep; } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index f36f010410..2610d5370c 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -373,6 +373,15 @@ public class RepresentationToModel { if(rep.getDefaultLocale() != null){ newRealm.setDefaultLocale(rep.getDefaultLocale()); } + + // import attributes + + if (rep.getAttributes() != null) { + for (Map.Entry attr : rep.getAttributes().entrySet()) { + newRealm.setAttribute(attr.getKey(), attr.getValue()); + } + } + } protected static void importComponents(RealmModel newRealm, MultivaluedHashMap components, String parentId) { @@ -821,6 +830,13 @@ public class RepresentationToModel { if (rep.getClientAuthenticationFlow() != null) { realm.setClientAuthenticationFlow(realm.getFlowByAlias(rep.getClientAuthenticationFlow())); } + + if (rep.getAttributes() != null) { + for (Map.Entry entry : rep.getAttributes().entrySet()) { + realm.setAttribute(entry.getKey(), entry.getValue()); + } + } + } // Basic realm stuff diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index f08f79059e..5bf2cc1651 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -57,6 +57,7 @@ import java.util.LinkedList; import java.util.List; import java.util.HashSet; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; import static org.junit.Assert.assertEquals; @@ -237,6 +238,16 @@ public class RealmTest extends AbstractAdminTest { assertEquals(Boolean.FALSE, rep.isRegistrationAllowed()); assertEquals(Boolean.FALSE, rep.isRegistrationEmailAsUsername()); assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed()); + + // attributes + rep.getAttributes().put("foo", "bar"); + + realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM); + + rep = realm.toRepresentation(); + assertEquals("bar", rep.getAttributes().get("foo")); + } @Test @@ -393,6 +404,13 @@ public class RealmTest extends AbstractAdminTest { assertEquals(realm.getBrowserSecurityHeaders(), storedRealm.getBrowserSecurityHeaders()); } + if (realm.getAttributes() != null) { + HashMap attributes = new HashMap<>(); + attributes.putAll(storedRealm.getAttributes()); + attributes.entrySet().retainAll(realm.getAttributes().entrySet()); + assertEquals(realm.getAttributes(), attributes); + } + } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/testrealm.json index bc5b2a72f7..b20eb5da1f 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/testrealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/testrealm.json @@ -95,5 +95,11 @@ "roles": ["customer-user"] } ] + }, + "attributes": { + "string-attr": "foo", + "int-attr": "123", + "long-attr": "1234567890123456", + "bool-attr": "true" } } From ee66cb51da580c94f9d3a80c5b384ac09b039c62 Mon Sep 17 00:00:00 2001 From: Ramunas Kraujutis Date: Sun, 21 Aug 2016 16:14:22 +0300 Subject: [PATCH 02/53] adding Lithuanian translation --- .../account/messages/messages_en.properties | 3 +- .../account/messages/messages_lt.properties | 153 +++ .../theme/base/account/theme.properties | 2 +- .../messages/admin-messages_lt.properties | 1114 +++++++++++++++++ .../admin/messages/messages_lt.properties | 14 + .../theme/base/admin/theme.properties | 2 +- .../email/messages/messages_lt.properties | 24 + .../theme/base/email/theme.properties | 2 +- .../login/messages/messages_en.properties | 1 + .../login/messages/messages_lt.properties | 218 ++++ .../theme/base/login/theme.properties | 2 +- 11 files changed, 1530 insertions(+), 5 deletions(-) create mode 100644 themes/src/main/resources/theme/base/account/messages/messages_lt.properties create mode 100644 themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties create mode 100644 themes/src/main/resources/theme/base/admin/messages/messages_lt.properties create mode 100644 themes/src/main/resources/theme/base/email/messages/messages_lt.properties create mode 100644 themes/src/main/resources/theme/base/login/messages/messages_lt.properties diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties index 5c7ff812a1..5a5282d065 100755 --- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties @@ -158,4 +158,5 @@ locale_es=Espa\u00F1ol locale_fr=Fran\u00e7ais locale_it=Italian locale_pt-BR=Portugu\u00EAs (Brasil) -locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \ No newline at end of file +locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 +locale_lt=Lietuvi\u0173 \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/account/messages/messages_lt.properties b/themes/src/main/resources/theme/base/account/messages/messages_lt.properties new file mode 100644 index 0000000000..f9edf13ecf --- /dev/null +++ b/themes/src/main/resources/theme/base/account/messages/messages_lt.properties @@ -0,0 +1,153 @@ +doSave=Saugoti +doCancel=At\u0161aukti + +doLogOutAllSessions=Atjungti visas sesijas +doRemove=\u0160alinti +doAdd=Prid\u0117ti +doSignOut=Atsijungti + +editAccountHtmlTitle=Redaguoti paskyr\u0105 +federatedIdentitiesHtmlTitle=Susietos paskyros +accountLogHtmlTitle=Paskyros \u017Eurnalas +changePasswordHtmlTitle=Keisti slapta\u017Eod\u012F +sessionsHtmlTitle=Prisijungimo sesijos +accountManagementTitle=Keycloak Naudotoj\u0173 Administravimas +authenticatorTitle=Autentifikatorius +applicationsHtmlTitle=Programos + +authenticatorCode=Vienkartinis kodas +email=El. pa\u0161tas +firstName=Vardas +givenName=Pavard\u0117 +fullName=Pilnas vardas +lastName=Pavard\u0117 +familyName=Pavard\u0117 +password=Slapta\u017Eodis +passwordConfirm=Pakartotas slapta\u017Eodis +passwordNew=Naujas slapta\u017Eodis +username=Naudotojo vardas +address=Adresas +street=Gatv\u0117 +locality=Miestas arba vietov\u0117 +region=Rajonas +postal_code=Pa\u0161to kodas +country=\u0160alis +emailVerified=El. pa\u0161to adresas patvirtintas +gssDelegationCredential=GSS prisijungimo duomen\u0173 delegavimas + +role_admin=Administratorius +role_realm-admin=Srities administravimas +role_create-realm=Kurti srit\u012F +role_view-realm=Per\u017Ei\u016Br\u0117ti srit\u012F +role_view-users=Per\u017Ei\u016Br\u0117ti naudotojus +role_view-applications=Per\u017Ei\u016Br\u0117ti programas +role_view-clients=Per\u017Ei\u016Br\u0117ti klientines programas +role_view-events=Per\u017Ei\u016Br\u0117ti \u012Fvyki\u0173 \u017Eurnal\u0105 +role_view-identity-providers=Per\u017Ei\u016Br\u0117ti tapatyb\u0117s teik\u0117jus +role_manage-realm=Valdyti sritis +role_manage-users=Valdyti naudotojus +role_manage-applications=Valdyti programas +role_manage-identity-providers=Valdyti tapatyb\u0117s teik\u0117jus +role_manage-clients=Valdyti programas +role_manage-events=Valdyti \u012Fvykius +role_view-profile=Per\u017Ei\u016Br\u0117ti paskyr\u0105 +role_manage-account=Valdyti paskyr\u0105 +role_read-token=Skaityti prieigos rak\u0161\u0105 +role_offline-access=Darbas neprisijungus +role_uma_authorization=\u012Egauti UMA autorizavimo teises +client_account=Paskyra +client_security-admin-console=Saugumo administravimo konsol\u0117 +client_admin-cli=Administravimo CLI +client_realm-management=Srities valdymas +client_broker=Tarpininkas + + +requiredFields=Privalomi laukai +allFieldsRequired=Visi laukai yra privalomi + +backToApplication=« Gr\u012F\u017Eti \u012F program\u0105 +backTo=Atgal \u012F {0} + +date=Data +event=\u012Evykis +ip=IP +client=Klientas +clients=Klientai +details=Detaliau +started=Suk\u016Brimo laikas +lastAccess=V\u0117liausia prieiga +expires=Galioja iki +applications=Programos + +account=Paskyra +federatedIdentity=Susieta tapatyb\u0117 +authenticator=Autentifikatorius +sessions=Sesijos +log=\u012Evykiai + +application=Programa +availablePermissions=Galimos teis\u0117s +grantedPermissions=\u012Egalintos teis\u0117s +grantedPersonalInfo=\u012Egalinta asmenin\u0117 informacija +additionalGrants=Papildomi \u012Fgaliojimai +action=Veiksmas +inResource=yra +fullAccess=Pilna prieiga +offlineToken=Re\u017Eimo neprisijungus raktas (token) +revoke=At\u0161aukti \u012Fgaliojim\u0105 + +configureAuthenticators=Sukonfig\u016Bruotas autentifikatorius +mobile=Mobilus +totpStep1=\u012Ediekite FreeOTP arba Google Authenticator savo \u012Frenginyje. Program\u0117l\u0117s prieinamos Google Play ir Apple App Store. +totpStep2=Atidarykite program\u0117l\u0119 ir nuskenuokite barkod\u0105 arba \u012Fveskite kod\u0105. +totpStep3=\u012Eveskite program\u0117l\u0117je sugeneruot\u0105 vien\u0105 kart\u0105 galiojant\u012F kod\u0105 ir paspauskite Saugoti nor\u0117dami prisijungti. + +missingUsernameMessage=Pra\u0161ome \u012Fvesti naudotojo vard\u0105. +missingFirstNameMessage=Pra\u0161ome \u012Fvesti vard\u0105. +invalidEmailMessage=Neteisingas el. pa\u0161to adresas. +missingLastNameMessage=Pra\u0161ome \u012Fvesti pavard\u0119. +missingEmailMessage=Pra\u0161ome \u012Fvesti el. pa\u0161to adres\u0105. +missingPasswordMessage=Pra\u0161ome \u012Fvesti slapta\u017Eod\u012F. +notMatchPasswordMessage=Slapta\u017Eod\u017Eiai nesutampa. + +missingTotpMessage=Pra\u0161ome \u012Fvesti autentifikacijos kod\u0105. +invalidPasswordExistingMessage=Neteisingas dabartinis slapta\u017Eodis. +invalidPasswordConfirmMessage=Pakartotas slapta\u017Eodis nesutampa. +invalidTotpMessage=Neteisingas autentifikacijos kodas. + +usernameExistsMessage=Toks naudotojas jau egzistuoja. +emailExistsMessage=El. pa\u0161to adresas jau egzistuoja. + +readOnlyUserMessage=Tik skaitymui sukonfig\u016Bruotos paskyros duomen\u0173 atnaujinti neleid\u017Eiama. +readOnlyPasswordMessage=Tik skaitymui sukonfig\u016Bruotos paskyros slapta\u017Eod\u017Eio atnaujinti neleid\u017Eiama. + +successTotpMessage=Mobilus autentifikatorius sukonfig\u016Bruotas. +successTotpRemovedMessage=Mobilus autentifikatorius pa\u0161alintas. + +successGrantRevokedMessage=\u012Egalinimas pa\u0161alintas s\u0117kmingai. + +accountUpdatedMessage=J\u016Bs\u0173 paskyros duomenys s\u0117kmingai atnaujinti. +accountPasswordUpdatedMessage=J\u016Bs\u0173 paskyros slapta\u017Eodis pakeistas. + +missingIdentityProviderMessage=Nenurodytas tapatyb\u0117s teik\u0117jas. +invalidFederatedIdentityActionMessage=Neteisingas arba ne\u017Einomas veiksmas. +identityProviderNotFoundMessage=Nurodytas tapatyb\u0117s teik\u0117jas nerastas. +federatedIdentityLinkNotActiveMessage=Nurodyta susieta tapatyb\u0117 neaktyvi. +federatedIdentityRemovingLastProviderMessage=J\u016Bs negalite pa\u0161alinti paskutinio tapatyb\u0117s teik\u0117jo s\u0105sajos, nes J\u016Bs neturite nusistat\u0119 paskyros slapta\u017Eod\u017Eio. +identityProviderRedirectErrorMessage=Klaida nukreipiant \u012F tapatyb\u0117s teik\u0117jo puslap\u012F. +identityProviderRemovedMessage=Tapatyb\u0117s teik\u0117jas s\u0117kmingai pa\u0161alintas. +identityProviderAlreadyLinkedMessage=Susieta tapatyb\u0117 i\u0161 {0} jau susieta su kita paskyra. +staleCodeAccountMessage=Puslapio galiojimas baig\u0117si. Bandykite dar kart\u0105. +consentDenied=Prieiga draud\u017Eiama. + +accountDisabledMessage=Paskyros galiojimas sustabdytas, kreipkit\u0117s \u012F administratori\u0173. + +accountTemporarilyDisabledMessage=Paskyros galiojimas laikinai sustabdytas. Kreipkit\u0117s \u012F administratori\u0173 arba pabandykite v\u0117liau. +invalidPasswordMinLengthMessage=Per trumpas slapta\u017Eodis: ma\u017Eiausias ilgis {0}. +invalidPasswordMinLowerCaseCharsMessage=Neteisingas slapta\u017Eodis: privaloma \u012Fvesti {0} ma\u017E\u0105j\u0105 raid\u0119. +invalidPasswordMinDigitsMessage=Neteisingas slapta\u017Eodis: privaloma \u012Fvesti {0} skaitmen\u012F. +invalidPasswordMinUpperCaseCharsMessage=Neteisingas slapta\u017Eodis: privaloma \u012Fvesti {0} did\u017Ei\u0105j\u0105 raid\u0119. +invalidPasswordMinSpecialCharsMessage=Neteisingas slapta\u017Eodis: privaloma \u012Fvesti {0} special\u0173 simbol\u012F. +invalidPasswordNotUsernameMessage=Neteisingas slapta\u017Eodis: slapta\u017Eodis negali sutapti su naudotojo vardu. +invalidPasswordRegexPatternMessage=Neteisingas slapta\u017Eodis: slapta\u017Eodis netenkina regex taisykl\u0117s(i\u0173). +invalidPasswordHistoryMessage=Neteisingas slapta\u017Eodis: slapta\u017Eodis negali sutapti su prie\u0161 tai buvusiais {0} slapta\u017Eod\u017Eiais. \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/account/theme.properties b/themes/src/main/resources/theme/base/account/theme.properties index 503eda7804..0bb29a975f 100644 --- a/themes/src/main/resources/theme/base/account/theme.properties +++ b/themes/src/main/resources/theme/base/account/theme.properties @@ -1 +1 @@ -locales=ca,de,en,es,fr,it,pt-BR,ru \ No newline at end of file +locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties new file mode 100644 index 0000000000..66089e32a0 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties @@ -0,0 +1,1114 @@ +consoleTitle=Keycloak administravimo konsol\u0117 + +# Common messages +enabled=\u012Egalintas +name=Pavadinimas +displayName=Rodomas pavadinimas +displayNameHtml=Rodomas pavadinimas HTML formatu +save=Saugoti +cancel=At\u0161aukti +onText=ON +offText=OFF +client=Klientas +clients=Klientai +clear=I\u0161valyti +selectOne=Pasirinkite vien\u0105... + +true=Taip +false=Ne + +endpoints=Prieigos adresai + +# Realm settings +realm-detail.enabled.tooltip=Naudotojai ir programos prie srities gali prieiti tik tuomet, kai ji \u012Fgalinta +realm-detail.oidc-endpoints.tooltip=Atidaromas langas su OpenID Connect prieigos URL adresais +registrationAllowed=Naudotoj\u0173 registracija +registrationAllowed.tooltip=\u012Egalina naudotoj\u0173 registravimosi s\u0105saj\u0105. Prisijungimo lange rodoma nuoroda \u012F registravimosi puslap\u012F. +registrationEmailAsUsername=El. pa\u0161tas kaip naudojo vardas +registrationEmailAsUsername.tooltip=Jei \u012Fgalintas tuomet naudotojo vardo laukas registravimosi lange yra slepiamas ir naujai besiregistruojantiems naudotojams el. pa\u0161to adresas naudojamas kaip naudotojo vardas. +editUsernameAllowed=Naudotojo vardo redagavimas +editUsernameAllowed.tooltip=Jei \u012Fgalintas, tuomet naudotojas gali keisti savo naudotojo vard\u0105. +resetPasswordAllowed=Slapta\u017Eod\u017Eio priminimas +resetPasswordAllowed.tooltip=Prisijungimo lange rodoma nuoroda pamir\u0161to slapta\u017Eod\u017Eio atk\u016Brimui. +rememberMe=Prisiminti mane +rememberMe.tooltip=Prisijungimo lange rodyti pasirinkim\u0105 leid\u017Eiant\u012F naudotojui likti prisijungus netgi tuomet kai nar\u0161ykl\u0117 yra i\u0161jungiama/\u012Fjungiama tol, kol nepasibaigia prisijungimo sesija. +verifyEmail=El. pa\u0161to patvirtinimas +verifyEmail.tooltip=Reikalauti naudotojo patvirtinti el. pa\u0161to adres\u0105 pirmojo prisijungimo metu. +sslRequired=Reikalauti SSL +sslRequired.option.all=visoms u\u017Eklausoms +sslRequired.option.external=i\u0161orin\u0117ms u\u017Eklausoms +sslRequired.option.none=niekada +sslRequired.tooltip=Ar HTTPS privalomas? 'niekada' - HTTPS nereikalaujamas. 'i\u0161orin\u0117ms u\u017Eklausoms' - jungiantis i\u0161 localhost ar serverio IP adres\u0173 galima prieiti ir per HTTP. 'visoms u\u017Eklausoms' - HTTPS reikalaujamas jungiantis i\u0161 vis\u0173 IP adres\u0173. +publicKey=Vie\u0161as raktas +privateKey=Privatus raktas +gen-new-keys=Generuoti naujus raktus +certificate=Sertifikatas +host=Serveris +smtp-host=SMTP serveris +port=Prievadas +smtp-port=SMTP prievadas (numatyta reik\u0161m\u0117 25) +from=Nuo +sender-email-addr=Siunt\u0117jo el. pa\u0161to adresas +enable-ssl=\u012Egalinti SSL +enable-start-tls=\u012Egalinti StartTLS +enable-auth=\u012Egalinti autentifikacij\u0105 +username=Naudotojo vardas +login-username=Prisijungimui naudojamas naudotojo vardas +password=Slapta\u017Eodis +login-password=Prisijungimui naudojamas slapta\u017Eodis +login-theme=Prisijungimo lango tema +login-theme.tooltip=Pasirinkite kaip atrodys J\u016Bs\u0173 prisijungimo, TOTP, teisi\u0173 suteikimo, naudotoj\u0173 registracijos ir slapta\u017Eod\u017Ei\u0173 priminimo langai. +account-theme=Naudotojo profilio tema +account-theme.tooltip=Pasirinkite kaip atrodys naudotojo profilio valdymo langai. +admin-console-theme=Administravimo konsol\u0117s tema +select-theme-admin-console=Pasirinkite kaip atrodys administravimo konsol\u0117s langai. +email-theme=El. pa\u0161to tema +select-theme-email=Pasirinkite kaip atrodys siun\u010Diami el. pa\u0161to lai\u0161kai. +i18n-enabled=Daugiakalbyst\u0117s palaikymas +supported-locales=Palaikomos kalbos +supported-locales.placeholder=Pasirinkite arba \u012Fra\u0161ykite kalbos pavadinim\u0105 +default-locale=Numatyta kalba +realm-cache-clear=Srities pod\u0117lis +realm-cache-clear.tooltip=I\u0161 vis\u0173 sri\u010Di\u0173 pod\u0117li\u0173 pa\u0161alinama visa pod\u0117lyje (cache) esanti informacija +user-cache-clear=Naudotoj\u0173 pod\u0117lis +user-cache-clear.tooltip=I\u0161 vis\u0173 sri\u010Di\u0173 pa\u0161alinama visa naudotoj\u0173 pod\u0117lyje (cache) esanti informacija +revoke-refresh-token=Prieigos rakt\u0105 naudoti tik kart\u0105 +revoke-refresh-token.tooltip=Jei \u012Fgalintas, tuomet atnaujinimo raktai (Refresh Token) gali b\u016Bti naudojami tik vien\u0105 kart\u0105. Kitu atveju - atnaujinimo raktai gali b\u016Bti pernaudojami daugel\u012F kart\u0173. +sso-session-idle=SSO sesijos neveikimo laikas +seconds=Sekund\u0117s +minutes=Minut\u0117s +hours=Valandos +days=Dienos +sso-session-max=SSO sesijos maksimalus laikas +sso-session-idle.tooltip=Laikas, po kurio neaktyvi sesija bus u\u017Ebaigta. Sesijos pasibaigimo metu visi raktai (Tokens) ir nar\u0161ykli\u0173 sesijos sunaikinamos. +sso-session-max.tooltip=Laikas, po kurio prisijungimo sesija yra sunaikinama. Sesijos pasibaigimo metu visi raktai (Tokens) ir nar\u0161ykli\u0173 sesijos sunaikinamos. +offline-session-idle=Neprisijungusios sesijos neveikimo laikas +offline-session-idle.tooltip=Darbo neprisijungus sesijos neveikimo laikas, po kurio neaktyvi sesija bus u\u017Ebaigta. Darbo neprisijungus metu, prisijungimo raktai turi b\u016Bti atnaujinami bent kart\u0105 per nurodyt\u0105 period\u0105. Kitu atveju sesijos galiojmas bus sustabdytas. +access-token-lifespan=Prisijungimo rakto galiojimo laikas +access-token-lifespan.tooltip=Laikas, po kurio prisijungimui naudojamas raktas (Access Token) nustoja galioti. Rekomenduojama, kad \u0161ios reik\u0161m\u0117s galiojimas b\u016Bt\u0173 reliatyviai trumpas palyginus su SSO galiojimo laiku. +access-token-lifespan-for-implicit-flow=Prisijungimo rakto galiojimo laikas (Implicit Flow) +access-token-lifespan-for-implicit-flow.tooltip=Laikas, po kurio prisijungimui naudojamas OpenID Connect Implicit Flow raktas nustoja galioti. Rekomenduojama, kad \u0161ios reik\u0161m\u0117s galiojimas b\u016Bt\u0173 reliatyviai trumpas palyginus su SSO galiojimo laiku. \u0160is parametras skiriasi nuo 'Prisijungimo rakto galiojimo laikas' nes n\u0117ra galimyb\u0117s atnaujinti prieigos rakto naudojant OpenID Connect Implicit Flow. +client-login-timeout=Kliento prisijungimui skirtas laikas +client-login-timeout.tooltip=Laikas, per kur\u012F klientas turi u\u017Ebaigti prisijungimo proces\u0105. Normaliu atveju reik\u0161m\u0117 tur\u0117t\u0173 b\u016Bti 1 minut\u0117. +login-timeout=Naudotojo prisijungimui skirtas laikas +login-timeout.tooltip=Laikas, per kur\u012F naudotojas turi u\u017Ebaigti prisijungimo proces\u0105. Rekomenduojamas pakankamai ilgas laiko tarpas. Pvz. 30 minu\u010Di\u0173 ar daugiau. +login-action-timeout=Naudotojo prisijungimo veiksmui skirtas laikas +login-action-timeout.tooltip=Laikas, per kur\u012F naudotojas turi u\u017Ebaigti su prisijungimu susijus\u012F veiksm\u0105. Pavyzd\u017Eiui atnaujinti slapta\u017Eod\u012F ar sukonfig\u016Bruoti TOTP. Rekomenduojamas laikas - 5 minut\u0117s ar daugiau. +headers=Antra\u0161t\u0117s +brute-force-detection=Grubios j\u0117gos ataka +x-frame-options=X-Frame-Options +x-frame-options-tooltip=Numatyta reik\u0161m\u0117 draud\u017Eia puslapius naudoti kitose svetain\u0117se per iframe (paspauskite antra\u0161t\u0119 nor\u0117dami gauti daugiau informacijos) +content-sec-policy=Content-Security-Policy +content-sec-policy-tooltip=Numatyta reik\u0161m\u0117 draud\u017Eia puslapius naudoti kitose svetain\u0117se per iframe (paspauskite antra\u0161t\u0119 nor\u0117dami gauti daugiau informacijos) +content-type-options=X-Content-Type-Options +content-type-options-tooltip=Numatyta reik\u0161m\u0117 draud\u017Eia Internet Explorer ir Google Chrome atlikti priimti kitokias MIME reik\u0161mes (MIME-sniffing) nei deklaruotas turinio tipas (content-type) (paspauskite antra\u0161t\u0119 nor\u0117dami gauti daugiau informacijos) +max-login-failures=Maksimalus bandym\u0173 prisijungim\u0173 skai\u010Dius +max-login-failures.tooltip=Pasiekus maksimal\u0173 nes\u0117kming\u0173 bandym\u0173 prisijungti skai\u010Di\u0173 \u012Fjungiamas specialus r\u0117\u017Eimas, kuomet laukimo intervalas yra didinamas po kiekvieno sekan\u010Dio neteisingo bandymo. +wait-increment=Laukimo laiko didinimas po +wait-increment.tooltip=Laikas, kur\u012F naudotojo prisijungimai yra draud\u017Eiami, kai n\u0117s\u0117kming\u0173 bandym\u0173 skai\u010Dius pasiekia nustatyt\u0105 rib\u0105 +quick-login-check-millis=Per greito bandymo prisijungti laikas milisekund\u0117mis +quick-login-check-millis.tooltip=Jei n\u0117s\u0117kmingi bandymai prisijungti seka vienas kit\u0105 per greitai, tuomet naudotojo paskyra yra u\u017Erakinama. +min-quick-login-wait=Per greito bandymo prisijungti u\u017Erakinimo laikas +min-quick-login-wait.tooltip=Laikas, kur\u012F naudotojo prisijungimai yra draud\u017Eiami, kai n\u0117s\u0117kmingi bandymai prisijungti seka vienas kit\u0105 per greitai. +max-wait=Maksimalus u\u017Erakinimo laikas +max-wait.tooltip=Maksimalus laikas, kuomet naudotojo paskyra yra u\u017Erakinama po nes\u0117kming\u0173 bandym\u0173 prisijungti. +failure-reset-time=Pamir\u0161ti nepavykusius prisijungimus po +failure-reset-time.tooltip=Laikas, po kurio nepavyk\u0119 prisijungimai bus pamir\u0161ti +realm-tab-login=Prisijungimas +realm-tab-keys=Raktai +realm-tab-email=El. pa\u0161tas +realm-tab-themes=Temos +realm-tab-cache=Pod\u0117lis +realm-tab-tokens=Raktai +realm-tab-client-initial-access=Pradiniai prieigos raktai +realm-tab-security-defenses=Saugos priemon\u0117s +realm-tab-general=Bendra informacija +add-realm=Prid\u0117ti srit\u012F + +#Session settings +realm-sessions=Srities sesijos +revocation=At\u0161aukimai +logout-all=Atjungti visus +active-sessions=Aktyvios sesijos +sessions=Sesijos +not-before=Ne anks\u010Diau +not-before.tooltip=At\u0161aukti visus raktus i\u0161duotis prie\u0161 nurodyt\u0105 dat\u0105. +set-to-now=Parinkti dabartin\u0119 dat\u0105 +push=Informuoti apie at\u0161aukim\u0105 +push.tooltip=Visus klientus, kurie turi administravimo URL, informuoti apie nauj\u0105 rakt\u0173 at\u0161aukimo taisykl\u0119. + +#Protocol Mapper +usermodel.prop.label=Atributas +usermodel.prop.tooltip=S\u0105sajos UserModel atributo metodo pavadinimas. Pavyzd\u017Eiui reik\u0161m\u0117 'email' atitinka UserMode.getEmail() metod\u0105. +usermodel.attr.label=Naudotojo atributas +usermodel.attr.tooltip=I\u0161saugoto naudotojo atributo pavadinimas kuris naudojamas UserModel.attribute rinkinyje. +userSession.modelNote.label=Naudotojo sesijos pastaba +userSession.modelNote.tooltip=I\u0161saugotos naudotojo sesijos pastaba, kuri saugoma UserSessionModel.note rinkinyje. +multivalued.label=Daugiareik\u0161mis +multivalued.tooltip=Nurodo, kad atributas gali tur\u0117ti daugiau nei vien\u0105 reik\u0161m\u0119. Jei pa\u017Eym\u0117tas, tuomet visos reik\u0161m\u0117s nustatomos kaip privalomos. Kitu atveju privaloma tik pirmoji reik\u0161m\u0117. +selectRole.label=Parinkite rol\u0119 +selectRole.tooltip=Kair\u0117je pus\u0117je esan\u010Diame laukelyje \u012Fveskite rol\u0117s pavadinim\u0105 arba paspauskite Rinktis nor\u0117dami nurodyti pageidaujam\u0105 rol\u0119. +tokenClaimName.label=Reikalaujamo rakto pavadinimas +tokenClaimName.tooltip=\u012E rakt\u0105 \u012Fterpiamas privalomas atributas. Galite nurodyte piln\u0105 keli\u0105 iki atributo, pavyzd\u017Eiui 'address.street'. Pateiktu atveju bus sukuriamas sud\u0117tinis (nested) JSON objektas. +jsonType.label=Privalomo atributo JSON tipas +jsonType.tooltip=Naudojamas JSON lauko tipas, kuris turi b\u016Bti u\u017Epildomas rakto privalomoje JSON informacijoje. Galimi tipai: long, int, boolean ir String. +includeInIdToken.label=Prid\u0117ti prie ID rakto +includeInIdToken.tooltip=Ar privaloma informacija turi b\u016Bti prid\u0117ta prie ID rakto? +includeInAccessToken.label=Prid\u0117ti prie prieigos rakto +includeInAccessToken.tooltip=Ar privaloma informacija turi b\u016Bti pridedama prie prieigos rakto? +includeInUserInfo.label=Prid\u0117ti prie naudotojo informacijos +includeInUserInfo.tooltip=Ar privaloma informacija turi b\u016Bti pridedama prie naudotojo informacijos? +usermodel.clientRoleMapping.clientId.label=Kliento ID +usermodel.clientRoleMapping.clientId.tooltip=Kliento ID naudojamas roli\u0173 atribut\u0173 susiejime +usermodel.clientRoleMapping.rolePrefix.label=Kliento rol\u0117s prefiksas +usermodel.clientRoleMapping.rolePrefix.tooltip=Prefiksas, pridedamas prie\u0161 kiekvien\u0105 kliento rol\u0119 (neprivalomas) +usermodel.realmRoleMapping.rolePrefix.label=Srities rol\u0117s prefiksas +usermodel.realmRoleMapping.rolePrefix.tooltip=Prefiksas, pridedamas prie\u0161 kiekvien\u0105 srities rol\u0119 (neprivalomas) + +# client details +clients.tooltip=Klientai - tai srities nar\u0161ykl\u0117s program\u0117l\u0117s arba tinklin\u0117s paslaugos, kuriomis pasitikima. Klientai gali jungtis prie sistemos. Klientams galima nurodyti specifines roles. +search.placeholder=Ie\u0161koti... +create=Sukurti +import=Importuoti +client-id=Kliento ID +base-url=Pagrindinis URL +actions=Veiksmai +not-defined=Nenurodyta +edit=Redaguoti +delete=Trinti +no-results=Rezultat\u0173 n\u0117ra +no-clients-available=N\u0117ra sukonfig\u016Bruot\u0173 klient\u0173 +add-client=Prid\u0117ti klient\u0105 +select-file=Parinkti rinkmen\u0105 +view-details=Per\u017Ei\u016Br\u0117ti detaliau +clear-import=I\u0161valyti importuojamas rinkmenas +client-id.tooltip=Identifikatorius, naudojamas URI adresuose ir prieigos raktuose. Pavyzd\u017Eiui 'my-client'. SAML protokolo atveju, \u0161i\u0105 reik\u0161m\u0119 tikimasi gauti kaip authn u\u017Eklausos siunt\u0117j\u0105 +client.name.tooltip=Reik\u0161m\u0117, kuri rodoma naudotojams. Pavyzd\u017Eiui 'My Client'. Galimos lokalizuotos reik\u0161m\u0117s - pavyzd\u017Eiui\: ${my_client} +client.enabled.tooltip=Klientai, kurie n\u0117ra \u012Fgalinti, negali inicijuoti prisijungimo arba gauti prieigos raktus. +consent-required=Reikalingas patvirtinimas +consent-required.tooltip=Jei \u012Fgalinta, tuomet naudotojai privalo patvirtinti, kad pageidauja prisijungti prie kliento (programos). +client-protocol=Kliento protokolas +client-protocol.tooltip='OpenID connect' leid\u017Eia klientams tikrinti galutinio naudotojo tapatyb\u0119 remiantis autorizacijos serverio atlikta autentifikacija. 'SAML' \u012Fgalina \u017Einiatinklio, \u012Fskaitant skirting\u0173 domen\u0173 atvejus, vieningos autentifikacijos ir autorizacijos scenarijus perduodant informacij\u0105 saugiose \u017Einut\u0117se. +access-type=Prieigos tipas +access-type.tooltip='Konfidencialus' klientai nor\u0117dami inicijuoti prisijungimo protokol\u0105 privalo perduoti slapt\u0105 kod\u0105. 'Vie\u0161as' klientai neprivalo perduoti slapto kodo. 'Tik ne\u0161\u0117jas' klientai - tai tinklin\u0117s paslaugos, kurios niekada neinicijuoja prisijungimo. +standard-flow-enabled=\u012Egalinta standartin\u0117 seka +standard-flow-enabled.tooltip=\u012Egalina standartin\u0117 OpenID Connect nukreipim\u0105, kuomet autentifikacijos metu yra perduodamas autorizacijos kodas. OpenID Connect arba OAuth2 specifikacijos terminais tai rei\u0161kia 'Authorization Code Flow' \u012Fgalinim\u0105 \u0161iam klientui. +implicit-flow-enabled=\u012Egalinta i\u0161reik\u0161tin\u0117 seka +implicit-flow-enabled.tooltip=\u012Egalina OpenID Connect nukreipim\u0105, kuomet autentifikacijos metu n\u0117ra perduodamas autorizacijos kodas. OpenID Connect arba OAuth2 specifikacijos terminais tai rei\u0161kia 'Implicit Flow' \u012Fgalinim\u0105 \u0161iam klientui. +direct-access-grants-enabled=\u012Egalintas tiesiogin\u0117s prieigos suteikimas +direct-access-grants-enabled.tooltip=\u012Egalina tiesiogin\u012F prieigos suteikim\u0105, kuomet klientas turi prieig\u0105 prie naudotojo vardo ir slapta\u017Eod\u017Eio ir prieigos rakt\u0173 gavimui \u0161iais duomenimis gali tiesiogiai apsikeisti su Keycloak serveriu. OAuth2 specifikacijos terminais, \u0161iam klientui \u012Fgalinimas 'Resource Owner Password Credentials Grant'. +service-accounts-enabled=\u012Egalintas paslaugos naudotojas +service-accounts-enabled.tooltip=\u012Egalina klient\u0105 autentifikuotis su Keycloak serveriu ir gauti dedikuot\u0105 prieigos rakt\u0105 skirt\u0105 \u0161iam klientui. OAuth2 specifikacijos terminais, tai rei\u0161kia 'Client Credentials Grant' teis\u0119 \u0161iam klientui. +include-authnstatement=\u012Etraukti AuthnStatement +include-authnstatement.tooltip=Ar prisijungimo b\u016Bdas ir laikas \u0161ur\u0117t\u0173 b\u016Bti \u012Ftraukiami \u012F prisijungimo operacijos atsakym\u0105? +sign-documents=Pasira\u0161yti dokumentus +sign-documents.tooltip=Ar SAML dokumentai turi b\u016Bt\u012F pasira\u0161omi \u0161ios srities? +sign-assertions=Pasira\u0161yti sprendinius +sign-assertions.tooltip=Ar SAML sprendiniai SAML dokumentuose turi b\u016Bti pasira\u0161omi? \u0160is nustatymas neb\u016Btinas, kuomet naudojamas viso dokumento pasira\u0161ymas. +signature-algorithm=Para\u0161o algoritmas +signature-algorithm.tooltip=Para\u0161o algoritmas naudojamas dokument\u0173 pasira\u0161ymui. +canonicalization-method=Standartizavimo metodas +canonicalization-method.tooltip=XML para\u0161o metodas. +encrypt-assertions=U\u017Ekoduoti sprendinius +encrypt-assertions.tooltip=Ar SAML sprendiniai turi b\u016Bti u\u017Ekoduojami kliento vie\u0161uoju raktu naudojant AES? +client-signature-required=Privalomas kliento para\u0161as +client-signature-required.tooltip=Ar kliento siun\u010Diamos SAML u\u017Eklausos ir atsakymai bus pasira\u0161yti? Jei taip, tuomet ar juos privaloma tikrinti? +force-post-binding=Priverstinai naudoti POST s\u0105ry\u0161\u012F +force-post-binding.tooltip=Visuomet naudoti POST s\u0105ry\u0161\u012F siun\u010Diant atsakymus. +front-channel-logout=I\u0161registravimas per nar\u0161ykl\u0119 +front-channel-logout.tooltip=Jei \u012Fgalinta, tuomet atsijungimas atliekamas nar\u0161ykl\u0117s nukreipimu \u012F kliento puslap\u012F. Kitu atveju, atsijungimas atliekamas perduodant serveris-serveris u\u017Eklaus\u0105. +force-name-id-format=Priverstinai naudoti NameID format\u0105 +force-name-id-format.tooltip=Ignoruoti NameID tapatyb\u0117s identifikatoriaus format\u0105, naudojant administratoriaus konsol\u0117je nurodyt\u0105 format\u0105. +name-id-format=NameID formatas +name-id-format.tooltip=Koks tapatyb\u0117s identifikatoriaus formatas turi b\u016Bti naudojamas. +root-url=\u0160akninis URL +root-url.tooltip=Prie reliatyvi\u0173 nuorod\u0173 pridedamas \u0161akninis URL +valid-redirect-uris=Leid\u017Eiamos nukreipimo nuorodos +valid-redirect-uris.tooltip=Nukreipimo URI \u0161ablonas, kuomet nar\u0161yklei leid\u017Eiama nukreipti naudotoj\u0105 po s\u0117kmingos autentifikacijos ar atsijungimo metu. Leid\u017Eiami pakaitos simboliai, pvz. 'http://pavyzdys.lt/*'. Leid\u017Eiami reliatyv\u016Bs keliai pvz. /mano/reliatyvus/kelias/*. Reliatyvumas skai\u010Diuojamas nuo kliento \u0161akninio URL (jei nurodyta) arba nuo autentifikacijos serverio \u0161akninio adreso. SAML atveju, kuomet tikimasi gav\u0117jo paslaugos URL \u012Ftraukimo \u012F prisijungimo u\u017Eklaus\u0105, privaloma nurodyti teisingus URI \u0161ablonus. +base-url.tooltip=Numatytas URL, kuris turi b\u016Bti naudojamas naudotojo nukreipimui atgal \u012F klient\u0105. +admin-url=Administravimo URL +admin-url.tooltip=Kliento administravimo tinklin\u0117s s\u0105sajos URL. \u012Era\u0161yti tuomet, kai klientas palaiko adapterio REST API. \u0160is REST API leid\u017Eia autentifikacijos serveriui perduoti at\u0161aukimo ir kitas su administravimu susijusias taisykles. Da\u017Eniausiai \u0161is URL sutampa su kliento pagrindiniu URL. +master-saml-processing-url=\u0160akninis SAML apdorojimo URL +master-saml-processing-url.tooltip=Kuomet sukonfig\u016Bruotas, \u0161is URL bus naudojamas visoms, 'SP's Assertion Consumer' ir 'Single Logout Services' u\u017Eklausoms. Detalioje SAML prieigos adres\u0173 konfig\u016Bravimo skyriuje \u0161ios reik\u0161m\u0117s gali b\u016Bti atskirai pakeistos. +idp-sso-url-ref=IDP inicijuojamo SSO URL fragmento pavadinimas +idp-sso-url-ref.tooltip=Pavadinimas, kuris IDP inicijuoto SSO prisijungimo metu, perduodamas klientui per URL fragment\u0105. Palikus tu\u0161\u010Di\u0105 reik\u0161m\u0119 IDP inicjuojam\u0105 SSO prisijungimo funkcionalumas i\u0161jungiamas. \u0160is fragmentas buv naudojamas formuojant \u0161i\u0105 nuorod\u0105: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name} +idp-sso-relay-state=IDP inicijuotos SSO b\u016Bsenos perdavimas +idp-sso-relay-state.tooltip=SSO b\u016Bsenos parametro (RelayState) perdavimas kartu su IDP inicijuota SSO SAML u\u017Eklausa. +web-origins=\u0160aknin\u0117s nuorodos +web-origins.tooltip=Leid\u017Eiamos CORS nuorodos. Nor\u0117dami leisti nukreipim\u0105 \u012F teisingas nuorodas naudokite '+'. Nor\u0117dami leisti visas nuorodas, naudokite '*'. +fine-saml-endpoint-conf=Detalioji SAML prieigos ta\u0161k\u0173 konfig\u016Bracija +fine-saml-endpoint-conf.tooltip=Nor\u0117dami konfig\u016Bruoti sprendini\u0173 pri\u0117mimo ir vieningo atsijungimo paslaugas i\u0161skleiskite \u0161\u012F skyri\u0173. +assertion-consumer-post-binding-url=Sprendini\u0173 naudotojo paslaugos POST jungties URL +assertion-consumer-post-binding-url.tooltip=Kliento sprendini\u0173 pri\u0117mimo paslaugos (prisijungimo rezultat\u0173) SAML POST jungties URL. Jei toki\u0173 jung\u010Di\u0173 neturite, tuomet palikite tu\u0161\u010Dias reik\u0161mes. +assertion-consumer-redirect-binding-url=Sprendini\u0173 pri\u0117mimo paslaugos nukreipimo jungties URL +assertion-consumer-redirect-binding-url.tooltip=Kliento sprendinio pri\u0117mimo paslaugos SAML nukreipimo jungties URL (prisijungimo atsakymams). Jei toki\u0173 jung\u010Di\u0173 neturite, tuomet palikite tu\u0161\u010Dias reik\u0161mes. +logout-service-binding-post-url=Atsijungimo paslaugos POST jungties URL +logout-service-binding-post-url.tooltip=Kliento vieningo atsijungimo SAML POST jungties URL. Jei naudojate kitas jungtis, tuomet \u0161ias reik\u0161mes galite palikti neu\u017Epildytas. +logout-service-redir-binding-url=Atsijungimo paslaugos nukreipimo jungties URL +logout-service-redir-binding-url.tooltip=Kliento vieningo atsijungimo paslaugos SAML nukreipimo jungties. Jei naudojate kitas jungtis, tuomet \u0161ias reik\u0161mes galite palikti neu\u017Epildytas. + +# client import +import-client=\u012Ediegti programos nustatymus +format-option=Formato pasirinkimas +select-format=Pasirinkite format\u0105 +import-file=Importuoti rinkmen\u0105 + +# client tabs +settings=Nustatymai +credentials=Prisijungimo duomenys +saml-keys=SAML raktai +roles=Rol\u0117s +mappers=Atribut\u0173 atitikmenys +mappers.tooltip=Protokolo atribut\u0173 susiejimas atlieka rakt\u0173 ir dokument\u0173 transformacijas. Naudotojo duomenys gali b\u016Bti ver\u010Diami \u012F protokolo teiginius, arba tiesiog transformuoti bet kurias u\u017Eklausas perduodamas tarp kliento ir autentifikacijos serverio. +scope=Apimtis +scope.tooltip=Apimties atitikmen\u0173 parinkimas leid\u017Eia apriboti, kurios naudotojo rol\u0117s kartu su raktu bus perduodamos klientui. +sessions.tooltip=Per\u017Ei\u016Br\u0117ti \u0161io kliento aktyvias sesijas. Matysite \u0161iuo metu prisijungusius naudotojus bei j\u0173 prisijungimo laikus. +offline-access=Darbas neprisijungus +offline-access.tooltip=Per\u017Ei\u016Br\u0117ti \u0161io kliento darbo neprisijungus r\u0117\u017Eimo aktyvias sesijas. Matysite naudotojus, kuriems yra i\u0161duoti darbo neprisijungus raktai bei j\u0173 i\u0161davimo laikus. Nor\u0117dami at\u0161aukti visus \u0161iam klientui i\u0161duotus raktus, eikite \u012F at\u0161aikim\u0173 kortel\u0119 ir pasirinkite 'Parinkti dabartin\u0119 dat\u0105' +clustering=Klasteriai +installation=Diegimas +installation.tooltip=Klient\u0173 konfig\u016Bravimo pagalbin\u0117 priemon\u0117, padedanti sugeneruoti klient\u0173 adapteri\u0173 konfig\u016Bracijas, kurias galima atsisi\u0173sti, kopijuoti ar \u012Fkelti i\u0161 i\u0161karpin\u0117s +service-account-roles=Paslaugos paskyros rol\u0117s +service-account-roles.tooltip=Dedikuot\u0173 roli\u0173 priskyrimas \u0161ios paslaugos naudotojui + +# client credentials +client-authenticator=Kliento autentifikavimo priemon\u0117s +client-authenticator.tooltip=Kliento autentifikavimo priemon\u0117s naudojamos kliento autentifikavimuisi \u012F Keycloak server\u012F +certificate.tooltip=Kliento sertifikatas naudojamas kliento i\u0161duot\u0173 ir priva\u010Diu raktu pasira\u0161yt\u0173 JWT prieigos rakt\u0173 tikrinimui. +publicKey.tooltip=Kliento i\u0161duotas vie\u0161asis raktas pasira\u0161ytas kliento priva\u010Diu raktu ir skirtas JWT tikrinimui. +no-client-certificate-configured=Nesukonfig\u016Bruotas nei vienas kliento sertifikatas +gen-new-keys-and-cert=Nauj\u0173 rakt\u0173 ir sertifikat\u0173 generavimas +import-certificate=Importuoti sertifikat\u0105 +gen-client-private-key=Generuoti kliento privat\u0173 rakt\u0105 +generate-private-key=Generuoti privat\u0173 rakt\u0105 +archive-format=Archyvo formatas +archive-format.tooltip=Java rakt\u0173 saugykla (keystore) arba PKCS12 formato rinkmena. +key-alias=Rakto pseudonimas +key-alias.tooltip=Privataus rakto ir sertifikato rinkmenos pseudonimas. +key-password=Rakto slapta\u017Eodis +key-password.tooltip=Slapta\u017Eod\u017Ei\u0173 saugykloje esan\u010Dio privataus rakto slapta\u017Eodis +store-password=Saugyklos slapta\u017Eodis +store-password.tooltip=Slapta\u017Eodis, reikalingas norint atidaryti slapta\u017Eod\u017Ei\u0173 saugykl\u0105 +generate-and-download=Generuoti ir atsisi\u0173sti +client-certificate-import=Kliento sertifikato importavimas +import-client-certificate=Importuoti kliento sertifikatus +jwt-import.key-alias.tooltip=Slapta\u017Eod\u017Ei\u0173 saugyklos pseudonimas +secret=Slaptas kodas +regenerate-secret=Pergeneruoti slapt\u0105 kod\u0105 +registrationAccessToken=Registracijos prieigos raktas +registrationAccessToken.regenerate=Pergeneruoti registracijos prieigos rakt\u0105 +registrationAccessToken.tooltip=Registracijos prieigos raktas klientams suteikia prieig\u0105 prie klient\u0173 registracijos paslaugos +add-role=Prid\u0117ti rol\u0119 +role-name=Rol\u0117s pavadinimas +composite=Sud\u0117tinis +description=Apra\u0161ymas +no-client-roles-available=Kliento rol\u0117s nesukonfig\u016Bruotos +scope-param-required=Privalomas taikymo srities parametras +scope-param-required.tooltip=\u0160i rol\u0117 suteikiama tik tuo atveju, kai taikymo srities parametras su rol\u0117s vardu panaudotas autorizacijos u\u017Eklausoje ar rakte. +composite-roles=Sud\u0117tin\u0117s rol\u0117s +composite-roles.tooltip=Visos susietos rol\u0117s bus automati\u0161kai priskiriamos naudotojui prisikiriant \u0161i\u0105 sud\u0117tin\u0119 rol\u0119. +realm-roles=Srities rol\u0117s +available-roles=Galimos rol\u0117s +add-selected=Prid\u0117ti pa\u017Eym\u0117tas +associated-roles=Priskirtos rol\u0117s +composite.associated-realm-roles.tooltip=Srities apmities rol\u0117s susietos su \u0161ia sud\u0117tine role. +composite.available-realm-roles.tooltip=Srities apmities rol\u0117s, kurias galima susieti su \u0161ia sud\u0117tine role. +remove-selected=Pa\u0161alinti pa\u017Eym\u0117tas +client-roles=Kliento rol\u0117s +select-client-to-view-roles=Nor\u0117dami pamatyti priskirtas roles pa\u017Eym\u0117kite klient\u0105 +available-roles.tooltip=\u0160io kliento rol\u0117s, kurios gali b\u016Bti priskiritos \u0161iai kompozicinei rolei. +client.associated-roles.tooltip=Su \u0161iuo klientu susietos sud\u0117tin\u0117s rol\u0117s. +add-builtin=Prid\u0117ti numatytuosius +category=Kategorija +type=Tipas +no-mappers-available=N\u0117ra susiet\u0173 atribut\u0173 +add-builtin-protocol-mappers=Prid\u0117ti numatytuosius protokolo atribut\u0173 susiejimus +add-builtin-protocol-mapper=Prid\u0117ti numatytuosius protokolo atribut\u0173 susiejimus +scope-mappings=atribut\u0173 susiejimo taikymo sritis +full-scope-allowed=Taikymas pilna apimtimi +full-scope-allowed.tooltip=\u012Egalinimo atveju visi apribojimai i\u0161jungiami +scope.available-roles.tooltip=Srities lygio rol\u0117s, kurios gali b\u016Bti priskiriamos \u0161iai taikymo sri\u010Diai. +assigned-roles=Priskirtos rol\u0117s +assigned-roles.tooltip=Srities lygio rol\u0117s, kurios yra priskirtos \u0161iai taikymo sri\u010Diai. +effective-roles=Aktyvios rol\u0117s +realm.effective-roles.tooltip=Priskirtos srities lygio rol\u0117s, kurios gali gali b\u016Bti paveld\u0117tos i\u0161 sud\u0117tini\u0173 roli\u0173. +select-client-roles.tooltip=Nor\u0117dami pamatyti visas roles pa\u017Eym\u0117kite klient\u0105 +assign.available-roles.tooltip=Kliento rol\u0117s, kurias galima priskirti. +client.assigned-roles.tooltip=Priskirtos kliento rol\u0117s. +client.effective-roles.tooltip=Priskirtos kliento rol\u0117s, kurios gali gali b\u016Bti paveld\u0117tos i\u0161 sud\u0117tini\u0173 roli\u0173. +basic-configuration=Pagrindin\u0117 konfig\u016Bracija +node-reregistration-timeout=Mazgo persiregistravimui skirtas laikas +node-reregistration-timeout.tooltip=Nurodykite maksimal\u0173 laiko interval\u0105, per kur\u012F mazgai privalo i\u0161 naujo prisiregistruoti. Jei mazgas neatsi\u0173s persiregistravimo u\u017Eklausos per nurodyt\u0105 laik\u0105, tuomet \u0161is mazgas bus i\u0161registruojamas i\u0161 Keycloak +registered-cluster-nodes=Registruoti klasterio mazgus +register-node-manually=Registruoti mazg\u0105 rankiniu b\u016Bdu +test-cluster-availability=Tikrinti ar mazgas prieinamas +last-registration=V\u0117liausia registracija +node-host=Mazgo serveris +no-registered-cluster-nodes=Nepriregistuotas nei vienas klasterio mazgas +cluster-nodes=Klasterio mazgai +add-node=Prid\u0117ti mazg\u0105 +active-sessions.tooltip=\u0160io kliento aktyvi\u0173 naudotoj\u0173 sesij\u0173 skai\u010Dius. +show-sessions=Rodyti sesijas +show-sessions.tooltip=D\u0117mesio, \u0161is veiksmas gali ilgai u\u017Etrukti priklausomai nuo aktyvi\u0173 sesij\u0173 skai\u010Diaus. +user=Naudotojas +from-ip=Prisijungimo IP +session-start=Sesijos prad\u017Eios laikas +first-page=Pirmas puslapis +previous-page=Atgalinis puslapis +next-page=Sekantis puslapis +client-revoke.not-before.tooltip=At\u0161aukti visus \u0161io kliento raktus i\u0161duotus prie\u0161 nurodyt\u0105 dat\u0105. +client-revoke.push.tooltip=Kuomet nurodytas administravimo URL, taisykl\u0117 perduodama klientui. +select-a-format=Formato parinkimas +download=Atsisi\u0173sti +offline-tokens=Darbo neprisijungus raktai +offline-tokens.tooltip=Rakt\u0173 skai\u010Dius kurie naudojami darbui neprisijungus +show-offline-tokens=Rodyti raktus +show-offline-tokens.tooltip=D\u0117mesio, \u0161is veiksmas gali ilgai u\u017Etrukti priklausomai nuo aktyvi\u0173 darbo neprisijungus sesij\u0173 skai\u010Diaus. +token-issued=Rakto i\u0161davimo laikas +last-access=V\u0117liausios prieigos laikas +last-refresh=V\u0117liausio atnaujinimo laikas +key-export=Eksportuoti rakt\u0105 +key-import=Importuoti rakt\u0105 +export-saml-key=Eksportuoti SAML rakt\u0105 +import-saml-key=Importuoti SAML rakt\u0105 +realm-certificate-alias=Srities sertifikato pseudonimas +realm-certificate-alias.tooltip=Srities sertifikato, kuris taip pat saugomas saugykloje, pseudonimas. +signing-key=Pasira\u0161ymo raktas +saml-signing-key=SAML pasira\u0161ymo raktas. +private-key=Privatus raktas +generate-new-keys=Generuoti naujus raktus +export=Eksportuoti +encryption-key=U\u017Ekodavimo raktas +saml-encryption-key.tooltip=SAML u\u017Ekodavimo raktas. +service-accounts=Paslaugos naudotojo profiliai +service-account.available-roles.tooltip=\u0160ios paslaugos paskyrai galimos priskirti srities rol\u0117s +service-account.assigned-roles.tooltip=Paslaugos paskyrai priskirtos srities rol\u0117s. +service-account-is-not-enabled-for=Kliento {{client}} paslaugos paskyra ne\u012Fgalinta +create-protocol-mappers=Protokolo atitkmen\u0173 susiejimas +create-protocol-mapper=Protokolo atitkmenens susiejimas +protocol=Protokolas +protocol.tooltip=Protokolas... +id=ID +mapper.name.tooltip=Atitikmens susiejimo vardas. +mapper.consent-required.tooltip=Ar teikiant laikin\u0105 prieig\u0105 naudotojas privalo pateikti sutikim\u0105 d\u0117l duomen\u0173 perdavimo? +consent-text=Sutikimo tekstas +consent-text.tooltip=Tekstas, kuris rodomas naudotojo sutikimo puslapyje. +mapper-type=Atitikmens tipas +mapper-type.tooltip=Atitikmens tipas +# realm identity providers +identity-providers=Tapatyb\u0117s teik\u0117jai +table-of-identity-providers=Tapatyb\u0117s teik\u0117j\u0173 s\u0105ra\u0161as +add-provider.placeholder=Prid\u0117ti teik\u0117j\u0105... +provider=Teik\u0117jas +gui-order=GUI eili\u0161kumas +first-broker-login-flow=Pirmojo prisijungimo eiga +post-broker-login-flow=Sekan\u010Di\u0173 prisijungim\u0173 eiga +redirect-uri=Nukreipimo URI +redirect-uri.tooltip=Tapatyb\u0117s teik\u0117jo konfig\u016Bravimo nuoroda. +alias=Pseudonimas +identity-provider.alias.tooltip=Pseudonimas, kuris vienareik\u0161mi\u0161kai identifikuoja tapatyb\u0117s teik\u0117j\u0105 ir yra naudojamas konstruojant nukreipimo nuorod\u0105. +identity-provider.enabled.tooltip=\u0116galinti \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. +authenticate-by-default=Autentifikuoti i\u0161 karto +identity-provider.authenticate-by-default.tooltip=Jei \u012Fgalinta, tuomet bus bandoma autentifikuoti naudotoj\u0105 prie\u0161 parodant prisijungimo lang\u0105. +store-tokens=Saugoti raktus +identity-provider.store-tokens.tooltip=Jei \u012Fgalinta, tuomet po naudotoj\u0173 prisijungimo, prieigos raktai bus i\u0161saugoti. +stored-tokens-readable=Saugoti raktus skaitomame formate +identity-provider.stored-tokens-readable.tooltip=Jei \u012Fgalinta, tuomet naudotojai gali per\u017Ei\u016Br\u0117ti i\u0161saugotus prieigos raktus. \u012Egalinama broker.read-token rol\u0117. +update-profile-on-first-login=Profilio duomen\u0173 atnaujinimas pirmojo prisijungimo metu +on=On +off=Off +on-missing-info=Kuomet tr\u016Bksta informacijos +update-profile-on-first-login.tooltip=Nurodykite s\u0105lygas, kuomet naudotojas privalo atnaujinti savo profil\u012F pirmojo prisijungimo metu. +trust-email=El. pa\u0161tas patikimas +trust-email.tooltip=Jei \u012Fgalintas, tuomet \u0161io tapatyb\u0117s teik\u0117jo pateiktas el. pa\u0161to adresas laikomas patikimu ir, nepaisant bendr\u0173j\u0173 srities nustatym\u0173, n\u0117ra papildomai tikrinamas. +gui-order.tooltip=Eili\u0161kum\u0105 GUI lange (pvz. Prisijungimo langas) nurodantis skai\u010Dius +first-broker-login-flow.tooltip=Autentifikacijos eigos pseodunimas, kuris bus su\u017Eadintas \u0161io tapatyb\u0117s teik\u0117jo naudotojui prisijungus pirm\u0105 kart\u0105. Terminas 'pirmas kartas' rei\u0161kia, kad Keycloak sistemoje nebuvo saugomas naudotojo profilis susietas su autentifikuotu \u0161io tapatyb\u0117s teik\u0117jo naudotoju. +post-broker-login-flow.tooltip=Autentifikacijos eigos pseodunimas, kuris bus su\u017Eadintas po kiekvieno prisijungimo naudojant \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. Naudingas tuomet, kai atlikti papildomus tikrinimus (pvz. OTP). Palikite tu\u0161\u010Di\u0105 reik\u0161m\u0119 jei nenorite su\u017Eadinti papildom\u0173 tikrinim\u0173 autentifikatoriumi jungiantis per \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. Tur\u0117kite omenyje, kad autentifikatoriaus realizacijos turi daryti prielaid\u0105, kad ClientSession naudotojas yra tapatyb\u0117s teik\u0117jo nustatytas. +openid-connect-config=OpenID prisijungimo konfig\u016Bracija +openid-connect-config.tooltip=OIDC SP ir i\u0161orinio IDP konfig\u016Bracija. +authorization-url=Autorizacijos URL +authorization-url.tooltip=Autorizacijos Url adresas. +token-url=Prieigos rakt\u0173 URL +token-url.tooltip=Prieigos rakt\u0173 URL. +logout-url=Atsijungimo URL +identity-provider.logout-url.tooltip=Adresas, kuris turi b\u016Bti naudojamas norint atjungti naudotoj\u0105 nuo i\u0161orinio tapatyb\u0117s teik\u0117jo. +backchannel-logout=Foninis atjungimas +backchannel-logout.tooltip=Ar i\u0161orinis tapatyb\u0117s teik\u0117jas palaiko serveris-serveris naudotojo atjungimo b\u016Bd\u0105? +user-info-url=Naudotojo informacijos URL +user-info-url.tooltip=Naudotojo informacijos URL. Neprivalomas. +identity-provider.client-id.tooltip=Kliento identifikatorius u\u017Eregistruotas tapatyb\u0117s teik\u0117jo sistemoje. +client-secret=Kliento slaptas kodas +show-secret=Rodysi slapt\u0105 kod\u0105 +hide-secret=Sl\u0117pti slapt\u0105 kod\u0105 +client-secret.tooltip=Kliento slaptas kodas u\u017Eregistruotas tapatyb\u0117s teik\u0117jo sistemoje. +issuer=I\u0161dav\u0117jas +issuer.tooltip=I\u0161dav\u0117jo identifikatorius perduodamas i\u0161dav\u0117jo atsakyme. Tikrinimas nebus atliekamas jei reik\u0161m\u0117 tu\u0161\u010Dia. +default-scopes=Numatytosios taikymo sritys +identity-provider.default-scopes.tooltip=Taikymos sritys, kurios siun\u010Diamos autorizavimo u\u017Eklausoje. Reik\u0161m\u0117s turi b\u016Bti atskirtos tarpo simboliu. Numatyta reik\u0161m\u0117 - 'openid'. +prompt=Raginimas +unspecified.option=nenurodyta +none.option=jokio +consent.option=sutikimo tekstas +login.option=prisijungimas +select-account.option=paskyros pasirinkimas +prompt.tooltip=Nurodo, ar autorizacijos serveris galutini\u0173 naudotoj\u0173 reikalauja pakartotinai patvirtinti sutikim\u0105 ar prisijungti. +validate-signatures=Para\u0161o tikrinimas +identity-provider.validate-signatures.tooltip=\u012Egalinamas i\u0161orini\u0173 IDP para\u0161\u0173 tikrinimas. +validating-public-key=Vie\u0161as raktas para\u0161o tikrinimui +identity-provider.validating-public-key.tooltip=PEM formato vie\u0161asis raktas, kuris turi b\u016Bti naudojamas i\u0161orinio IDP para\u0161t\u0173 tikrinimui. +import-external-idp-config=Importuoti i\u0161orinio IDP konfig\u016Bracij\u0105 +import-external-idp-config.tooltip=Leid\u017Eia \u012Fkelti konfig\u016Bracin\u0119 rinkmen\u0105 arba nurodyti atsisiuntimo URL su i\u0161orinio IDP metaduomenimis. +import-from-url=Importuoti i\u0161 URL +identity-provider.import-from-url.tooltip=Importuoti metaduomenis i\u0161 nutolusio IDP aptikimo apra\u0161o (IDP discovery descriptor). +import-from-file=Importuoti i\u0161 rinkmenos +identity-provider.import-from-file.tooltip=Importuoti metaduomenis i\u0161 rinkmenos, kuri\u0105 atsisiunt\u0117te i\u0161 IDP aptikimo apra\u0161o (IDP discovery descriptor). +saml-config=SAML konfig\u016Bracija +identity-provider.saml-config.tooltip=SAML SP ir i\u0161oriniu IDP konfig\u016Bracija. +single-signon-service-url=Vieningo prisijungimo paslaugos URL +saml.single-signon-service-url.tooltip=Adresas, kuriuo turi b\u016Bti siun\u010Diamos autentifikacijos u\u017Eklausos (SAML AuthnRequest). +single-logout-service-url=Vieningo atsijungimo paslaugos URL +saml.single-logout-service-url.tooltip=Adresas, kuriuo turi b\u016Bti siun\u010Diamos naudotojo atjungimo u\u017Eklausos. +nameid-policy-format=NameID taisykli\u0173 formatas +nameid-policy-format.tooltip=Nurodykite URI nuorod\u0105 atitinkan\u010Di\u0105 vardo identifikatoriaus format\u0105. Numatyta reik\u0161m\u0117 urn:oasis:names:tc:SAML:2.0:nameid-format:persistent. +http-post-binding-response=Si\u0173sti atsakymus HTTP-POST +http-post-binding-response.tooltip=Jei \u012Fgalinta, tuomet atsakymai siun\u010Diami HTTP-POST saistymu. Kitu atveju bus naudojamas HTTP-REDIRECT. +http-post-binding-for-authn-request=Si\u0173sti AuthnRequest HTTP-POST +http-post-binding-for-authn-request.tooltip=Jei \u012Fgalinta, tuomet AuthnRequest siun\u010Diami HTTP-POST saistymu. Kitu atveju bus naudojamas HTTP-REDIRECT. +want-authn-requests-signed=Reikalaujami pasira\u0161yt\u0173 AuthnRequests +want-authn-requests-signed.tooltip=Nurodykite, ar tapatyb\u0117s teik\u0117jas tikisi pasira\u0161yt\u0173 AuthnRequest u\u017Eklaus\u0173. +force-authentication=Priverstin\u0117 autentifikacija +identity-provider.force-authentication.tooltip=Jei \u012Fgalinta, tuomet tapatyb\u0117s teik\u0117jas privalo autentifikuoti naudotoj\u0105 i\u0161 naujo nepasitikint ankstesniu prisijungimu. +validate-signature=Para\u0161o tikrinimas +saml.validate-signature.tooltip=\u012Ejungti/i\u0161jungti SAML atsakym\u0173 para\u0161o tikrinim\u0105. +validating-x509-certificate=X509 sertifikatas tikrinimui +validating-x509-certificate.tooltip=PEM formato sertifikatas, kuris turi b\u016Bti naudojamas para\u0161\u0173 tikrinimui. +saml.import-from-url.tooltip=Importuoti metaduomenis i\u0161 nutolusio IDP SAML subjekto apra\u0161o. +social.client-id.tooltip=Kliento identifikatorius u\u017Eregistruotas tapatyb\u0117s teik\u0117jo sistemoje. +social.client-secret.tooltip=Kliento saugos kodas u\u017Eregistruotas tapatyb\u0117s teik\u0117jo sistemoje. +social.default-scopes.tooltip=Autorizacijos metu siun\u010Diamos taikymo sritys. Galim\u0173 reik\u0161mi\u0173 s\u0105ra\u0161o, skirtuko ir numatytos reik\u0161m\u0117s ie\u0161kokite tapatyb\u0117s teik\u0117jo sistemos dokumentacijoje.. +key=Raktas +stackoverflow.key.tooltip=Stack Overflow kliento registracijos metu gautas raktas. + +# User federation +sync-ldap-roles-to-keycloak=Sinchronizuoti LDAP roles \u012F Keycloak +sync-keycloak-roles-to-ldap=Sinchronizuoti Keycloak roles \u012F LDAP +sync-ldap-groups-to-keycloak=Sinchronizuoti LDAP grupes \u012F Keycloak +sync-keycloak-groups-to-ldap=Sinchronizuoti Keycloak grupes \u012F LDAP + +realms=Sritys +realm=Sritis + +identity-provider-mappers=Tapatyb\u0117s teik\u0117jo atitikmen\u0173 susiejimai +create-identity-provider-mapper=Sukurti tapatyb\u0117s teik\u0117jo atitikmens susiejim\u0105 +add-identity-provider-mapper=Prid\u0117ti tapatyb\u0117s teik\u0117jo atitikmens susiejim\u0105 +client.description.tooltip=Nurodomas kliento apra\u0161as. Pavyzd\u017Eiui 'Mano laiko lenteli\u0173 klientas'. Palaikomos lokalizuotos reik\u0161m\u0117s. Pavyzd\u017Eiui\: ${my_client_description} + +expires=Galioja iki +expiration=Galiojimas +expiration.tooltip=Nurodykite kiek laiko galios prieigos raktas +count=Kiekis +count.tooltip=Nurodykite kiek klient\u0173 gali b\u016Bti sukurti naudojant prieigos rakt\u0105 +remainingCount=Lik\u0119s kiekis +created=Sukurta +back=Atgal +initial-access-tokens=Pradiniai prieigos raktai +add-initial-access-tokens=Prid\u0117ti pradin\u012F prieigos rakt\u0105 +initial-access-token=Pradinis prieigos raktas +initial-access.copyPaste.tooltip=Nukopijuokite ir \u012Fklijuokite prieigos rakt\u0105 prie\u0161 i\u0161eidami i\u0161 \u0161io puslapio. V\u0117liau negal\u0117site kopijuoti \u0161i\u0173 prieigos rakt\u0173. +continue=T\u0119sti +initial-access-token.confirm.title=Kopijuoti pradinius prieigos raktus +initial-access-token.confirm.text=Pra\u0161ome \u012Fsitikinti, kad nusikopijavote pradinius prieigos raktus nes v\u0117liau prie rakt\u0173 nebegal\u0117site prieiti +no-initial-access-available=N\u0117ra galim\u0173 pradini\u0173 prieigos rak\u0161\u0173 + +trusted-hosts-legend=Patikimi kliento registracijos serveriai +trusted-hosts-legend.tooltip=Serveri\u0173 vardai, kuriais pasitikima kliento registracijos metu. Klient\u0173 registravimo u\u017Eklausos i\u0161 \u0161i\u0173 serveri\u0173 gali b\u016Bti siun\u010Diamos be pradini\u0173 prieigos rakt\u0173. Klient\u0173 registracijos skai\u010Dius ribojamas pagal nurodyt\u0105 kiekvieno serverio limit\u0105. +no-client-trusted-hosts-available=N\u0117ra galim\u0173 patikim\u0173 serveri\u0173 +add-client-reg-trusted-host=Prid\u0117ti patikim\u0105 server\u012F +hostname=Serverio vardas +client-reg-hostname.tooltip=Pilnas serverio vardas arba IP adresas. Klient\u0173 registracijomis su \u0161iuo serverio vardu arba IP adresu bus pasitikima ir leid\u017Eiama nauj\u0173 klient\u0173 registracija. +client-reg-count.tooltip=Limitas, kiek registravimo u\u017Eklaus\u0173 galima atsi\u0173sti i\u0161 kiekvieno serverio. Limitas bus atkurtas tik po atk\u016Brimo. +client-reg-remainingCount.tooltip=I\u0161 \u0161io serverio lik\u0119s galim\u0173 registracijos u\u017Eklaus\u0173 skai\u010Dius. Limitas bus atkurtas tik po atk\u016Brimo. +reset-remaining-count=Atk\u016Brimo limit\u0105 + +client-templates=Klient\u0173 \u0161ablonai +client-templates.tooltip=Klient\u0173 \u0161ablonai leid\u017Eia nurodyti bendr\u0105 vis\u0173 klient\u0173 konfig\u016Bracij\u0105 + +groups=Grup\u0117s + +group.add-selected.tooltip=Grupei galimos priskirti srities rol\u0117s. +group.assigned-roles.tooltip=Su \u0161ia grupe susietos srities roles +group.effective-roles.tooltip=Visos srities susietos rol\u0117s. \u0160iame s\u0105ra\u0161e taip pat rodomos visos rol\u0117s, kurios priskirtos sud\u0117tin\u0117ms rol\u0117ms. +group.available-roles.tooltip=\u0160io kliento galimos susieti rol\u0117s. +group.assigned-roles-client.tooltip=Susietos \u0161io kliento rol\u0117s. +group.effective-roles-client.tooltip=Visos \u0161io kliento susietos rol\u0117s. \u0160iame s\u0105ra\u0161e taip pat rodomos visos rol\u0117s, kurios priskirtos sud\u0117tin\u0117ms rol\u0117ms. + +default-roles=Numatytosios rol\u0117s +no-realm-roles-available=Sritis neturi roli\u0173 + +users=Naudotojai +user.add-selected.tooltip=Naudotojui galimos priskirti srities rol\u0117s. +user.assigned-roles.tooltip=Su \u0161iuo naudotoju susietos srities rol\u0117s +user.effective-roles.tooltip=Visos srities susietos rol\u0117s. \u0160iame s\u0105ra\u0161e taip pat rodomos visos rol\u0117s, kurios priskirtos sud\u0117tin\u0117ms rol\u0117ms. +user.available-roles.tooltip=\u0160io kliento galimos susieti rol\u0117s. +user.assigned-roles-client.tooltip=Susietos \u0161io kliento rol\u0117s. +user.effective-roles-client.tooltip=Visos \u0161io kliento susietos rol\u0117s. \u0160iame s\u0105ra\u0161e taip pat rodomos visos rol\u0117s, kurios priskirtos sud\u0117tin\u0117ms rol\u0117ms. +default.available-roles.tooltip=Galimos priskirti srities rol\u0117s. +realm-default-roles=Numatytosios srities rol\u0117s +realm-default-roles.tooltip=Srities rol\u0117s, kurios automati\u0161kai priskiriamos naujiems naudotojams. +default.available-roles-client.tooltip=\u0160io kliento rol\u0117s, kurios automati\u0161kai gali b\u016Bti priskiriamos naudotojams. +client-default-roles=Numatytosios kliento rol\u0117s +client-default-roles.tooltip=Kliento rol\u0117s, kurios automati\u0161kai priskiriamos naujiems naudotojams. +composite.available-roles.tooltip=Srities rol\u0117s, kurias galima susieti su \u0161ia sud\u0117tine role. +composite.associated-roles.tooltip=Srities rol\u0117s, kurios susietos su \u0161ia sud\u0117tine role. +composite.available-roles-client.tooltip=Kliento rol\u0117s, kurias galima susieti su \u0161ia sud\u0117tine role. +composite.associated-roles-client.tooltip=Kliento rol\u0117s, kurios susietos su \u0161ia sud\u0117tine role. +partial-import=Dalinis duomen\u0173 importavimas +partial-import.tooltip=Dalinis duomen\u0173 importavimas leid\u017Eia \u012Fkelti prie\u0161 tai eksportuot\u0105 JSON rinkmen\u0105 su naudotojais, klientais ir kitais resursais. + +file=Rinkmena +exported-json-file=Eksportuota JSON rinkmena +import-from-realm=\u012Ekelti i\u0161 srities +import-users=\u012Ekelti naudotojus +import-groups=\u012Ekelti grupes +import-clients=\u012Ekelti klientus +import-identity-providers=\u012Ekelti tapatyb\u0117s teik\u0117jus +import-realm-roles=\u012Ekelti srities roles +import-client-roles=\u012Ekelti klient\u0173 roles +if-resource-exists=Jei resursas egzistuoja +fail=Nevykdyti +skip=Praleisti +overwrite=Perra\u0161yti +if-resource-exists.tooltip=Nurodykite k\u0105 daryti kuomet bandoma \u012Fkelti jau egzistuojant\u012F resurs\u0105. + +action=Veiksmas +role-selector=Roli\u0173 parinkimas +realm-roles.tooltip=Srities rol\u0117s, kurias galima pasirinkti. + +select-a-role=Pasirinkti rol\u0119 +select-realm-role=Pasirinkti srities rol\u0119 +client-roles.tooltip=Kliento rol\u0117s, kurias galite pa\u017Eym\u0117ti. +select-client-role=Pasirinkti kliento rol\u0119 + +client-template=Kliento \u0161ablonas +client-template.tooltip=Kliento \u0161ablonas, i\u0161 kurio paveldima konfig\u016Bracija +client-saml-endpoint=Kliento SAML adresas +add-client-template=Kliento \u0161ablono k\u016Brimas + +manage=Valdyti +authentication=Autentifikavimas +user-federation=Naudotoj\u0173 federavimas +user-storage=Naudotoj\u0173 saugykla +events=\u012Evykiai +realm-settings=Srities nustatymai +configure=Konfig\u016Bruoti +select-realm=Pasirinkite srit\u012F +add=Prid\u0117ti + +client-template.name.tooltip=Kliento \u0161ablono pavadinimas. Privalo b\u016Bti unikalus \u0161ioje srityje +client-template.description.tooltip=Kliento \u0161ablono apra\u0161ymas +client-template.protocol.tooltip=Kurio SSO protokolo konfig\u016Bracija teikia \u0161is \u0161ablonas + +add-user-federation-provider=Prid\u0117ti naudotoj\u0173 federacijos teik\u0117ja +required-settings=Privalomi nustatymai +provider-id=Teik\u0117jo ID +console-display-name=Konsol\u0117je rodomas pavadinimas +console-display-name.tooltip=Administravimo konsol\u0117je rodomas teik\u0117jo pavadinimas. +priority=Prioritetas +priority.tooltip=Skai\u010Dius nurodantis naudotojo paie\u0161kos \u0161iame federacijos teik\u0117juje prioritet\u0105. Pirmiausia imama su ma\u017Eesniu skai\u010Diumi. +sync-settings=Sinchronizuoti nustatymus +periodic-full-sync=Pilnas periodinis sinchronizavimas +periodic-full-sync.tooltip=Ar turi b\u016Bti atliekamas periodinis pilnas teik\u0117jo naudotoj\u0173 sinchronizavimas \u012F Keycloak? +full-sync-period=Pilno sinchronizavimo intervalas +full-sync-period.tooltip=Laikas sekund\u0117mis, kas kur\u012F atliekamas pilnas naudotoj\u0173 sinchronizavimas \u012F Keycloak sistem\u0105 +periodic-changed-users-sync=Periodinis pakeitim\u0173 sinchronizavimas +periodic-changed-users-sync.tooltip=Ar turi b\u016Bti atliekamas naujai u\u017Eregistruot\u0173 naudotoj\u0173 ar naudotoj\u0173 su redaguotais profilio duomenimis periodinis sinchronizavimas \u012F Keycloak? +changed-users-sync-period=Periodinis sinchronizavimo intervalas +changed-users-sync-period.tooltip=Laikas sekund\u0117mis, kas kur\u012F atliekamas naujai u\u017Eregistruot\u0173 naudotoj\u0173 ar naudotoj\u0173 su redaguotais profilio duomenimis sinchronizavimas \u012F Keycloak +synchronize-changed-users=Sinchronizuoti naudotoj\u0173 pakeitimus +synchronize-all-users=Sinchronizuoti visus naudotojus +kerberos-realm=Kerberos sritis +kerberos-realm.tooltip=Kerberos srities pavadinimas. Pavyzd\u017Eiui FOO.ORG +server-principal=Pagrindinis serveris +server-principal.tooltip=Pilnas HTTP paslaugai skirtas pagrindinio serverio su domenu pavadinimas. Pavyzd\u017Eiui HTTP/host.foo.org@FOO.ORG +keytab=KeyTab +keytab.tooltip=Kelias iki Kerberos KeyTab rinkmenos talpinan\u010Dios prisijungimo prie pagrindinio serverio duomenis. Pavyzd\u017Eiui /etc/krb5.keytab +debug=Derinti +debug.tooltip=Ar \u012Fgalinti Krb5LoginModule veikimo prane\u0161im\u0173 ra\u0161ym\u0105 \u012F standarin\u0119 i\u0161vest\u012F derinimo r\u0117\u017Eimu? +allow-password-authentication=Leisti autentifikacij\u0105 naudojant slapta\u017Eod\u012F +allow-password-authentication.tooltip=Ar suteikti galimyb\u0119 naudotojui prisijungti prie Kerberos naudojant naudotojo vard\u0105 ir slapta\u017Eod\u012F? +edit-mode=Pakeitim\u0173 r\u0117\u017Eimas +edit-mode.tooltip=READ_ONLY rei\u0161kia, kad naudotojui neleid\u017Eiama keisti slapta\u017Eod\u017Eio ir autentifikacija visuomet bus atliekama Kerberos. UNSYNCED rei\u0161kia, kad naudotojui leid\u017Eiama keisti slapta\u017Eod\u012F saugom\u0105 Keycloak duomen\u0173 baz\u0117je ir kuris bus naudojamas autentifikacijos metu vietoj Kerberos slapta\u017Eod\u017Eio. +ldap.edit-mode.tooltip=READ_ONLY rei\u0161kia, kad LDAP saugykla bus naudojama vien tik skaitymo r\u0117\u017Eimu. WRITABLE rei\u0161kia, kad duomenys sinchronizuojami atgal \u012F LDAP pagal poreik\u012F. UNSYNCED rei\u0161kia, kad naudotoj\u0173 duomenys bus importuoti, ta\u010Diau niekuomet nesinchronizuojami atgal \u012F LDAP. +update-profile-first-login=Pirmojo prisijungimo metu atnaujinti duomenis +update-profile-first-login.tooltip=Pirmojo prisijungimo metu atnaujinti naudotojo profilio duomenis +sync-registrations=Sinchronizuoti registracijas +ldap.sync-registrations.tooltip=Ar naujai u\u017Esiregistrav\u0119 naudotojai tur\u0117t\u0173 b\u016Bti sinchonizuojami \u012F LDAP saugykl\u0105? Federavimo teik\u0117jas, \u012F kur\u012F sinchronizuojami nauji naudotojai, parenkamas pagal prioritet\u0105. Pirmiausia imami su ma\u017Eiausiu skai\u010Diumi. +vendor=Gamintojas +ldap.vendor.tooltip=LDAP gamintojas (teik\u0117jas) +username-ldap-attribute=Prisijungimo vardo LDAP atributas +ldap-attribute-name-for-username=LDAP atributo pavadinimas, kuriame saugomas naudotojo prisijungimo vardas +username-ldap-attribute.tooltip=LDAP atributas, kuris turi b\u016Bti susietas su Keycloak naudotojo prisijungimo vardu. Su daugeliu LDAP serveri\u0173 gali b\u016Bti naudojamas 'uid' atributas. ActiveDirectory gali b\u016Bti 'sAMAccountName' arba 'cn'. Reikalaujama, kad nurodytas LDAP atributas b\u016Bt\u0173 u\u017Epildytas visiems naudotojams kurie importuojami i\u0161 LDAP \u012F Keycloak. +rdn-ldap-attribute=RDN LDAP atributas +ldap-attribute-name-for-user-rdn=LDAP atributo pavadinimas, kuriame saugomas naudotojo RDN +rdn-ldap-attribute.tooltip=LDAP atributas, kuris naudojamas kaip RDN (Relative Distinguished Name) vietoj tipinio naudotojo DN (Distinguished Name). Da\u017Eniausiai reik\u0161m\u0117 sutampa su prisijungimo vardo LDAP atributu, ta\u010Diau pastarasis n\u0117ra privalomas. Pavyzd\u017Eiui ActiveDirectory da\u017Eniausiai kaip RDN atributas naudojamas 'cn' nors prisijungimo vardo atributas b\u016Bna 'sAMAccountName'. +uuid-ldap-attribute=UUID LDAP atributas +ldap-attribute-name-for-uuid=LDAP atributo pavadinimas, kuriame saugomas UUID +uuid-ldap-attribute.tooltip=LDAP atributas, kuris naudojamas kaip unikalus LDAP objekt\u0173 identifikatorius (UUID). Daugelis LDAP serveri\u0173 naudoja 'entryUUID', ta\u010Diau pasitaiko ir i\u0161im\u010Di\u0173. Pavyzd\u017Eiui ActiveDirectory naudojamas 'objectGUID' atributas. Jei j\u016Bs\u0173 LDAP serveris nepalaiko UUID atribut\u0173, tuomet galite naudoti bet kur\u012F kit\u0105 atribut\u0105 kuris u\u017Etikrina LDAP naudotoj\u0173 unikalum\u0105. Pavyzd\u017Eiui 'uid' arba 'entryDN'. +user-object-classes=Naudotoj\u0173 objekt\u0173 klas\u0117s +ldap-user-object-classes.placeholder=LDAP naudotoj\u0173 objekt\u0173 klas\u0117s (skiriamos kableliu) +ldap.user-object-classes.tooltip=LDAP atributo objectClass atskirtos kableliu reikm\u0117s skirtos LDAP naudotojo objektui. Pavyzd\u017Eiui: 'inetOrgPerson, organizationalPerson' . Naujai registruoti Keycloak naudotojai bus \u012Fra\u0161yti \u0161 LDAP su visomis nurodytomis objekt\u0173 klas\u0117mis. Egzistuojantys LDAP naudotoj\u0173 objektai randami tik tuomet, kai jie turi visas \u0161ias nurodytas objekto klases. + +ldap-connection-url=LDAP jungties URL +ldap-users-dn=LDAP naudotoj\u0173 DN +ldap-bind-dn=LDAP prisijungimo DN +ldap-bind-credentials=LDAP prisijungimo slapta\u017Eodis +ldap-filter=LDAP filtras + +connection-url=Jungties URL +ldap.connection-url.tooltip=Jungties \u012F LDAP server\u012F URL +test-connection=Tikrinti jungt\u012F +users-dn=Naudotoj\u0173 DN +ldap.users-dn.tooltip=\u0160aknin\u0117 LDAP med\u017Eio DN (Distinguished Name) kuriame saugomi naudotojai. Jei pavyzd\u017Eiui tipinio naudotojo DN yra 'uid=john,ou=users,dc=example,dc=com' tuomet \u0161io atributo reik\u0161m\u0117 tur\u0117t\u0173 b\u016Bti 'ou=users,dc=example,dc=com' +authentication-type=Autentifikacijos tipas +ldap.authentication-type.tooltip=LDAP autentifikacijos tipas. Galimi 'none' (anonimin\u0117 LDAP prieiga) arba 'simple' (Prisijungimo DN + slapta\u017Eodis autentifikacijai) autentifikacijos b\u016Bdai +bind-dn=Prisijungimo DN +ldap.bind-dn.tooltip=LDAP administratoriaus DN (Distinguished Name), kuris turi b\u016Bti naudojamas Keycloak prieiti prie LDAP serverio +bind-credential=Prisijungimo slapta\u017Eodis +ldap.bind-credential.tooltip=LDAP administratoriaus slapta\u017Eodis +test-authentication=Tikrinti autentifikacij\u0105 +custom-user-ldap-filter=Papildomas naudotoj\u0173 LDAP filtras +ldap.custom-user-ldap-filter.tooltip=Papildomas LDAP filtras, kuris turi b\u016Bti naudojamas surast\u0173 naudotoj\u0173 nufiltravimui. Palikite tu\u0161\u010Di\u0105 lauk\u0105 jei papildomas filtravimas nereikalingas. \u012Esitikinkite, kad filtras prasideda '(' ir baigiasi ')' simboliais +search-scope=Paie\u0161kos apimtis +ldap.search-scope.tooltip=Jei pasirinkta vieno lygio paie\u0161ka, tuomet naudotoj\u0173 ie\u0161koma vien tik nurodytame naudotoj\u0173 DN. Kai pasirinkta paie\u0161ka medyje, tuomet naudotoj\u0173 ie\u0161koma visose med\u017Eio \u0161akose. I\u0161samesn\u0117s informacijos ie\u0161kokite LDAP dokumentacij\u0105 +use-truststore-spi=Nuadoti rakt\u0173 saugyklos SPI +ldap.use-truststore-spi.tooltip=Nurodykite, kuomet LDAP jungtis naudos keycloak-server.json sukonfig\u016Bruot\u0105 patikim\u0173 liudijim\u0173/rakt\u0173 saugyklos SPI. 'Visada' rei\u0161kia, kad bus naudojama visada. 'Niekada' rei\u0161kia, kad sukonfig\u016Bruota liudijim\u0173 saugykla nebus naudojama. 'Tik LDAP' rei\u0161kia, kad saugykla bus naudojama tik su LDAP jungtimis. Pastaba: jei keycloak-server.json nesukonfig\u016Bruotas, tuomet bus naudojama standartin\u0117 Java cacerts arba 'javax.net.ssl.trustStore' parametre nurodyta liudijim\u0173 saugykla. +connection-pooling=Jung\u010Di\u0173 buferizavimas +ldap.connection-pooling.tooltip=Ar Keycloak tur\u0117t\u0173 naudoti jung\u010Di\u0173 telkin\u012F jungiantis prie LDAP serverio? +ldap.pagination.tooltip=Ar LDAP serveris palaiko puslapiavim\u0105? +kerberos-integration=Kerberos intergacija +allow-kerberos-authentication=Leisti Kerberos autentifikacij\u0105 +ldap.allow-kerberos-authentication.tooltip=\u012Egalina HTTP naudotoj\u0173 autentifikacij\u0105 naudojant SPNEGO/Kerberos raktus. Duomenys apie prisijungus\u012F naudotoj\u0105 bus teikiama \u0161io LDAP serverio +use-kerberos-for-password-authentication=Naudoti Kerberos autentifikacijai su slapta\u017Eod\u017Eiu +ldap.use-kerberos-for-password-authentication.tooltip=Ar jungiantis su naudotojo vardu ir slapta\u017Eod\u017Eiu naudoti Kerberos server\u012F vietoj LDAP serverio Directory Service API +batch-size=Paketo dydis +ldap.batch-size.tooltip=Vienos tranzacijos metu \u012F Keycloak importuojam\u0173 LDAP naudotoj\u0173 skai\u010Dius. +ldap.periodic-full-sync.tooltip=Ar \u012Fgalinti periodin\u0119 piln\u0105 LDAP naudotoj\u0173 sinchronizacij\u0105 \u012F Keycloak? +ldap.periodic-changed-users-sync.tooltip=Ar \u012Fgalinti periodin\u0119 naujai registruot\u0173 arba su pakeistais duomenimis LDAP naudotoj\u0173 sinchronizacij\u0105 \u012F Keycloak? +ldap.changed-users-sync-period.tooltip=Intervalas sekund\u0117mis, kas kur\u012F atliekamas periodinis naujai registruot\u0173 arba su pakeistais duomenimis LDAP naudotoj\u0173 sinchronizavimas \u012F Keycloak +user-federation-mappers=Federuoto naudotojo atribut\u0173 atitikmenys +create-user-federation-mapper=Sukurti federuoto naudotojo atributo atitikmen\u012F +add-user-federation-mapper=Prid\u0117ti federuoto naudotojo atributo atitikmen\u012F +provider-name=Teik\u0117jo pavadinimas +no-user-federation-providers-configured=Nesukonfig\u016Bruotas nei vienas naudotoj\u0173 federacijos teik\u0117jas +no-user-storage-providers-configured=Nesukonfig\u016Bruota nei viena naudotoj\u0173 saugykla +add-identity-provider=Prid\u0117ti tapatyb\u0117s teik\u0117j\u0105 +add-identity-provider-link=Prid\u0117ti s\u0105saj\u0105 su tapatyb\u0117s teik\u0117ju +identity-provider=Tapatyb\u0117s teik\u0117jas +identity-provider-user-id=Tapatyb\u0117s teik\u0117jo naudotojo ID +identity-provider-user-id.tooltip=Unikalus, tapatyb\u0117s teik\u0117jo saugomas, naudotojo ID +identity-provider-username=Tapatyb\u0117s teik\u0117jo naudotojo vardas +identity-provider-username.tooltip=Tapatyb\u0117s teik\u0117jo sistemoje saugomas naudotojo vardas +pagination=Puslapiavimas + +browser-flow=Autentifikacijos seka +browser-flow.tooltip=Pasirinkite autentifikacijos nar\u0161ykl\u0117je sek\u0105 +registration-flow=Registracijos seka +registration-flow.tooltip=Pasirinkite registracijos nar\u0161ykl\u0117je sek\u0105. +direct-grant-flow=Tiesiogini\u0173 teisi\u0173 seka +direct-grant-flow.tooltip=Pasirinkite tiesiogini\u0173 teisi\u0173 sek\u0105 (direct grant authentication). +reset-credentials=Prisijungimo duomen\u0173 atk\u016Brimo seka +reset-credentials.tooltip=Pasirinkite prisijungimo duomen\u0173 priminimo nar\u0161ykl\u0117je sek\u0105 +client-authentication=Klient\u0173 autentifikacijos seka +client-authentication.tooltip=Pasirinkite klient\u0173 autentifikacijos sek\u0105. +new=Naujas +copy=Kopijuoti +add-execution=Prid\u0117ti i\u0161imt\u012F +add-flow=Prid\u0117ti sek\u0105 +auth-type=Autentifikacijos tipas +requirement=Privalomumas +config=Konfig\u016Bruoti +no-executions-available=N\u0117ra sukonfig\u016Bruot\u0173 i\u0161im\u010Di\u0173 +authentication-flows=Autentifikacijos sekos +create-authenticator-config=Sukurti autentifikatoriaus konfig\u016Bracij\u0105 +authenticator.alias.tooltip=Konfig\u016Bracijos pavadinimas +otp-type=OTP tipas +time-based=Paremtas laiku +counter-based=Paremtas skaitliuku +otp-type.tooltip='totp' paremtas ribot\u0105 laik\u0105 galiojan\u010Diu vienkartiniu slapta\u017Eod\u017Eiu. 'hotp' ribot\u0105 kart\u0173 galiojan\u010Diu vienkartiniu slapta\u017Eod\u017Eiu. +otp-hash-algorithm=OTP mai\u0161os algoritmas +otp-hash-algorithm.tooltip=Kuris mai\u0161os algoritmas turi b\u016Bti naudojamas OTP generavimui. +number-of-digits=Skaitmen\u0173 skai\u010Dius +otp.number-of-digits.tooltip=Kiek OTP tur\u0117t\u0173 tur\u0117ti skaitmen\u0173? +look-ahead-window=Neatitikimo langas +otp.look-ahead-window.tooltip=Koks intervalas yra leid\u017Eiamas tuo atveju, kai prieigos rakt\u0173 generatoriaus ir serverio laikai arba skaitliukai nesutampa. +initial-counter=Pradin\u0117 skaitliuko reik\u0161m\u0117 +otp.initial-counter.tooltip=Kokia turi b\u016Bti pradin\u0117 skaitliuko reik\u0161m\u0117? +otp-token-period=OTP rakto galiojimo intervalas +otp-token-period.tooltip=Kiek sekund\u017Eiu galios OTP prieigos raktas? Numatyta reik\u0161m\u0117 30 sekund\u017Ei\u0173. +table-of-password-policies=Slapta\u017Eod\u017Eio taisykli\u0173 lentel\u0117 +add-policy.placeholder=Prid\u0117ti taisykl\u0119... +policy-type=Taisykl\u0117s tipas +policy-value=Taisykl\u0117s reik\u0161m\u0117 +admin-events=Administravimo \u012Fvykiai +admin-events.tooltip=Rodomi srities administravimo \u012Fvykiai susij\u0119 su administratoriaus paskyra, pvz. srities k\u016Brimas. Pasirinkite konfig\u016Bravimo skilt\u012F nor\u0117dami kad \u012Fvykiai b\u016Bt\u0173 saugomi. +login-events=Prisijungimo \u012Fvykiai +filter=Filtruoti +update=Atnaujinti +reset=I\u0161valyti +operation-types=Veiksmas +select-operations.placeholder=Pasirinkite veiksmus... +resource-path=Resurso kelias +resource-path.tooltip=Filtravimas pagal resurso keli\u0105. Palaikomas pakaitos simbolis '*' atitinkantis vien\u0105 kelio element\u0105 ir '**' daugiau nei vien\u0105 element\u0105. Pavyzd\u017Eiui 'realms/*/clients/asbc' visose sritise randa klient\u0105 su identifikatoriumi 'asbc'. Kitas pavyzdys 'realms/master/**' randa visus veiksmus 'master' srityje. +date-(from)=Data (Nuo) +date-(to)=Data (Iki) +authentication-details=Autentifikacijos informacija +ip-address=IP adresas +time=Laikas +operation-type=Veiksmo tipas +auth=Autentifikacijos informacija +representation=Reprezentacija +register=Registracijos +required-action=Privalomi veiksmai +default-action=Numatytas veiksmas +auth.default-action.tooltip=Jei \u012Fgalintas, tuomet visi nauji naudotojai prival\u0117s atlikti pa\u017Eym\u0117tus veiksmus. +no-required-actions-configured=N\u0117ra nei vieno sukonfig\u016Bruoto privalomo veiksmo +defaults-to-id=Nenurod\u017Eius bus naudojamas identifikatorius +flows=Sekos +bindings=S\u0105ry\u0161iai +required-actions=Privalomi veiksmai +password-policy=Slapta\u017Eod\u017Ei\u0173 taisykl\u0117s +otp-policy=OTP taisykl\u0117s +user-groups=Naudotoj\u0173 grup\u0117s +default-groups=Numatytos grup\u0117s +groups.default-groups.tooltip=Nurodykite grupes, \u012F kurias automati\u0161kai \u012Ftraukiami nauji naudotojai. +cut=I\u0161kirpti +paste=\u012Eklijuoti + +create-group=Sukurti grup\u0119 +create-authenticator-execution=Sukurti autentifikatoriaus veiksmo vykdym\u0105 +create-form-action-execution=Sukurti formos veiksmo vykdym\u0105 +create-top-level-form=Sukurti auk\u0161\u010Diausio lygio form\u0105 +flow.alias.tooltip=\u012Era\u0161ykite sekos rodom\u0105 pavadinim\u0105. +top-level-flow-type=Auk\u0161\u010Diausio lygio sekos tipas +flow.generic=generic +flow.client=client +top-level-flow-type.tooltip=Kokios tipo \u0161i auk\u0161\u010Diausio lygio sritis? 'client' tipas naudojamas klient\u0173 (program\u0173) autentifikacijai. 'generic' naudojamas visais kitais atvejais. +create-execution-flow=Sukurti vykdymo sek\u0105 +flow-type=Sekos tipas +flow.form.type=form +flow-type.tooltip=Kokios r\u016B\u0161ies \u0161i forma? +form-provider=Formos teik\u0117jas +default-groups.tooltip=Naujai sukurti ar u\u017Eregistruoti naudotojai automati\u0161kai priskiriami \u0161ioms grup\u0117ms +select-a-type.placeholder=pasirinkite tip\u0105 +available-groups=Galimos grup\u0117s +available-groups.tooltip=Nurodykite grup\u0119, kuri bus numatytoji. +value=Reik\u0161m\u0117 +table-of-group-members=Grup\u0117s nari\u0173 lentel\u0117 +last-name=Pavard\u0117 +first-name=Vardas +email=El. pa\u0161tas +toggle-navigation=Perjungti navigacij\u0105 +manage-account=Valdyti paskyr\u0105 +sign-out=Atsijungti +server-info=Serverio informacija +resource-not-found=Resuras nerastas... +resource-not-found.instruction=Negalime rasti j\u016Bs\u0173 ie\u0161komo resurso. \u012Esitikinkite, kad \u012Fved\u0117te teising\u0105 URL. +go-to-the-home-page=Eiti \u012F pradin\u012F puslap\u012F » +page-not-found=Puslapis nerastas... +page-not-found.instruction=Negalime rasti j\u016Bs\u0173 ie\u0161komo puslapio. \u012Esitikinkite, kad \u012Fved\u0117te teising\u0105 URL. +events.tooltip=Rodomi srities i\u0161saugoti \u012Fvykiai. Rodomi \u012Fvykiai susij\u0119 su naudotoj\u0173 paskyromis, pavyzd\u017Eiui naudotojo prisijungimas. Nor\u0117dami keisti nustatymus pasirinkite 'Konfig\u016Bruoti' +select-event-types.placeholder=Pasirinkite \u012Fvykiu tipus... +events-config.tooltip=Rodoma naudotoj\u0173 ir administravimo \u012Fvyki\u0173 konfig\u016Bracija. +select-an-action.placeholder=Pasirinkite veiksm\u0105... +event-listeners.tooltip=Nurodykite srities \u012Fvyki\u0173 gav\u0117jus. +login.save-events.tooltip=Jei \u012Fgalinta, tuomet su prisijungimu susij\u0119 veiksmai saugomi duomen\u0173 baz\u0117je ir tampa prieinami per administravimo bei naudotojo paskyros valdymo skydus. +clear-events.tooltip=I\u0161trinti visus \u012Fvykius i\u0161 duomen\u0173 baz\u0117s. +events.expiration.tooltip=Nustato \u012Fvyki\u0173 galiojimo laik\u0105. Nebegaliojantys \u012Fvykiai periodi\u0161kai i\u0161trinami i\u0161 duomen\u0173 baz\u0117s. +admin-events-settings=Administravimo veiksm\u0173 nustatymai +save-events=Saugoti \u012Fvykius +admin.save-events.tooltip=Jei \u012Fgalinta, tuomet administravimo veiksmai saugomi duomen\u0173 baz\u0117je ir tampa prieinami per administravimo valdymo skyd\u0105. +saved-types.tooltip=Nurodykite veiksm\u0173 tipus, kurie tur\u0117t\u0173 b\u016Bti i\u0161saugoti. +include-representation=I\u0161saugoti reprezentacij\u0105 +include-representation.tooltip=I\u0161saugoti kur\u016Bmo ir redagavimo u\u017Eklaus\u0173 JSON reprezentacij\u0105. +clear-admin-events.tooltip=I\u0161trina visus su administravimu susijusius veiksmus i\u0161 duomen\u0173 baz\u0117s. +server-version=Serverio versija +info=Informacija +providers=Teik\u0117jai +server-time=Serverio laikas +server-uptime=Serverio veikimo laikas +memory=Atmintis +total-memory=Viso atminties +free-memory=Laisva atmintis +used-memory=Naudojama atmintis +system=Sistema +current-working-directory=Darbinis katalogas +java-version=Java Version +java-vendor=Java Vendor +java-runtime=Java Runtime +java-vm=Java VM +java-vm-version=Java VM Version +java-home=Java Home +user-name=User Name +user-timezone=User Timezone +user-locale=User Locale +system-encoding=System Encoding +operating-system=Operating System +os-architecture=OS Architecture +spi=SPI +granted-roles=Suteiktos rol\u0117s +granted-protocol-mappers=Suteiktos protokolo atitikmen\u0173 s\u0105sajos +additional-grants=Papildomai suteikta +revoke=At\u0161aukti +new-password=Naujas slapta\u017Eodis +password-confirmation=Pakartotas slapta\u017Eodis +reset-password=Pakeisti slapta\u017Eod\u012F +credentials.temporary.tooltip=Jei \u012Fgalinta, tuomet naudotojas prival\u0117s pasikeisti slapta\u017Eod\u012F sekan\u010Dio prisijungimo metu +remove-totp=\u0160alinti TOTP +credentials.remove-totp.tooltip=\u0160alinti vienkartin\u012F naudotojo slapta\u017Eod\u017Ei\u0173 generatori\u0173. +reset-actions=Atkurti veiksmus +credentials.reset-actions.tooltip=Nurodykite naudotojui el. pa\u0161tu siun\u010Diamus privalomus atlikti veiksmus. 'Patvirtinti el. pa\u0161to adres\u0105' \u012F naudotojo el. pa\u0161to adres\u0105 siun\u010Dia patvirtinimo nuorod\u0105. 'Atnaujinti profilio informacij\u0105' reikalauja naudotojo per\u017Ei\u016Br\u0117ti ir atnaujinti profilio informacij\u0105. 'Atnaujinti slapta\u017Eod\u012F' reikalauja naudotojo pasikeisti slapta\u017Eod\u012F. 'Konfig\u016Bruoti TOTP' reikalauja atnaujinti mobilaus slapta\u017Eod\u017Ei\u0173 generatoriaus konfig\u016Bracij\u0105. +reset-actions-email=Atk\u016Brimo veiksm\u0173 siuntimas +send-email=Si\u0173sti el. pa\u0161to lai\u0161k\u0105 +credentials.reset-actions-email.tooltip=Naudotojui siun\u010Diamas el. pa\u0161to lai\u0161kas su nuorodomis leid\u017Eian\u010Diomis atlikti pasirinktus veiksmus. Naudotojas atidar\u0119s siun\u010Diam\u0105 nuorod\u0105 gal\u0117s atlikti atk\u016Brimo veiksmus. Veism\u0173 atlikimui naudotoj\u0173 nebus reikalaujama prisijungti. Pavyzd\u017Eiui parinkus slapta\u017Eod\u017Eio atk\u016Brimo veiksm\u0105, naudotojas gal\u0117s neprisijung\u0119s nurodyti nauj\u0105 slapta\u017Ed\u012F. +add-user=Prid\u0117ti naudotoj\u0105 +created-at=Suk\u016Brimo data +user-enabled=Naudotojas \u012Fgalintas +user-enabled.tooltip=Ne\u012Fgalintam naudotojai neleid\u017Eiama prisijungti prie sistemos. +user-temporarily-locked=Naudotojas laikinai u\u017Erakintas +user-temporarily-locked.tooltip=Naudotojas laikintai u\u017Erakintas, nes per daug klydo prisijungiant prie sistemos. +unlock-user=Atrakinti naudotoj\u0105 +federation-link=Federacijos s\u0105saja +email-verified=El. pa\u0161tas patvirtintas +email-verified.tooltip=Ar naudotojo el. pa\u0161to adresas yra patvirtintas? +required-user-actions=Privalomi veiksmai naudotojui +required-user-actions.tooltip=Nurodykite kuriuos veiksmus po prisijungimo naudotojas privalo atlikti. 'Patvirtinti el. pa\u0161to adres\u0105' \u012F naudotojo el. pa\u0161to adres\u0105 siun\u010Dia patvirtinimo nuorod\u0105. 'Atnaujinti profilio informacij\u0105' reikalauja naudotojo per\u017Ei\u016Br\u0117ti ir atnaujinti profilio informacij\u0105. 'Atnaujinti slapta\u017Eod\u012F' reikalauja naudotojo pasikeisti slapta\u017Eod\u012F. 'Konfig\u016Bruoti TOTP' reikalauja atnaujinti mobilaus slapta\u017Eod\u017Ei\u0173 generatoriaus konfig\u016Bracij\u0105. +locale=Lokal\u0117 +select-one.placeholder=Pasirinkite... +impersonate=\u012Ek\u016Bnyti +impersonate-user=\u012Ek\u016Bnyti naudotoj\u0105 +impersonate-user.tooltip=Prisijungti kaip \u0161is naudotojas. Jei j\u016Bs\u0173 sritis sutampa su naudotojo sritimi, tuomet j\u016Bs\u0173 sesija bus baigta prie\u0161 prisijungiant \u0161iuo naudotoju. +identity-provider-alias=Tapatyb\u0117s teik\u0117jo pseudonimas +provider-user-id=Teik\u0117jo naudotojo ID +provider-username=Teik\u0117jo naudotojo vardas +no-identity-provider-links-available=N\u0117ra nei vienos tapatyb\u0117s teik\u0117jo s\u0105sajos +group-membership=Naryst\u0117 grup\u0117se +group-membership.tooltip=Visos grup\u0117s, kuri\u0173 narys yra \u0161is naudotojas. Pa\u017Eym\u0117kite grup\u0119 ir paspauskite 'Palikti' nor\u0117dami pa\u0161alinti naudotoj\u0105 i\u0161 grup\u0117s. +leave=Palikti +membership.available-groups.tooltip=Grup\u0117s, \u012F kurias galima \u012Ftraukti naudotoj\u0105. Pa\u017Eym\u0117kite grup\u0119 ir paspauskite \u012Ftraukti. +table-of-realm-users=Srities naudotoj\u0173 s\u0105ra\u0161as +view-all-users=Rodyti visus naudotojus +unlock-users=Atrakinti naudotojus +no-users-available=Naudotoj\u0173 n\u0117ra +users.instruction=\u012Eveskite paie\u0161kos kriterij\u0173 arba paspauskite rodyti visus naudotojus +consents=Sutikimai +started=Prad\u0117ta +logout-all-sessions=Atjungti visas sesijas +logout=Seanso pabaiga +new-name=Naujas pavadinimas +ok=Gerai +attributes=Atributai +role-mappings=Roli\u0173 susiejimas +members=Nariai +details=Detaliau +identity-provider-links=S\u0105sajos su tapatyb\u0117s teik\u0117jais +register-required-action=Registruoti privalom\u0105 atlikti veiksm\u0105 +gender=Lytis +address=Adresas +phone=Telefonas +profile-url=Profilio URL +picture-url=Nuotraukos URL +website=Internetin\u0117 svetain\u0117 +import-keys-and-cert=Importuoti raktus ir sertifikatus +import-keys-and-cert.tooltip=\u012Ekelti kliento rakt\u0173 por\u0105 ir sertifikat\u0105. +upload-keys=\u012Ekelti raktus +download-keys-and-cert=Atsisi\u0173sti raktus ir sertifikat\u0105 +no-value-assigned.placeholder=N\u0117ra priskirtos reik\u0161m\u0117s +remove=\u0160alinti +no-group-members=Grup\u0117 neturi nari\u0173 +temporary=Laikinas +join=Prijungti +event-type=\u012Evykio tipas +events-config=\u012Evyki\u0173 konfig\u016Bracija +event-listeners=\u012Evyki\u0173 gav\u0117jai +login-events-settings=Prisijungimo \u012Fvyki\u0173 nustatymai +clear-events=I\u0161valyti \u012Fvykius +saved-types=Saugomi tipai +clear-admin-events=I\u0161valyti administravimo \u012Fvykius +clear-changes=I\u0161valyti pasikeitimus +error=Klaida + +# Authz +# Authz Common +authz-authorization=Autorizacija +authz-owner=Savininkas +authz-uri=URI +authz-scopes=Taikymo sritys +authz-resource=Resursas +authz-resource-type=Resurso tipas +authz-resources=Resursai +authz-scope=Taikymo sritis +authz-authz-scopes=Autorizacijos taikymo sritys +authz-policies=Taisykl\u0117s +authz-permissions=Leidimai +authz-evaluate=I\u0161bandyti +authz-icon-uri=Ikonos URI +authz-icon-uri.tooltip=Ikonos paveiksliuko URI. +authz-select-scope=Parinkite taikymo srit\u012F +authz-select-resource=Parinkite resurs\u0105 +authz-associated-policies=Susietos taisykl\u0117s +authz-any-resource=Bet kuris resursas +authz-any-scope=Bet kuri taikymo sritis +authz-any-role=Bet kuri rol\u0117 +authz-policy-evaluation=I\u0161bandyti taisykl\u0119 +authz-select-client=Parinkite klient\u0105 +authz-select-user=Parinkite naudotoj\u0105 +authz-entitlements=Teis\u0117s +authz-no-resources=Resurs\u0173 n\u0117ra +authz-result=Rezultatas +authz-authorization-services-enabled=Autorizacija \u012Fgalinta +authz-authorization-services-enabled.tooltip=\u012Egalinti detal\u0173 kliento autorizacijos palaikym\u0105 +authz-required=Privalimas + +# Authz Settings +authz-import-config.tooltip=Importuoti \u0161io resurs\u0173 serverio autorizacijos nustatym\u0173 JSON rinkmen\u0105. + +authz-policy-enforcement-mode=Taisykli\u0173 vykdymo r\u0117\u017Eimas +authz-policy-enforcement-mode.tooltip=Taisykli\u0173 vykdymo r\u0117\u017Eimas nusako kaip turi b\u016Bti tenkinamos autorizacijos u\u017Eklaus\u0173 taisykl\u0117s. 'Taikyti' rei\u0161kia, kad tuo atveju kai n\u0117ra sukonfig\u016Bruota nei viena su resursu susijusi taisykl\u0117, prieiga draud\u017Eiama. 'Liberalus' rei\u0161kia, kad tuo atveju kai n\u0117ra sukonfig\u016Bruota nei viena su resursu susijusi taisykl\u0117, prieiga leid\u017Eiama. 'I\u0161jungta' rei\u0161kia, kad neatliekamas taisykli\u0173 tikrinimas ir prieiga leid\u017Eiama prie vis\u0173 resurs\u0173. +authz-policy-enforcement-mode-enforcing=Taikyti +authz-policy-enforcement-mode-permissive=Liberalus +authz-policy-enforcement-mode-disabled=I\u0161jungta + +authz-remote-resource-management=Nuotolinis resurs\u0173 valdymas +authz-remote-resource-management.tooltip=Ar leid\u017Eiama nuotoliniu b\u016Bdu resurs\u0173 serveriui valdyti resursus? Jei ne\u012Fgalinta, tuomet resursai gali b\u016Bti valdomi tik per \u0161i\u0105 administravimo konsol\u0119. + +authz-export-settings=Eksportuoti nustatymus +authz-export-settings.tooltip=Eksportuoti ir atsisi\u0173sti visus \u0161io resurs\u0173 serverio autorazacijos nustatymus. + +# Authz Resource List +authz-no-resources-available=N\u0117ra galim\u0173 resurs\u0173. +authz-no-scopes-assigned=N\u0117ra susiet\u0173 taikymo sri\u010Di\u0173. +authz-no-type-defined=N\u0117ra nurodyt\u0173 tip\u0173. +authz-no-permission-assigned=Nera susiet\u0173 leidim\u0173. +authz-no-policy-assigned=N\u0117ra susiet\u0173 taisykli\u0173. +authz-create-permission=Sukurti leidim\u0105 + +# Authz Resource Detail +authz-add-resource=Prid\u0117ti resurs\u0105 +authz-resource-name.tooltip=Unikalus resurso vardas. Vardas turi unikaliai identifikuoti resurs\u0105. Naudingas, kuomet ie\u0161koma specifini\u0173 resurs\u0173. +authz-resource-owner.tooltip=\u0160io resurso savininkas. +authz-resource-type.tooltip=\u0160io resurso tipas. Reik\u0161m\u0117 leid\u017Eia sugrupuoti skirtingus resursus turin\u010Dius t\u0105 pat\u012F tip\u0105. +authz-resource-uri.tooltip=URI kuris taip pat gali b\u016Bti naudojamas vienareik\u0161mi\u0161kam resurso identifikavimui. +authz-resource-scopes.tooltip=Su \u0161iuo resursu susietos taikymo sritys. + +# Authz Scope List +authz-add-scope=Pri\u0117ti taikymo srit\u012F +authz-no-scopes-available=N\u0117ra galim\u0173 taikymo sri\u010Di\u0173. + +# Authz Scope Detail +authz-scope-name.tooltip=Unikalus taikymo srities pavadinimas. \u0160is pavadinimas gali vienareik\u0161mi\u0161kai identifikuoti taikymo srit\u012F. Naudingas kuomet ie\u0161koma \u0161ios tam tikros srities. + +# Authz Policy List +authz-all-types=Visi tipai +authz-create-policy=Sukurti taisykl\u0119 +authz-no-policies-available=N\u0117ra galim\u0173 taisykli\u0173. + +# Authz Policy Detail +authz-policy-name.tooltip=\u0160ios taisykl\u0117s pavadinimas. +authz-policy-description.tooltip=\u0160ios taisykl\u0117s apra\u0161ymas. +authz-policy-logic=Logika +authz-policy-logic-positive=Teigiama +authz-policy-logic-negative=Neigiama +authz-policy-logic.tooltip=Logika nurodo kaip turi b\u016Bti tenkinama taisykl\u0117. Jei nurodyta 'Teigiama', tuomet \u0161ios taisykl\u0117s vykdymo metu gautas rezultatas (leisti arba drausti) bus naudojamas sprendinio pri\u0117mimui. Jei nurodyta 'Neigiama', tuomet \u0161ios taisykl\u0117s vykdymo rezultatas bus paneigtas, t.y. leid\u017Eiama taps draud\u017Eiama ir atvirk\u0161\u010Diai. +authz-policy-apply-policy=Pritaikyti taisykl\u0119 +authz-policy-apply-policy.tooltip=Nurodo visas taisykles, kurios turi b\u016Bti \u012Fvertintos \u0161ios taisykl\u0117s ar leidimo taikymo sri\u010Diai. +authz-policy-decision-strategy=Sprendimo strategija +authz-policy-decision-strategy.tooltip=Sprendimo strategija nurodo kaip priimamas galutinis sprendimas, kuomet yra vykdomos visos \u0161io leidimo taisykl\u0117s. 'Pozityvi' rei\u0161kia, kad galutiniam teigiamam sprendimui turi b\u016Bti tenkinama bent viena taisykl\u0117. 'Vienbals\u0117' rei\u0161kia, kad galutiniam teigiamam sprendimui visos taisykl\u0117s turi b\u016Bti teigiamos. 'Daugumos' rei\u0161kia, kad galutinis teigiamas sprendimas bus priimtas tuomet, kai teigiam\u0173 taisykli\u0173 bus daugiau nei neigiam\u0173. Jei teigiam\u0173 ir neigiam\u0173 taisykli\u0173 skai\u010Dius yra vienodas, tuomet galutinis rezultatas bus neigiamas. +authz-policy-decision-strategy-affirmative=Pozityvi +authz-policy-decision-strategy-unanimous=Vienbals\u0117 +authz-policy-decision-strategy-consensus=Daugumos +authz-select-a-policy=Parinkite taisykl\u0119 + +# Authz Role Policy Detail +authz-add-role-policy=Prid\u0117ti rol\u0117s taisykl\u0119 +authz-no-roles-assigned=N\u0117ra susiet\u0173 roli\u0173. +authz-policy-role-realm-roles.tooltip=Nurodo kurios *srities* rol\u0117(s) tenkina \u0161i\u0105 taisykl\u0119. +authz-policy-role-clients.tooltip=Parinkite klien\u0105 nor\u0117dami rodyti tik \u0161io kliento roles. +authz-policy-role-client-roles.tooltip=Nurodo *kliento* rol\u0117(\u012Fs) kurios tenkina \u0161i\u0105 taisykl\u0119. + +# Authz User Policy Detail +authz-add-user-policy=Prid\u0117ti naudotojo taisykl\u0119 +authz-no-users-assigned=N\u0117ra susiet\u0173 naudotoj\u0173. +authz-policy-user-users.tooltip=Nurodo kurie naudotojai tenkina \u0161i\u0105 taisykl\u0119. + +# Authz Time Policy Detail +authz-add-time-policy=Prid\u0117ti laiko taisykl\u0119 +authz-policy-time-not-before.tooltip=Nurodykite laik\u0105 iki kurio \u0161i taisykl\u0117 NETENKINAMA. Teigiamas rezultatas duodamas tik tuo atveju, kuomet dabartin\u0117 data ir laikas yra v\u0117lesn\u0117 arba lygi \u0161iai reik\u0161mei. +authz-policy-time-not-on-after=Ne v\u0117liau +authz-policy-time-not-on-after.tooltip=Nurodykite laik\u0105 po kurio \u0161i taisykl\u0117 NETENKINAMA. Teigiamas rezultatas duodamas tik tuo atveju, kuomet dabartin\u0117 data ir laikas yra ankstesni arba lygi \u0161iai reik\u0161mei. + +# Authz Drools Policy Detail +authz-add-drools-policy=Prid\u0117ti Drools taisykl\u0119 +authz-policy-drools-maven-artifact-resolve=I\u0161spr\u0119sti +authz-policy-drools-maven-artifact=Maven taisykl\u0117s artefaktas +authz-policy-drools-maven-artifact.tooltip=Nuoroda \u012F Maven GAV artifakt\u0105 kuriame apra\u0161ytos taisykl\u0117s. Kai tik nurodysite GAV, galite paspausti *I\u0161spr\u0119sti* tam kad \u012Fkelti *Modulis* ir *Sesija* laukus. +authz-policy-drools-module=Modulis +authz-policy-drools-module.tooltip=\u0160ioje taisykl\u0117je naudojamas modulis. Privalote nurodyti modul\u012F tam, kad gal\u0117tum\u0117te pasirinkti specifin\u0119 sesij\u0105 taisykli\u0173 \u012Fk\u0117limui. +authz-policy-drools-session=Sesija +authz-policy-drools-session.tooltip=\u0160ioje taisykl\u0117je naudojama sesija. Sesija teikia taisykles reikalingas \u0161ios taisykl\u0117s vykdymui. +authz-policy-drools-update-period=Atnaujinimo intervalas +authz-policy-drools-update-period.tooltip=Nurodykite laiko interval\u0105, kas kur\u012F turi b\u016Bti ie\u0161koma artefakto atnaujinim\u0173. + +# Authz JS Policy Detail +authz-add-js-policy=Prid\u0117ti JavaScript taisykl\u0119 +authz-policy-js-code=Programinis kodas +authz-policy-js-code.tooltip=JavaScript kodas kuriame apra\u0161ytos \u0161ios taisykl\u0117s s\u0105lygos. + + +# Authz Aggregated Policy Detail +authz-aggregated=Agreguota +authz-add-aggregated-policy=Prid\u0117ti agreguot\u0105 taisykl\u0119 + +# Authz Permission List +authz-no-permissions-available=N\u0117ra galim\u0173 leidim\u0173. + +# Authz Permission Detail +authz-permission-name.tooltip=\u0160io leidimo pavadinimas. +authz-permission-description.tooltip=\u0160io leidimo apra\u0161ymas. + +# Authz Resource Permission Detail +authz-add-resource-permission=Prid\u0117ti resurso leidim\u0105 +authz-permission-resource-apply-to-resource-type=Pritaikyti resurso tipui +authz-permission-resource-apply-to-resource-type.tooltip=Nurodykite ar \u0161is leidimas turi b\u016Bti pritaikomas visiems \u0161io tipo resursams. Jei \u012Fgalinta, tuomet leidimo tikrinimas bus atliekamas visiems nurodyto tipo resursams. +authz-permission-resource-resource.tooltip=Nurodykite, kad \u0161is leidimas turi b\u016Bti taikomas tik tam tikriems resursams. +authz-permission-resource-type.tooltip=Nurodykite, kad \u0161i taisykl\u0117 turi b\u016Bti taikoma visiems \u0161io tipo resursams. + +# Authz Scope Permission Detail +authz-add-scope-permission=Prid\u0117ti taikymo srities leidim\u0105 +authz-permission-scope-resource.tooltip=Pasirinkdami resur\u0105 apribosite taikymo sri\u010Di\u0173 s\u0105ra\u0161\u0105. Jei nepasirinkta, tuomet matysite visas galimas taikymo sritis. +authz-permission-scope-scope.tooltip=Nurodo, kad \u0161is leidimas turi b\u016Bti pritaikytas vienai ar daugiau taikymo sri\u010Di\u0173. + +# Authz Evaluation +authz-evaluation-identity-information=Tapatyb\u0117s informacija +authz-evaluation-identity-information.tooltip=Nurodykite tapatyb\u0117s informacij\u0105, kuri bus naudojama taisykli\u0173 vertinime. +authz-evaluation-client.tooltip=Nurodykite klient\u0105, kuris atlieka autorizacijos u\u017Eklausas. Nei nenurodyta, tuomet autorizacijos u\u017Eklausa bus vertinama naudojant dabartin\u012F klient\u0105. +authz-evaluation-user.tooltip=Nurodykite naudotoj\u0105, kurio vardu atliekamas teisi\u0173 serveryje filtravimas. +authz-evaluation-role.tooltip=Nurodykite pasirinkto naudotojo roles. +authz-evaluation-new=Papildyti u\u017Eklaus\u0105 +authz-evaluation-re-evaluate=Vertinti pakartotinai +authz-evaluation-previous=Prie\u0161 tai buv\u0119s bandymas + +authz-evaluation-contextual-info=Kontekstin\u0117 informacija +authz-evaluation-contextual-info.tooltip=Nurodykite kontekstin\u0119 informacij\u0105, kuri bus naudojama taisykli\u0173 vertinime. +authz-evaluation-contextual-attributes=Kontekstiniai atributai +authz-evaluation-contextual-attributes.tooltip=Galite pateikti vykdymo aplinkos arba vykdymo konteksto atributus. +authz-evaluation-permissions.tooltip=Nurodykite leidimus, kuriems bus taikomos taisykl\u0117s. +authz-evaluation-evaluate=Vertinti +authz-evaluation-any-resource-with-scopes=Bet kuris resursas su \u0161ia taikymo sritimi (sritimis) +authz-evaluation-no-result=Vertinant autorizacijos u\u017Eklaus\u0105 rezultat\u0173 nerasta. Patikrinkite ar egzistuoja resursai ar taikymo sritys susietos su taisykl\u0117mis. +authz-evaluation-no-policies-resource=\u0160iam resursui taisykl\u0117s nerastos. +authz-evaluation-result.tooltip=Leidim\u0173 u\u017Eklausos bendras rezultatas. +authz-evaluation-scopes.tooltip=Leid\u017Eiam\u0173 taikymo sri\u010Di\u0173 s\u0105ra\u0161as. +authz-evaluation-policies.tooltip=Informacija apie vertinime dalyvavusias taisykles ir sprendimus. +authz-evaluation-authorization-data=Atsakymas +authz-evaluation-authorization-data.tooltip=Autorizavimo u\u017Eklausos apdorojimo rezultatas su autorizacijos duomenimis. Rezultatas parodo k\u0105 Keycloak gr\u0105\u017Eina klientui pra\u0161an\u010Diam leidimo. Per\u017Ei\u016Br\u0117kite 'authorization' teigin\u012F su leidimais, kurie buvo suteikti \u0161iai autorizacijos u\u017Eklausai. +authz-show-authorization-data=Rodyti autorizacijos duomenis diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties b/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties new file mode 100644 index 0000000000..a44fad4e0e --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties @@ -0,0 +1,14 @@ +invalidPasswordMinLengthMessage=Per trumpas slapta\u00c5\u00beodis: ma\u00c5\u00beiausias ilgis {0}. +invalidPasswordMinLowerCaseCharsMessage=Neteisingas slapta\u00c5\u00beodis: privaloma \u00c4\u00c6vesti {0} ma\u00c5\u00be\u00c4\u2026j\u00c4\u2026 raid\u00c4\u2122. +invalidPasswordMinDigitsMessage=Neteisingas slapta\u00c5\u00beodis: privaloma \u00c4\u00c6vesti {0} skaitmen\u00c4\u00c6. +invalidPasswordMinUpperCaseCharsMessage=Neteisingas slapta\u00c5\u00beodis: privaloma \u00c4\u00c6vesti {0} did\u00c5\u00bei\u00c4\u2026j\u00c4\u2026 raid\u00c4\u2122. +invalidPasswordMinSpecialCharsMessage=Neteisingas slapta\u00c5\u00beodis: privaloma \u00c4\u00c6vesti {0} special\u00c5\u00b3 simbol\u00c4\u00c6. +invalidPasswordNotUsernameMessage=Neteisingas slapta\u00c5\u00beodis: slapta\u00c5\u00beodis negali sutapti su naudotojo vardu. +invalidPasswordRegexPatternMessage=Neteisingas slapta\u00c5\u00beodis: slapta\u00c5\u00beodis netenkina regex taisykl\u00c4\u2014s(i\u00c5\u00b3). +invalidPasswordHistoryMessage=Neteisingas slapta\u00c5\u00beodis: slapta\u00c5\u00beodis negali sutapti su prie\u00c5\ufffd tai buvusiais {0} slapta\u00c5\u00beod\u00c5\u00beiais. + +ldapErrorInvalidCustomFilter=Sukonfig\u016Bruotas LDAP filtras neprasideda "(" ir nesibaigia ")" simboliais. +ldapErrorMissingClientId=Privaloma nurodyti kliento ID kai srities roli\u0173 susiejimas n\u0117ra nenaudojamas. +ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Grupi\u0173 paveld\u0117jimo ir UID naryst\u0117s tipas kartu negali b\u016Bti naudojami. +ldapErrorCantWriteOnlyForReadOnlyLdap=Negalima nustatyti ra\u0161ymo r\u0117\u017Eimo kuomet LDAP teik\u0117jo r\u0117\u017Eimas ne WRITABLE +ldapErrorCantWriteOnlyAndReadOnly=Negalima nustatyti tik ra\u0161yti ir tik skaityti kartu diff --git a/themes/src/main/resources/theme/base/admin/theme.properties b/themes/src/main/resources/theme/base/admin/theme.properties index 67fb523f6f..20aa4121dc 100644 --- a/themes/src/main/resources/theme/base/admin/theme.properties +++ b/themes/src/main/resources/theme/base/admin/theme.properties @@ -1,2 +1,2 @@ import=common/keycloak -locales=ca,de,en,es,fr,it,pt-BR,ru \ No newline at end of file +locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/email/messages/messages_lt.properties b/themes/src/main/resources/theme/base/email/messages/messages_lt.properties new file mode 100644 index 0000000000..f0eb51c5b7 --- /dev/null +++ b/themes/src/main/resources/theme/base/email/messages/messages_lt.properties @@ -0,0 +1,24 @@ +emailVerificationSubject=El. pa\u0161to patvirtinimas +emailVerificationBody=Paskyra {2} sukurta naudojant \u0161\u012F el. pa\u0161to adres\u0105. Jei tau buvote J\u016Bs, tuomet paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105\n\n{0}\n\n\u0160i nuoroda galioja {1} min.\n\nJei paskyros nek\u016Br\u0117te, tuomet ignuoruokite \u0161\u012F lai\u0161k\u0105. +emailVerificationBodyHtml=

Paskyra {2} sukurta naudojant \u0161\u012F el. pa\u0161to adres\u0105. Jei tau buvote J\u016Bs, tuomet paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105

{0}

\u0160i nuoroda galioja {1} min.

nJei paskyros nek\u016Br\u0117te, tuomet ignuoruokite \u0161\u012F lai\u0161k\u0105.

+identityProviderLinkSubject=S\u0105saja {0} +identityProviderLinkBody=Ka\u017Eas pageidauja susieti J\u016Bs\u0173 "{1}" paskyr\u0105 su "{0}" {2} naudotojo paskyr\u0105. Jei tai buvote J\u016Bs, tuomet paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105 nor\u0117dami susieti paskyras\n\n{3}\n\n\u0160i nuoroda galioja {4} min.\n\nJei paskyr\u0173 susieti nenorite, tuomet ignoruokite \u0161\u012F lai\u0161k\u0105. Jei paskyras susiesite, tuomet prie {1} gal\u0117siste prisijungti per {0}. +identityProviderLinkBodyHtml=

\u017Eas pageidauja susieti J\u016Bs\u0173 {1} paskyr\u0105 su {0} {2} naudotojo paskyr\u0105. Jei tai buvote J\u016Bs, tuomet paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105 nor\u0117dami susieti paskyras

{3}

\u0160i nuoroda galioja {4} min.

Jei paskyr\u0173 susieti nenorite, tuomet ignoruokite \u0161\u012F lai\u0161k\u0105. Jei paskyras susiesite, tuomet prie {1} gal\u0117siste prisijungti per {0}.

+passwordResetSubject=Slapta\u017Eod\u017Eio atk\u016Brimas +passwordResetBody=Ka\u017Ekas pageidauja pakeisti J\u016Bs\u0173 paskyros {2} slapta\u017Eod\u012F. Jei tai buvote J\u016Bs, tuomet paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105 slapta\u017Eod\u017Eio pakeitimui.\n\n{0}\n\n\u0160i nuoroda ir kodas galioja {1} min.\n\nJei nepageidajate keisti slapta\u017Eod\u017Eio, tuomet ignoruokite \u0161\u012F lai\u0161k\u0105 ir niekas nebus pakeista. +passwordResetBodyHtml=

Ka\u017Ekas pageidauja pakeisti J\u016Bs\u0173 paskyros {2} slapta\u017Eod\u012F. Jei tai buvote J\u016Bs, tuomet paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105 slapta\u017Eod\u017Eio pakeitimui.

{0}

\u0160i nuoroda ir kodas galioja {1} min.

Jei nepageidajate keisti slapta\u017Eod\u017Eio, tuomet ignoruokite \u0161\u012F lai\u0161k\u0105 ir niekas nebus pakeista.

+executeActionsSubject=Atnaujinkite savo paskyr\u0105 +executeActionsBody=Sistemos administratorius pageidauja, kad J\u016Bs atnaujintum\u0117te savo {2} paskyr\u0105. Paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105 paskyros duomen\u0173 atnaujinimui.\n\n{0}\n\n\u0160i nuoroda galioja {1} min.\n\nJei J\u016Bs neasate tikri, kad tai administratoriaus pageidavimas, tuomet ignoruokite \u0161\u012F lai\u0161k\u0105 ir niekas nebus pakeista. +executeActionsBodyHtml=

Sistemos administratorius pageidauja, kad J\u016Bs atnaujintum\u0117te savo {2} paskyr\u0105. Paspauskite \u017Eemiau esan\u010Di\u0105 nuorod\u0105 paskyros duomen\u0173 atnaujinimui.

{0}

\u0160i nuoroda galioja {1} min.

Jei J\u016Bs neasate tikri, kad tai administratoriaus pageidavimas, tuomet ignoruokite \u0161\u012F lai\u0161k\u0105 ir niekas nebus pakeista.

+eventLoginErrorSubject=Bandymas prisijungti prie J\u016Bs\u0173 paskyros +eventLoginErrorBody=Bandymas prisijungti prie J\u016Bs\u0173 paskyros {0} i\u0161 {1} nes\u012Fkmingas. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi +eventLoginErrorBodyHtml=

Bandymas prisijungti prie J\u016Bs\u0173 paskyros {0} i\u0161 {1} nes\u012Fkmingas. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi

+eventRemoveTotpSubject=TOTP pa\u0161alinimas +eventRemoveTotpBody=Ka\u017Ekas pageidauja atsieti TOPT J\u016Bs\u0173 {1} paskyroje su {0}. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi +eventRemoveTotpBodyHtml=

Ka\u017Ekas pageidauja atsieti TOPT J\u016Bs\u0173 {1} paskyroje su {0}. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi

+eventUpdatePasswordSubject=Slapta\u017Eod\u017Eio atnaujinimas +eventUpdatePasswordBody=J\u016Bs\u0173 slapta\u017Eodis J\u016Bs\u0173 {1} paskyroje su {0} buvo pakeisas. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi +eventUpdatePasswordBodyHtml=

J\u016Bs\u0173 {1} paskyroje su {0. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi

+eventUpdateTotpSubject=TOTP atnaujinimas +eventUpdateTotpBody=TOTP J\u016Bs\u0173 {1} paskyroje su {0} buvo atnaujintas. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi +eventUpdateTotpBodyHtml=

TOTP J\u016Bs\u0173 {1} paskyroje su {0} buvo atnaujintas. Jei tai nebuvote J\u016Bs, tuomet susisiekite su administratoriumi

diff --git a/themes/src/main/resources/theme/base/email/theme.properties b/themes/src/main/resources/theme/base/email/theme.properties index 503eda7804..0bb29a975f 100644 --- a/themes/src/main/resources/theme/base/email/theme.properties +++ b/themes/src/main/resources/theme/base/email/theme.properties @@ -1 +1 @@ -locales=ca,de,en,es,fr,it,pt-BR,ru \ No newline at end of file +locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties index 725705ec99..45305c7177 100755 --- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties @@ -217,6 +217,7 @@ locale_it=Italian locale_pt_BR=Portugu\u00EAs (Brasil) locale_pt-BR=Portugu\u00EAs (Brasil) locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 +locale_lt=Lietuvi\u0173 backToApplication=« Back to Application missingParameterMessage=Missing parameters\: {0} diff --git a/themes/src/main/resources/theme/base/login/messages/messages_lt.properties b/themes/src/main/resources/theme/base/login/messages/messages_lt.properties new file mode 100644 index 0000000000..8d75629090 --- /dev/null +++ b/themes/src/main/resources/theme/base/login/messages/messages_lt.properties @@ -0,0 +1,218 @@ +doLogIn=Prisijungti +doRegister=Registruotis +doCancel=At\u0161aukti +doSubmit=Patvirtinti +doYes=Taip +doNo=Ne +doContinue=T\u0119sti +doAccept=Patvirtinti +doDecline=At\u0161aukti +doForgotPassword=Pamir\u0161ote slapta\u017Eod\u012F? +doClickHere=Spauskite \u010Dia +doImpersonate=Apsimesti kaip +kerberosNotConfigured=Kerberos nesukonfig\u016Bruotas +kerberosNotConfiguredTitle=Kerberos nesukonfig\u016Bruotas +bypassKerberosDetail=J\u016Bs neprisijung\u0119s per Kerberos arba J\u016Bs\u0173 nar\u0161ykl\u0117 nesukonfig\u016Bruota Kerberos prisijungimui. T\u0119skite ir pasirinkite kit\u0105 prisijungimo b\u016Bd\u0105 +kerberosNotSetUp=Kerberos nesukonfig\u016Bruotas. J\u016Bs negalite prisijungti. +registerWithTitle=Registruotis su {0} +registerWithTitleHtml={0} +loginTitle=Prisijungti su {0} +loginTitleHtml={0} +impersonateTitle=Apsimesti kaip naudotojas {0} +impersonateTitleHtml=Apsimesti kaip {0} +realmChoice=Sritis +unknownUser=Ne\u017Einomas naudotojas +loginTotpTitle=Mobilaus autentifikatoriaus nustatymas +loginProfileTitle=Atnaujinti paskyros informacij\u0105 +loginTimeout=U\u017Etrukote per ilgai. Prisijungimo procesas pradedamas i\u0161 naujo. +oauthGrantTitle=Suteitikti prieig\u0105 +oauthGrantTitleHtml={0} +errorTitle=Atsipra\u0161ome ... +errorTitleHtml=Atsipra\u0161ome ... +emailVerifyTitle=El. pa\u0161to adreso patvirtinimas +emailForgotTitle=Pamir\u0161ote slapta\u017Eod\u012F? +updatePasswordTitle=Atnaujinti slapta\u017Eod\u012F +codeSuccessTitle=S\u0117km\u0117 +codeErrorTitle=Klaidos kodas\: {0} + +termsTitle=Naudojimo s\u0105lygos +termsTitleHtml=Naudojimo s\u0105lygos +termsText=

Naudotjimo s\u0105lygos nenurodytos

+ +recaptchaFailed=Recaptcha neteisingas +recaptchaNotConfigured=Reikalingas Recaptcha nesukonfig\u016Bruotas +consentDenied=Prieiga draud\u017Eiama. + +noAccount=Dar neturite paskyros? +username=Naudotojo vardas +usernameOrEmail=Naudotojo vardas arba el. pa\u0161to adresas +firstName=Vardas +givenName=Vardas +fullName=Pavard\u0117 +lastName=Pavard\u0117 +familyName=Pavard\u0117 +email=El. pa\u0161tas +password=Slapta\u017Eodis +passwordConfirm=Pakartotas slapta\u017Eodis +passwordNew=Naujas slapta\u017Eodis +passwordNewConfirm=Pakartotas naujas slapta\u017Eodis +rememberMe=Prisiminti mane +authenticatorCode=Vienkartinis kodas +address=Adresas +street=Gatv\u0117 +locality=Miestas arba vietov\u0117 +region=Rajonas +postal_code=Pa\u0161to kodas +country=\u0160alis +emailVerified=El. pa\u0161to adresas patvirtintas +gssDelegationCredential=GSS prisijungimo duomen\u0173 delegavimas + +loginTotpStep1=\u012Ediekite FreeOTP arba Google Authenticator savo \u012Frenginyje. Program\u0117l\u0117s prieinamos Google Play ir Apple App Store. +loginTotpStep2=Atidarykite program\u0117l\u0119 ir nuskenuokite barkod\u0105 arba \u012Fveskite kod\u0105. +loginTotpStep3=\u012Eveskite program\u0117l\u0117je sugeneruot\u0105 vien\u0105 kart\u0105 galiojant\u012F kod\u0105 ir paspauskite Saugoti nor\u0117dami prisijungti. +loginTotpOneTime=Vienkartinis kodas + +oauthGrantRequest=Ar J\u016Bs suteikiate \u0161ias prieigos teises? +inResource=\u012F + +emailVerifyInstruction1=El. pa\u0161tas su instrukcijomis ir patvirtinimo nuoroda nusi\u0173sti \u012F J\u016Bs\u0173 el. pa\u0161t\u0105. +emailVerifyInstruction2=El. pa\u0161tu negavote patvirtinimo kodo? +emailVerifyInstruction3=pakartotoinai si\u0173sti el. lai\u0161k\u0105. + +emailLinkIdpTitle=Susieti {0} +emailLinkIdp1=El. pa\u0161to lai\u0161kas su instrukcijomis susieti {0} paskyr\u0105 {1} su {2} buvo nusi\u0173stas. +emailLinkIdp2=Negavote patvirtinimo kodo el. pa\u0161tu? +emailLinkIdp3=pakartotoinai si\u0173sti el. lai\u0161k\u0105. + +backToLogin=« Gr\u012F\u017Eti \u012F prisijungimo lang\u0105 + +emailInstruction=\u012Eveskite naudotojo vard\u0105 arba slapta\u017Eod\u012F ir slapta\u017Eod\u017Eio pakeitimo instrukcijos bus atsi\u0173stos Jums el. pa\u0161tu + +copyCodeInstruction=Nukopijuokite \u0161\u012F kod\u0105 \u012F J\u016Bs\u0173 program\u0105: + +personalInfo=Asmenin\u0117 informacija: +role_admin=Administratorius +role_realm-admin=Srities administravimas +role_create-realm=Kurti srit\u012F +role_create-client=Kurti program\u0105 +role_view-realm=Per\u017Ei\u016Br\u0117ti srit\u012F +role_view-users=Per\u017Ei\u016Br\u0117ti naudotojus +role_view-applications=Per\u017Ei\u016Br\u0117ti programas +role_view-clients=Per\u017Ei\u016Br\u0117ti klientines programas +role_view-events=Per\u017Ei\u016Br\u0117ti \u012Fvyki\u0173 \u017Eurnal\u0105 +role_view-identity-providers=Per\u017Ei\u016Br\u0117ti tapatyb\u0117s teik\u0117jus +role_manage-realm=Valdyti sritis +role_manage-users=Valdyti naudotojus +role_manage-applications=Valdyti programas +role_manage-identity-providers=Valdyti tapatyb\u0117s teik\u0117jus +role_manage-clients=Valdyti programas +role_manage-events=Valdyti \u012Fvykius +role_view-profile=Per\u017Ei\u016Br\u0117ti paskyr\u0105 +role_manage-account=Valdyti paskyr\u0105 +role_read-token=Skaityti prieigos rak\u0161\u0105 +role_offline-access=Darbas neprisijungus +client_account=Paskyra +client_security-admin-console=Saugumo administravimo konsol\u0117 +client_admin-cli=Administravimo CLI +client_realm-management=Srities valdymas +client_broker=Tarpininkas + +invalidUserMessage=Neteisingas naudotojo vardas arba slapta\u017Eodis. +invalidEmailMessage=Neteisingas el. pa\u0161to adresas. +accountDisabledMessage=Paskyros galiojimas sustabdytas, kreipkit\u0117s \u012F administratori\u0173. +accountTemporarilyDisabledMessage=Paskyros galiojimas laikinai sustabdytas. Kreipkit\u0117s \u012F administratori\u0173 arba pabandykite v\u0117liau. +expiredCodeMessage=Prisijungimo laikas baig\u0117si. Bandykite dar kart\u0105. + +missingFirstNameMessage=Pra\u0161ome \u012Fvesti vard\u0105. +missingLastNameMessage=Pra\u0161ome \u012Fvesti pavard\u0119. +missingEmailMessage=Pra\u0161ome \u012Fvesti el. pa\u0161to adres\u0105. +missingUsernameMessage=Pra\u0161ome \u012Fvesti naudotojo vard\u0105. +missingPasswordMessage=Pra\u0161ome \u012Fvesti slapta\u017Eod\u012F. +missingTotpMessage=Pra\u0161ome \u012Fvesti autentifikacijos kod\u0105. +notMatchPasswordMessage=Slapta\u017Eod\u017Eiai nesutampa. + +invalidPasswordExistingMessage=Neteisingas dabartinis slapta\u017Eodis. +invalidPasswordConfirmMessage=Pakartotas slapta\u017Eodis nesutampa. +invalidTotpMessage=Neteisingas autentifikacijos kodas. + +usernameExistsMessage=Toks naudotojas jau egzistuoja. +emailExistsMessage=El. pa\u0161to adresas jau egzistuoja. + +federatedIdentityExistsMessage=Naudotojas {0} {1} jau egzistuoja. Pra\u0161ome prsijungti prie naudotoj\u0173 valdymo posistem\u0117s paskyr\u0173 susiejimui. + +confirmLinkIdpTitle=Paskyra jau egzistuoja +federatedIdentityConfirmLinkMessage=Naudotojas {0} {1} jau egzistuoja. Ar t\u0119sti? +federatedIdentityConfirmReauthenticateMessage=Prisijunkite prie {0} nor\u0117dami susieti paskyr\u0105 su {1} +confirmLinkIdpReviewProfile=Per\u017Ei\u016Br\u0117ti naudotojo profilio informacij\u0105 +confirmLinkIdpContinue=Susieti su egzistuojan\u010Dia paskyra + +configureTotpMessage=Paskyros aktyvavimui Jums reikalingas Mobilus autentifikatorius. +updateProfileMessage=Paskyros aktyvavimui Jums reikia atnaujinti profilio informacij\u0105. +updatePasswordMessage=Paskyros aktyvavimui Jums reikia pakeisti slapta\u017Eod\u012F. +verifyEmailMessage=Paskyros aktyvavimui Jums reikia patvirtinti el. pa\u0161to adres\u0105. +linkIdpMessage=El. pa\u0161to adreso susiejimui su J\u016Bsu paskyra {0} reikalingas patvirtinimas. + +emailSentMessage=Netrukus tur\u0117tum\u0117te gauti el. pa\u0161to adres\u0105 su instrukcijomis. +emailSendErrorMessage=Klaida siun\u010Diant el. pa\u0161t\u0105, bandykite v\u0117liau. + +accountUpdatedMessage=J\u0173s\u0173 paskyros informacija atnaujinta. +accountPasswordUpdatedMessage=J\u016Bs\u0173 slapta\u017Eodis pakeistas. + +noAccessMessage=Prieiga negalima + +invalidPasswordMinLengthMessage=Neteisingas slapta\u017Eodis: privalomi bent {0} simboliai. +invalidPasswordMinDigitsMessage=Neteisingas slapta\u017Eodis: privalomi bent {0} skaitmenys. +invalidPasswordMinLowerCaseCharsMessage=Neteisingas slapta\u017Eodis: privalomos bent {0} ma\u017Eosios raid\u0117s. +invalidPasswordMinUpperCaseCharsMessage=Neteisingas slapta\u017Eodis: privalomos bent {0} did\u017Eiosios raid\u0117s. +invalidPasswordMinSpecialCharsMessage=Neteisingas slapta\u017Eodis: privalomi bent {0} special\u016Bs simboliai. +invalidPasswordNotUsernameMessage=Neteisingas slapta\u017Eodis: negali sutapti su naudotojo vardu. +invalidPasswordRegexPatternMessage=Neteisingas slapta\u017Eodis: neatitinka regexp taisykl\u0117s. +invalidPasswordHistoryMessage=Neteisingas slapta\u017Eodis: negali sutapti su prie\u0161 tai naudotais {0} slapta\u017Eod\u017Eiais. + +failedToProcessResponseMessage=Klaida apdorojant atsakym\u0105 +httpsRequiredMessage=Privalomas HTTPS +realmNotEnabledMessage=Srities galiojimas i\u0161jungtas +invalidRequestMessage=Neteisinga u\u017Eklausa +failedLogout=Nepavyko u\u017Ebaigti sesijos +unknownLoginRequesterMessage=Ne\u017Einomas prisijungimo pra\u0161ytojas +loginRequesterNotEnabledMessage=Prisijungimo pra\u0161ytojo galiojimas i\u0161jungtas +bearerOnlyMessage=Programos, sukonfig\u016Bruotos tik kaip perdav\u0117jai, negali inicijuoti prisijungim\u0105 per nar\u0161ykl\u0119. +standardFlowDisabledMessage=Su pateiktu atsakymo tipu prisijungimas per nar\u0161ykl\u0119 \u0161iam klientui negalimas. \u0160iam klientui ne\u012Fgalinta standartin\u0117 seka. +implicitFlowDisabledMessage=Su pateiktu atsakymo tipu prisijungimas per nar\u0161ykl\u0119 \u0161iam klientui negalimas. \u0160iam klientui ne\u012Fgalinta i\u0161reik\u0161tin\u0117 seka. +invalidRedirectUriMessage=Neteisinga nukreipimo nuoroda +unsupportedNameIdFormatMessage=Nepalaikomas NameIDFormat +invalidRequesterMessage=Neteisingas pra\u0161ytojas +registrationNotAllowedMessage=Registracija negalima +resetCredentialNotAllowedMessage=Prisijungimo duomen\u0173 atk\u016Brimas negalimas + +permissionNotApprovedMessage=Teis\u012F nepatvirtinta. +noRelayStateInResponseMessage=Tapatyb\u0117s teik\u0117jo atsakyme tr\u016Bksta perdavimo b\u016Bsenos. +insufficientPermissionMessage=Tr\u016Bksta teisi\u0173 tapatybi\u0173 susiejimui. +couldNotProceedWithAuthenticationRequestMessage=Nepavyksta prad\u0117ti tapatyb\u0117s teik\u0117jo autentifikacijos u\u017Eklausos. +couldNotObtainTokenMessage=Negaunamas prieigos raktas i\u0161 tapatyb\u0117s teik\u0117jo. +unexpectedErrorRetrievingTokenMessage=Prieigos rak\u0161o gavimo i\u0161 tapatyb\u0117s teik\u0117jo metu \u012Fvyko netik\u0117ta klaida. +unexpectedErrorHandlingResponseMessage=Tapatyb\u0117s teik\u0117jo atsakymo apdorojimo metu \u012Fvyko netik\u0117ta klaida. +identityProviderAuthenticationFailedMessage=Autentifikacijos klaida. Nepavyksta autentifikacija su tapatyb\u0117s teik\u0117ju. +identityProviderDifferentUserMessage=Autentifikuota kaip {0}, nors buvo tikimasi {1} +couldNotSendAuthenticationRequestMessage=Tapatyb\u0117s teik\u0117jui nepavyksta nusi\u0173sti autentifikacijos u\u017Eklausos. +unexpectedErrorHandlingRequestMessage=U\u017Eklausos tapatyb\u0117s teik\u0117jui formavimo metu \u012Fvyko netik\u0117ta klaida. +invalidAccessCodeMessage=Neteisingas prieigos kodas. +sessionNotActiveMessage=Sesija neaktyvi. +invalidCodeMessage=\u012Evyko klaida. Pra\u0161ome bandyti prisijungti dar kart\u0105. +identityProviderUnexpectedErrorMessage=Autentifikavimo su i\u0161oriniu tapatyb\u0117s teik\u0117ju metu \u012Fvyko netik\u0117ta klaida. +identityProviderNotFoundMessage=Su nurodytu identifikatoriumi nerastas tapatyb\u0117s teik\u0117jas. +identityProviderLinkSuccess=J\u016Bs\u0173 naudotojo paskyra buvo s\u0117kmingai susieta su {0} paskyra {1} . +staleCodeMessage=\u0160is puslapis nebegalioja. Pra\u0161ome gr\u012F\u017Eti \u012F program\u0105 ir bandyti prisijungti i\u0161 naujo. +realmSupportsNoCredentialsMessage=Sritis nepalaiko prisijungim\u0173 naudojant prisijungimo duomenis. +identityProviderNotUniqueMessage=Sritis palaiko daugiau nei vien\u0105 tapatyb\u0117s teik\u0117j\u0105. Negalima nustatyti kuris tapatyb\u0117s teik\u0117jas turi b\u016Bti naudojamas autentifikacijai. +emailVerifiedMessage=J\u016Bs\u0173 el. pa\u0161to adresas patvirtintas. +staleEmailVerificationLink=Nuoroda, kuri\u0105 paspaud\u0117te nebegalioja? Galb\u016Bt J\u016Bs jau patvirtinote el. pa\u0161to adres\u0105? + +backToApplication=« Gr\u012F\u017Eti \u012F program\u0105 +missingParameterMessage=Nenurodytas parametras\: {0} +clientNotFoundMessage=Nenurodytas klientas. +clientDisabledMessage=Kliento galiojimas i\u0161jungtas. +invalidParameterMessage=Neteisingas parametras\: {0} +alreadyLoggedIn=J\u016Bs jau esate prisijung\u0119. + +p3pPolicy=CP="Nurodyta reik\u0161m\u0117 n\u0117ra P3P taisykl\u0117!" \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/login/theme.properties b/themes/src/main/resources/theme/base/login/theme.properties index 503eda7804..0bb29a975f 100644 --- a/themes/src/main/resources/theme/base/login/theme.properties +++ b/themes/src/main/resources/theme/base/login/theme.properties @@ -1 +1 @@ -locales=ca,de,en,es,fr,it,pt-BR,ru \ No newline at end of file +locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file From c333d1eaa9b9279c1bbdb26eb1a8017929dd3d03 Mon Sep 17 00:00:00 2001 From: Ramunas Kraujutis Date: Mon, 22 Aug 2016 19:10:20 +0300 Subject: [PATCH 03/53] change lt locale position to be ordered alphabetically --- themes/src/main/resources/theme/base/account/theme.properties | 2 +- themes/src/main/resources/theme/base/admin/theme.properties | 2 +- themes/src/main/resources/theme/base/email/theme.properties | 2 +- themes/src/main/resources/theme/base/login/theme.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/themes/src/main/resources/theme/base/account/theme.properties b/themes/src/main/resources/theme/base/account/theme.properties index 0bb29a975f..db32af801b 100644 --- a/themes/src/main/resources/theme/base/account/theme.properties +++ b/themes/src/main/resources/theme/base/account/theme.properties @@ -1 +1 @@ -locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file +locales=ca,de,en,es,fr,it,lt,pt-BR,ru \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/theme.properties b/themes/src/main/resources/theme/base/admin/theme.properties index 20aa4121dc..65bc856908 100644 --- a/themes/src/main/resources/theme/base/admin/theme.properties +++ b/themes/src/main/resources/theme/base/admin/theme.properties @@ -1,2 +1,2 @@ import=common/keycloak -locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file +locales=ca,de,en,es,fr,it,lt,pt-BR,ru \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/email/theme.properties b/themes/src/main/resources/theme/base/email/theme.properties index 0bb29a975f..db32af801b 100644 --- a/themes/src/main/resources/theme/base/email/theme.properties +++ b/themes/src/main/resources/theme/base/email/theme.properties @@ -1 +1 @@ -locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file +locales=ca,de,en,es,fr,it,lt,pt-BR,ru \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/login/theme.properties b/themes/src/main/resources/theme/base/login/theme.properties index 0bb29a975f..db32af801b 100644 --- a/themes/src/main/resources/theme/base/login/theme.properties +++ b/themes/src/main/resources/theme/base/login/theme.properties @@ -1 +1 @@ -locales=ca,de,en,es,fr,it,pt-BR,ru,lt \ No newline at end of file +locales=ca,de,en,es,fr,it,lt,pt-BR,ru \ No newline at end of file From 352602da39bec0c52716725263c75b79d89d4b95 Mon Sep 17 00:00:00 2001 From: Ramunas Kraujutis Date: Mon, 22 Aug 2016 19:12:03 +0300 Subject: [PATCH 04/53] change lt locale position to be ordered alphabetically --- .../theme/base/account/messages/messages_en.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties index 5a5282d065..0bb466db63 100755 --- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties @@ -157,6 +157,6 @@ locale_en=English locale_es=Espa\u00F1ol locale_fr=Fran\u00e7ais locale_it=Italian +locale_lt=Lietuvi\u0173 locale_pt-BR=Portugu\u00EAs (Brasil) -locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 -locale_lt=Lietuvi\u0173 \ No newline at end of file +locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 \ No newline at end of file From 8f0afb255172cd87452799fa3a3927c8ce9b257d Mon Sep 17 00:00:00 2001 From: Mohit Suman Date: Thu, 1 Sep 2016 15:36:17 +0530 Subject: [PATCH 05/53] Add empty state for Identitiy Provider in admin console --- .../partials/realm-identity-provider.html | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html index 1b1ae9611a..af035f9789 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html @@ -1,7 +1,24 @@

{{:: 'identity-providers' | translate}}

- -
+
+
+ +
+

+ Identity Providers +

+

+ Through Identity Brokering it's easy to allow users to authenticate to Keycloak using external Identity Providers or Social Networks.
We have built-in support for OpenID Connect and SAML 2.0 as well as a number of social networks such as Google, GitHub, Facebook and Twitter. +

+ +
+
@@ -18,7 +35,7 @@ - + @@ -26,7 +43,7 @@ - + + + + + diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html index f39bc2c6cb..e2d5db2665 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html @@ -19,7 +19,7 @@ {{:: 'scope' | translate}}{{:: 'scope.tooltip' | translate}} -
  • {{:: 'authz-authorization' | translate}}
  • +
  • {{:: 'authz-authorization' | translate}}
  • {{:: 'revocation' | translate}}
  • From 06c48a2830be2823b5d8fa35f304c4a35725e4d6 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Wed, 21 Sep 2016 09:13:05 +0200 Subject: [PATCH 53/53] KEYCLOAK-3586 Token is not refreshed in updateToken --- .../oidc/js/src/main/resources/keycloak.js | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js index 6f56643f87..2d8f4218c8 100755 --- a/adapters/oidc/js/src/main/resources/keycloak.js +++ b/adapters/oidc/js/src/main/resources/keycloak.js @@ -155,7 +155,6 @@ } else if (initOptions) { if (initOptions.token || initOptions.refreshToken) { setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken); - kc.timeSkew = initOptions.timeSkew || 0; if (loginIframe.enable) { setupCheckLoginIframe().success(function() { @@ -363,11 +362,10 @@ throw 'Not authenticated'; } - var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew; + var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew; if (minValidity) { expiresIn -= minValidity; } - return expiresIn < 0; } @@ -382,7 +380,20 @@ minValidity = minValidity || 5; var exec = function() { - if (minValidity >= 0 && !kc.isTokenExpired(minValidity)) { + var refreshToken = false; + if (kc.timeSkew == -1) { + console.info('Skew ' + kc.timeSkew); + refreshToken = true; + console.info('[KEYCLOAK] Refreshing token: time skew not set'); + } else if (minValidity == -1) { + refreshToken = true; + console.info('[KEYCLOAK] Refreshing token: forced refresh'); + } else if (kc.isTokenExpired(minValidity)) { + refreshToken = true; + console.info('[KEYCLOAK] Refreshing token: token expired'); + } + + if (!refreshToken) { promise.setSuccess(false); } else { var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken; @@ -407,18 +418,21 @@ req.onreadystatechange = function () { if (req.readyState == 4) { if (req.status == 200) { + console.info('[KEYCLOAK] Token refreshed'); + timeLocal = (timeLocal + new Date().getTime()) / 2; var tokenResponse = JSON.parse(req.responseText); - kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat; - setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']); + setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal); kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess(); for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { p.setSuccess(true); } } else { + console.warn('[KEYCLOAK] Failed to refresh token'); + kc.onAuthRefreshError && kc.onAuthRefreshError(); for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { p.setError(true); @@ -529,18 +543,16 @@ function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) { timeLocal = (timeLocal + new Date().getTime()) / 2; - setToken(accessToken, refreshToken, idToken); + setToken(accessToken, refreshToken, idToken, timeLocal); if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) || (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) || (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) { - console.log('invalid nonce!'); + console.info('[KEYCLOAK] Invalid nonce, clearing token'); kc.clearToken(); promise && promise.setError(); } else { - kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat; - if (fulfillPromise) { kc.onAuthSuccess && kc.onAuthSuccess(); promise && promise.setSuccess(); @@ -613,7 +625,7 @@ return promise.promise; } - function setToken(token, refreshToken, idToken) { + function setToken(token, refreshToken, idToken, timeLocal) { if (kc.tokenTimeoutHandle) { clearTimeout(kc.tokenTimeoutHandle); kc.tokenTimeoutHandle = null; @@ -632,12 +644,23 @@ kc.realmAccess = kc.tokenParsed.realm_access; kc.resourceAccess = kc.tokenParsed.resource_access; + if (timeLocal) { + kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat; + console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds'); + } else { + kc.timeSkew = -1; + } + if (kc.onTokenExpired) { - var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000; - if (expiresIn <= 0) { + if (kc.timeSkew == -1) { kc.onTokenExpired(); } else { - kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn); + var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000; + if (expiresIn <= 0) { + kc.onTokenExpired(); + } else { + kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn); + } } }
  • {{:: 'name' | translate}} {{:: 'provider' | translate}} {{:: 'enabled' | translate}}{{:: 'actions' | translate}}
    {{identityProvider.alias}} From d11efa363c30118118165e9b9433351c37545207 Mon Sep 17 00:00:00 2001 From: Mohit Suman Date: Thu, 1 Sep 2016 16:02:34 +0530 Subject: [PATCH 06/53] Dropdown alignment with the empty state --- .../partials/realm-identity-provider.html | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html index af035f9789..46f86b9e37 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html @@ -10,13 +10,17 @@

    Through Identity Brokering it's easy to allow users to authenticate to Keycloak using external Identity Providers or Social Networks.
    We have built-in support for OpenID Connect and SAML 2.0 as well as a number of social networks such as Google, GitHub, Facebook and Twitter.

    - +
    +
    +
    + +
    +
    +
    From 05fe17be4ff7c02919277b7103a4b7ad2173a50c Mon Sep 17 00:00:00 2001 From: Ramunas Kraujutis Date: Sun, 4 Sep 2016 22:20:30 +0300 Subject: [PATCH 07/53] merge latest English messages --- .../messages/admin-messages_en.properties | 20 +++++++++++++++++-- .../admin/messages/messages_en.properties | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 02c7a7039f..356cbb2c5f 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -236,6 +236,12 @@ idp-sso-relay-state=IDP Initiated SSO Relay State idp-sso-relay-state.tooltip=Relay state you want to send with SAML request when you want to do IDP Initiated SSO. web-origins=Web Origins web-origins.tooltip=Allowed CORS origins. To permit all origins of Valid Redirect URIs add '+'. To permit all origins add '*'. +fine-oidc-endpoint-conf=Fine Grain OpenID Connect Configuration +fine-oidc-endpoint-conf.tooltip=Expand this section to configure advanced settings of this client related to OpenID Connect protocol +user-info-signed-response-alg=User Info Signed Response Algorithm +user-info-signed-response-alg.tooltip=JWA algorithm used for signed User Info Endpoint response. If set to 'unsigned', then User Info Response won't be signed and will be returned in application/json format. +request-object-signature-alg=Request Object Signature Algorithm +request-object-signature-alg.tooltip=JWA algorithm, which client needs to use when sending OIDC request object specified by 'request' or 'request_uri' parameters. If set to 'any', then Request object can be signed by any algorithm (including 'none' ). fine-saml-endpoint-conf=Fine Grain SAML Endpoint Configuration fine-saml-endpoint-conf.tooltip=Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service. assertion-consumer-post-binding-url=Assertion Consumer Service POST Binding URL @@ -684,7 +690,7 @@ ldap.custom-user-ldap-filter.tooltip=Additional LDAP Filter for filtering search search-scope=Search Scope ldap.search-scope.tooltip=For one level, we search for users just in DNs specified by User DNs. For subtree, we search in whole of their subtree. See LDAP documentation for more details use-truststore-spi=Use Truststore SPI -ldap.use-truststore-spi.tooltip=Specifies whether LDAP connection will use the truststore SPI with the truststore configured in keycloak-server.json. 'Always' means that it will always use it. 'Never' means that it won't use it. 'Only for ldaps' means that it will use if your connection URL use ldaps. Note even if keycloak-server.json is not configured, the default Java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used. +ldap.use-truststore-spi.tooltip=Specifies whether LDAP connection will use the truststore SPI with the truststore configured in standalone.xml/domain.xml. 'Always' means that it will always use it. 'Never' means that it won't use it. 'Only for ldaps' means that it will use if your connection URL use ldaps. Note even if standalone.xml/domain.xml is not configured, the default Java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used. connection-pooling=Connection Pooling ldap.connection-pooling.tooltip=Does Keycloak should use connection pooling for accessing LDAP server ldap.pagination.tooltip=Does the LDAP server support pagination. @@ -1028,7 +1034,7 @@ authz-policy-logic.tooltip=The logic dictates how the policy decision should be authz-policy-apply-policy=Apply Policy authz-policy-apply-policy.tooltip=Specifies all the policies that must be applied to the scopes defined by this policy or permission. authz-policy-decision-strategy=Decision Strategy -authz-policy-decision-strategy.tooltip=The decision strategy dictates how the policies associated with a given policy are evaluated and how a final decision is obtained. 'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive. 'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive. 'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative. +authz-policy-decision-strategy.tooltip=The decision strategy dictates how the policies associated with a given permission are evaluated and how a final decision is obtained. 'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive. 'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive. 'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative. authz-policy-decision-strategy-affirmative=Affirmative authz-policy-decision-strategy-unanimous=Unanimous authz-policy-decision-strategy-consensus=Consensus @@ -1051,6 +1057,16 @@ authz-add-time-policy=Add Time Policy authz-policy-time-not-before.tooltip=Defines the time before which the policy MUST NOT be granted. Only granted if current date/time is after or equal to this value. authz-policy-time-not-on-after=Not On or After authz-policy-time-not-on-after.tooltip=Defines the time after which the policy MUST NOT be granted. Only granted if current date/time is before or equal to this value. +authz-policy-time-day-month=Day of Month +authz-policy-time-day-month.tooltip=Defines the day of month before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the day of month before/equal which the policy MUST be granted. In this case, the policy would be granted if current day of month is between/equal the two values you provided. +authz-policy-time-month=Month +authz-policy-time-month.tooltip=Defines the month before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the month before/equal which the policy MUST be granted. In this case, the policy would be granted if current month is between/equal the two values you provided. +authz-policy-time-year=Year +authz-policy-time-year.tooltip=Defines the year before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the year before/equal which the policy MUST be granted. In this case, the policy would be granted if current year is between/equal the two values you provided. +authz-policy-time-hour=Hour +authz-policy-time-hour.tooltip=Defines the hour before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the hour before/equal which the policy MUST be granted. In this case, the policy would be granted if current hour is between/equal the two values you provided. +authz-policy-time-minute=Minute +authz-policy-time-minute.tooltip=Defines the minute before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the minute before/equal which the policy MUST be granted. In this case, the policy would be granted if current minute is between/equal the two values you provided. # Authz Drools Policy Detail authz-add-drools-policy=Add Drools Policy diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties index 95e16db33a..345cb2503c 100644 --- a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties @@ -12,3 +12,6 @@ ldapErrorMissingClientId=Client ID needs to be provided in config when Realm Rol ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Not possible to preserve group inheritance and use UID membership type together. ldapErrorCantWriteOnlyForReadOnlyLdap=Can't set write only when LDAP provider mode is not WRITABLE ldapErrorCantWriteOnlyAndReadOnly=Can't set write-only and read-only together + +clientRedirectURIsFragmentError=Redirect URIs must not contain an URI fragment +clientRootURLFragmentError=Root URL must not contain an URL fragment \ No newline at end of file From 05813832fc0a078533207466ab527662e2e4f60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C5=ABnas=20Kraujutis?= Date: Tue, 6 Sep 2016 10:53:23 +0300 Subject: [PATCH 08/53] add newly added translations to Lithuanian locale files --- .../messages/admin-messages_lt.properties | 30 ++++++++++++++----- .../admin/messages/messages_lt.properties | 3 ++ .../login/messages/messages_lt.properties | 2 +- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties index 66089e32a0..5153e53bab 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties @@ -31,7 +31,7 @@ editUsernameAllowed.tooltip=Jei \u012Fgalintas, tuomet naudotojas gali keisti sa resetPasswordAllowed=Slapta\u017Eod\u017Eio priminimas resetPasswordAllowed.tooltip=Prisijungimo lange rodoma nuoroda pamir\u0161to slapta\u017Eod\u017Eio atk\u016Brimui. rememberMe=Prisiminti mane -rememberMe.tooltip=Prisijungimo lange rodyti pasirinkim\u0105 leid\u017Eiant\u012F naudotojui likti prisijungus netgi tuomet kai nar\u0161ykl\u0117 yra i\u0161jungiama/\u012Fjungiama tol, kol nepasibaigia prisijungimo sesija. +rememberMe.tooltip=Prisijungimo lange rodyti pasirinkim\u0105 leid\u017Eiant\u012F naudotojui likti prisijungus netgi tuomet, kai nar\u0161ykl\u0117 yra i\u0161jungiama/\u012Fjungiama tol, kol nepasibaigia prisijungimo sesija. verifyEmail=El. pa\u0161to patvirtinimas verifyEmail.tooltip=Reikalauti naudotojo patvirtinti el. pa\u0161to adres\u0105 pirmojo prisijungimo metu. sslRequired=Reikalauti SSL @@ -69,7 +69,7 @@ supported-locales=Palaikomos kalbos supported-locales.placeholder=Pasirinkite arba \u012Fra\u0161ykite kalbos pavadinim\u0105 default-locale=Numatyta kalba realm-cache-clear=Srities pod\u0117lis -realm-cache-clear.tooltip=I\u0161 vis\u0173 sri\u010Di\u0173 pod\u0117li\u0173 pa\u0161alinama visa pod\u0117lyje (cache) esanti informacija +realm-cache-clear.tooltip=I\u0161 vis\u0173 sri\u010Di\u0173 pa\u0161alinama visa pod\u0117lyje (cache) esanti informacija user-cache-clear=Naudotoj\u0173 pod\u0117lis user-cache-clear.tooltip=I\u0161 vis\u0173 sri\u010Di\u0173 pa\u0161alinama visa naudotoj\u0173 pod\u0117lyje (cache) esanti informacija revoke-refresh-token=Prieigos rakt\u0105 naudoti tik kart\u0105 @@ -87,7 +87,7 @@ offline-session-idle.tooltip=Darbo neprisijungus sesijos neveikimo laikas, po ku access-token-lifespan=Prisijungimo rakto galiojimo laikas access-token-lifespan.tooltip=Laikas, po kurio prisijungimui naudojamas raktas (Access Token) nustoja galioti. Rekomenduojama, kad \u0161ios reik\u0161m\u0117s galiojimas b\u016Bt\u0173 reliatyviai trumpas palyginus su SSO galiojimo laiku. access-token-lifespan-for-implicit-flow=Prisijungimo rakto galiojimo laikas (Implicit Flow) -access-token-lifespan-for-implicit-flow.tooltip=Laikas, po kurio prisijungimui naudojamas OpenID Connect Implicit Flow raktas nustoja galioti. Rekomenduojama, kad \u0161ios reik\u0161m\u0117s galiojimas b\u016Bt\u0173 reliatyviai trumpas palyginus su SSO galiojimo laiku. \u0160is parametras skiriasi nuo 'Prisijungimo rakto galiojimo laikas' nes n\u0117ra galimyb\u0117s atnaujinti prieigos rakto naudojant OpenID Connect Implicit Flow. +access-token-lifespan-for-implicit-flow.tooltip=Laikas, po kurio prisijungimui naudojamas OpenID Connect Implicit Flow raktas nustoja galioti. Rekomenduojama, kad \u0161ios reik\u0161m\u0117s galiojimas b\u016Bt\u0173 reliatyviai trumpas palyginus su SSO galiojimo laiku. \u0160is parametras skiriasi nuo 'Prisijungimo rakto galiojimo laikas', nes n\u0117ra galimyb\u0117s atnaujinti prieigos rakto naudojant OpenID Connect Implicit Flow. client-login-timeout=Kliento prisijungimui skirtas laikas client-login-timeout.tooltip=Laikas, per kur\u012F klientas turi u\u017Ebaigti prisijungimo proces\u0105. Normaliu atveju reik\u0161m\u0117 tur\u0117t\u0173 b\u016Bti 1 minut\u0117. login-timeout=Naudotojo prisijungimui skirtas laikas @@ -192,7 +192,7 @@ client-protocol.tooltip='OpenID connect' leid\u017Eia klientams tikrinti galutin access-type=Prieigos tipas access-type.tooltip='Konfidencialus' klientai nor\u0117dami inicijuoti prisijungimo protokol\u0105 privalo perduoti slapt\u0105 kod\u0105. 'Vie\u0161as' klientai neprivalo perduoti slapto kodo. 'Tik ne\u0161\u0117jas' klientai - tai tinklin\u0117s paslaugos, kurios niekada neinicijuoja prisijungimo. standard-flow-enabled=\u012Egalinta standartin\u0117 seka -standard-flow-enabled.tooltip=\u012Egalina standartin\u0117 OpenID Connect nukreipim\u0105, kuomet autentifikacijos metu yra perduodamas autorizacijos kodas. OpenID Connect arba OAuth2 specifikacijos terminais tai rei\u0161kia 'Authorization Code Flow' \u012Fgalinim\u0105 \u0161iam klientui. +standard-flow-enabled.tooltip=\u012Egalina standartin\u012F OpenID Connect nukreipim\u0105, kuomet autentifikacijos metu yra perduodamas autorizacijos kodas. OpenID Connect arba OAuth2 specifikacijos terminais tai rei\u0161kia 'Authorization Code Flow' \u012Fgalinim\u0105 \u0161iam klientui. implicit-flow-enabled=\u012Egalinta i\u0161reik\u0161tin\u0117 seka implicit-flow-enabled.tooltip=\u012Egalina OpenID Connect nukreipim\u0105, kuomet autentifikacijos metu n\u0117ra perduodamas autorizacijos kodas. OpenID Connect arba OAuth2 specifikacijos terminais tai rei\u0161kia 'Implicit Flow' \u012Fgalinim\u0105 \u0161iam klientui. direct-access-grants-enabled=\u012Egalintas tiesiogin\u0117s prieigos suteikimas @@ -235,9 +235,15 @@ idp-sso-url-ref.tooltip=Pavadinimas, kuris IDP inicijuoto SSO prisijungimo metu, idp-sso-relay-state=IDP inicijuotos SSO b\u016Bsenos perdavimas idp-sso-relay-state.tooltip=SSO b\u016Bsenos parametro (RelayState) perdavimas kartu su IDP inicijuota SSO SAML u\u017Eklausa. web-origins=\u0160aknin\u0117s nuorodos -web-origins.tooltip=Leid\u017Eiamos CORS nuorodos. Nor\u0117dami leisti nukreipim\u0105 \u012F teisingas nuorodas naudokite '+'. Nor\u0117dami leisti visas nuorodas, naudokite '*'. +web-origins.tooltip=Leid\u017Eiamos CORS nuorodos. Nor\u0117dami leisti nukreipim\u0105 \u012F teisingas nuorodas, naudokite '+'. Nor\u0117dami leisti visas nuorodas, naudokite '*'. +fine-oidc-endpoint-conf=Detalioji OpenID prisijungimo konfig\u016Bracija +fine-oidc-endpoint-conf.tooltip=Nor\u0117dami konfig\u016Bruoti kliento s\u0105sajos su OpenID prisijungimo protokolu i\u0161pl\u0117stines nuostatas, i\u0161skleiskite \u0161\u012F skyri\u0173. +user-info-signed-response-alg=Naudotojo informacijos pasira\u0161yto atsako algoritmas +user-info-signed-response-alg.tooltip=JWA algoritmas naudojamas pasira\u0161yti naudotojo informacijos prieigos ta\u0161ko atsak\u0105. Jei nustatyta 'unsigned', tuomet naudotojo informacijos atsakas nebus pasira\u0161ytas ir bus gr\u0105\u017Eintas application/json formatu. +request-object-signature-alg=U\u017Eklausos objekto para\u0161o algoritmas +request-object-signature-alg.tooltip=JWA algoritmas, kur\u012F klientas naudoja siun\u010Diant OIDC u\u017Eklausos objekt\u0105, nusakyt\u0105 'request' arba 'request_uri' parameterais. Jei nustatyta 'any', tuomet u\u017Eklausos objektas gali b\u016Bti nepasira\u0161ytas arba pasira\u0161ytas bet kuriuo algoritmu. fine-saml-endpoint-conf=Detalioji SAML prieigos ta\u0161k\u0173 konfig\u016Bracija -fine-saml-endpoint-conf.tooltip=Nor\u0117dami konfig\u016Bruoti sprendini\u0173 pri\u0117mimo ir vieningo atsijungimo paslaugas i\u0161skleiskite \u0161\u012F skyri\u0173. +fine-saml-endpoint-conf.tooltip=Nor\u0117dami konfig\u016Bruoti sprendini\u0173 pri\u0117mimo ir vieningo atsijungimo paslaugas, i\u0161skleiskite \u0161\u012F skyri\u0173. assertion-consumer-post-binding-url=Sprendini\u0173 naudotojo paslaugos POST jungties URL assertion-consumer-post-binding-url.tooltip=Kliento sprendini\u0173 pri\u0117mimo paslaugos (prisijungimo rezultat\u0173) SAML POST jungties URL. Jei toki\u0173 jung\u010Di\u0173 neturite, tuomet palikite tu\u0161\u010Dias reik\u0161mes. assertion-consumer-redirect-binding-url=Sprendini\u0173 pri\u0117mimo paslaugos nukreipimo jungties URL @@ -681,7 +687,7 @@ ldap.custom-user-ldap-filter.tooltip=Papildomas LDAP filtras, kuris turi b\u016B search-scope=Paie\u0161kos apimtis ldap.search-scope.tooltip=Jei pasirinkta vieno lygio paie\u0161ka, tuomet naudotoj\u0173 ie\u0161koma vien tik nurodytame naudotoj\u0173 DN. Kai pasirinkta paie\u0161ka medyje, tuomet naudotoj\u0173 ie\u0161koma visose med\u017Eio \u0161akose. I\u0161samesn\u0117s informacijos ie\u0161kokite LDAP dokumentacij\u0105 use-truststore-spi=Nuadoti rakt\u0173 saugyklos SPI -ldap.use-truststore-spi.tooltip=Nurodykite, kuomet LDAP jungtis naudos keycloak-server.json sukonfig\u016Bruot\u0105 patikim\u0173 liudijim\u0173/rakt\u0173 saugyklos SPI. 'Visada' rei\u0161kia, kad bus naudojama visada. 'Niekada' rei\u0161kia, kad sukonfig\u016Bruota liudijim\u0173 saugykla nebus naudojama. 'Tik LDAP' rei\u0161kia, kad saugykla bus naudojama tik su LDAP jungtimis. Pastaba: jei keycloak-server.json nesukonfig\u016Bruotas, tuomet bus naudojama standartin\u0117 Java cacerts arba 'javax.net.ssl.trustStore' parametre nurodyta liudijim\u0173 saugykla. +ldap.use-truststore-spi.tooltip=Nurodykite, kuomet LDAP jungtis naudos standalone.xml/domain.xml sukonfig\u016Bruot\u0105 patikim\u0173 liudijim\u0173/rakt\u0173 saugyklos SPI. 'Visada' rei\u0161kia, kad bus naudojama visada. 'Niekada' rei\u0161kia, kad sukonfig\u016Bruota liudijim\u0173 saugykla nebus naudojama. 'Tik LDAP' rei\u0161kia, kad saugykla bus naudojama tik su LDAP jungtimis. Pastaba: jei standalone.xml/domain.xml nesukonfig\u016Bruotas, tuomet bus naudojama standartin\u0117 Java cacerts arba 'javax.net.ssl.trustStore' parametre nurodyta liudijim\u0173 saugykla. connection-pooling=Jung\u010Di\u0173 buferizavimas ldap.connection-pooling.tooltip=Ar Keycloak tur\u0117t\u0173 naudoti jung\u010Di\u0173 telkin\u012F jungiantis prie LDAP serverio? ldap.pagination.tooltip=Ar LDAP serveris palaiko puslapiavim\u0105? @@ -1045,6 +1051,16 @@ authz-add-time-policy=Prid\u0117ti laiko taisykl\u0119 authz-policy-time-not-before.tooltip=Nurodykite laik\u0105 iki kurio \u0161i taisykl\u0117 NETENKINAMA. Teigiamas rezultatas duodamas tik tuo atveju, kuomet dabartin\u0117 data ir laikas yra v\u0117lesn\u0117 arba lygi \u0161iai reik\u0161mei. authz-policy-time-not-on-after=Ne v\u0117liau authz-policy-time-not-on-after.tooltip=Nurodykite laik\u0105 po kurio \u0161i taisykl\u0117 NETENKINAMA. Teigiamas rezultatas duodamas tik tuo atveju, kuomet dabartin\u0117 data ir laikas yra ankstesni arba lygi \u0161iai reik\u0161mei. +authz-policy-time-day-month=M\u0117nesio diena +authz-policy-time-day-month.tooltip=Nurodykite m\u0117nesio dien\u0105 iki kurios \u0161i taisykl\u0117 TENKINAMA. U\u017Epild\u017Eius antr\u0105j\u012F laukel\u012F, taisykl\u0117 bus TENKINAMA jei diena patenka \u012F nurodyt\u0105 interval\u0105. Reik\u0161m\u0117s nurodomos imtinai. +authz-policy-time-month=M\u0117nesis +authz-policy-time-month.tooltip=Nurodykite m\u0117nes\u012F iki kurio \u0161i taisykl\u0117 TENKINAMA. U\u017Epild\u017Eius antr\u0105j\u012F laukel\u012F, taisykl\u0117 bus TENKINAMA jei m\u0117nesis patenka \u012F nurodyt\u0105 interval\u0105. Reik\u0161m\u0117s nurodomos imtinai. +authz-policy-time-year=Metai +authz-policy-time-year.tooltip=Nurodykite metus iki kuri\u0173 \u0161i taisykl\u0117 TENKINAMA. U\u017Epild\u017Eius antr\u0105j\u012F laukel\u012F, taisykl\u0117 bus TENKINAMA jei metai patenka \u012F nurodyt\u0105 interval\u0105. Reik\u0161m\u0117s nurodomos imtinai. +authz-policy-time-hour=Valanda +authz-policy-time-hour.tooltip=Nurodykite valand\u0105 iki kurios \u0161i taisykl\u0117 TENKINAMA. U\u017Epild\u017Eius antr\u0105j\u012F laukel\u012F, taisykl\u0117 bus TENKINAMA jei valanda patenka \u012F nurodyt\u0105 interval\u0105. Reik\u0161m\u0117s nurodomos imtinai. +authz-policy-time-minute=Minut\u0117 +authz-policy-time-minute.tooltip=Nurodykite minut\u0119 iki kurios \u0161i taisykl\u0117 TENKINAMA. U\u017Epild\u017Eius antr\u0105j\u012F laukel\u012F, taisykl\u0117 bus TENKINAMA jei minut\u0117 patenka \u012F nurodyt\u0105 interval\u0105. Reik\u0161m\u0117s nurodomos imtinai. # Authz Drools Policy Detail authz-add-drools-policy=Prid\u0117ti Drools taisykl\u0119 diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties b/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties index a44fad4e0e..49089257be 100644 --- a/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties +++ b/themes/src/main/resources/theme/base/admin/messages/messages_lt.properties @@ -12,3 +12,6 @@ ldapErrorMissingClientId=Privaloma nurodyti kliento ID kai srities roli\u0173 su ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Grupi\u0173 paveld\u0117jimo ir UID naryst\u0117s tipas kartu negali b\u016Bti naudojami. ldapErrorCantWriteOnlyForReadOnlyLdap=Negalima nustatyti ra\u0161ymo r\u0117\u017Eimo kuomet LDAP teik\u0117jo r\u0117\u017Eimas ne WRITABLE ldapErrorCantWriteOnlyAndReadOnly=Negalima nustatyti tik ra\u0161yti ir tik skaityti kartu + +clientRedirectURIsFragmentError=Nurodykite URI fragment\u0105, kurio negali b\u016Bti peradresuojamuose URI adresuose +clientRootURLFragmentError=Nurodykite URL fragment\u0105, kurio negali b\u016Bti \u0161akniniame URL adrese \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/login/messages/messages_lt.properties b/themes/src/main/resources/theme/base/login/messages/messages_lt.properties index 8d75629090..bffd8ed888 100644 --- a/themes/src/main/resources/theme/base/login/messages/messages_lt.properties +++ b/themes/src/main/resources/theme/base/login/messages/messages_lt.properties @@ -37,7 +37,7 @@ codeErrorTitle=Klaidos kodas\: {0} termsTitle=Naudojimo s\u0105lygos termsTitleHtml=Naudojimo s\u0105lygos -termsText=

    Naudotjimo s\u0105lygos nenurodytos

    +termsText=

    Naudojimo s\u0105lygos nenurodytos

    recaptchaFailed=Recaptcha neteisingas recaptchaNotConfigured=Reikalingas Recaptcha nesukonfig\u016Bruotas From ca6d1f3c48e3301c733cf6989f486ed32449e8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C5=ABnas=20Kraujutis?= Date: Tue, 6 Sep 2016 14:58:09 +0300 Subject: [PATCH 09/53] LT locale improvements --- .../messages/admin-messages_lt.properties | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties index 5153e53bab..b65bb7cc1b 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_lt.properties @@ -132,7 +132,7 @@ logout-all=Atjungti visus active-sessions=Aktyvios sesijos sessions=Sesijos not-before=Ne anks\u010Diau -not-before.tooltip=At\u0161aukti visus raktus i\u0161duotis prie\u0161 nurodyt\u0105 dat\u0105. +not-before.tooltip=At\u0161aukti visus raktus i\u0161duotus prie\u0161 nurodyt\u0105 dat\u0105. set-to-now=Parinkti dabartin\u0119 dat\u0105 push=Informuoti apie at\u0161aukim\u0105 push.tooltip=Visus klientus, kurie turi administravimo URL, informuoti apie nauj\u0105 rakt\u0173 at\u0161aukimo taisykl\u0119. @@ -153,7 +153,7 @@ tokenClaimName.tooltip=\u012E rakt\u0105 \u012Fterpiamas privalomas atributas. G jsonType.label=Privalomo atributo JSON tipas jsonType.tooltip=Naudojamas JSON lauko tipas, kuris turi b\u016Bti u\u017Epildomas rakto privalomoje JSON informacijoje. Galimi tipai: long, int, boolean ir String. includeInIdToken.label=Prid\u0117ti prie ID rakto -includeInIdToken.tooltip=Ar privaloma informacija turi b\u016Bti prid\u0117ta prie ID rakto? +includeInIdToken.tooltip=Ar privaloma informacija turi b\u016Bti pridedama prie ID rakto? includeInAccessToken.label=Prid\u0117ti prie prieigos rakto includeInAccessToken.tooltip=Ar privaloma informacija turi b\u016Bti pridedama prie prieigos rakto? includeInUserInfo.label=Prid\u0117ti prie naudotojo informacijos @@ -270,7 +270,7 @@ scope=Apimtis scope.tooltip=Apimties atitikmen\u0173 parinkimas leid\u017Eia apriboti, kurios naudotojo rol\u0117s kartu su raktu bus perduodamos klientui. sessions.tooltip=Per\u017Ei\u016Br\u0117ti \u0161io kliento aktyvias sesijas. Matysite \u0161iuo metu prisijungusius naudotojus bei j\u0173 prisijungimo laikus. offline-access=Darbas neprisijungus -offline-access.tooltip=Per\u017Ei\u016Br\u0117ti \u0161io kliento darbo neprisijungus r\u0117\u017Eimo aktyvias sesijas. Matysite naudotojus, kuriems yra i\u0161duoti darbo neprisijungus raktai bei j\u0173 i\u0161davimo laikus. Nor\u0117dami at\u0161aukti visus \u0161iam klientui i\u0161duotus raktus, eikite \u012F at\u0161aikim\u0173 kortel\u0119 ir pasirinkite 'Parinkti dabartin\u0119 dat\u0105' +offline-access.tooltip=Per\u017Ei\u016Br\u0117ti \u0161io kliento darbo neprisijungus r\u0117\u017Eimo aktyvias sesijas. Matysite naudotojus, kuriems yra i\u0161duoti darbo neprisijungus raktai bei j\u0173 i\u0161davimo laikus. Nor\u0117dami at\u0161aukti visus \u0161iam klientui i\u0161duotus raktus, eikite \u012F at\u0161aukim\u0173 kortel\u0119 ir pasirinkite 'Parinkti dabartin\u0119 dat\u0105' clustering=Klasteriai installation=Diegimas installation.tooltip=Klient\u0173 konfig\u016Bravimo pagalbin\u0117 priemon\u0117, padedanti sugeneruoti klient\u0173 adapteri\u0173 konfig\u016Bracijas, kurias galima atsisi\u0173sti, kopijuoti ar \u012Fkelti i\u0161 i\u0161karpin\u0117s @@ -367,7 +367,7 @@ client-revoke.push.tooltip=Kuomet nurodytas administravimo URL, taisykl\u0117 pe select-a-format=Formato parinkimas download=Atsisi\u0173sti offline-tokens=Darbo neprisijungus raktai -offline-tokens.tooltip=Rakt\u0173 skai\u010Dius kurie naudojami darbui neprisijungus +offline-tokens.tooltip=Rakt\u0173 skai\u010Dius, kurie naudojami darbui neprisijungus show-offline-tokens=Rodyti raktus show-offline-tokens.tooltip=D\u0117mesio, \u0161is veiksmas gali ilgai u\u017Etrukti priklausomai nuo aktyvi\u0173 darbo neprisijungus sesij\u0173 skai\u010Diaus. token-issued=Rakto i\u0161davimo laikas @@ -413,7 +413,7 @@ redirect-uri=Nukreipimo URI redirect-uri.tooltip=Tapatyb\u0117s teik\u0117jo konfig\u016Bravimo nuoroda. alias=Pseudonimas identity-provider.alias.tooltip=Pseudonimas, kuris vienareik\u0161mi\u0161kai identifikuoja tapatyb\u0117s teik\u0117j\u0105 ir yra naudojamas konstruojant nukreipimo nuorod\u0105. -identity-provider.enabled.tooltip=\u0116galinti \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. +identity-provider.enabled.tooltip=\u012Egalinti \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. authenticate-by-default=Autentifikuoti i\u0161 karto identity-provider.authenticate-by-default.tooltip=Jei \u012Fgalinta, tuomet bus bandoma autentifikuoti naudotoj\u0105 prie\u0161 parodant prisijungimo lang\u0105. store-tokens=Saugoti raktus @@ -428,12 +428,12 @@ update-profile-on-first-login.tooltip=Nurodykite s\u0105lygas, kuomet naudotojas trust-email=El. pa\u0161tas patikimas trust-email.tooltip=Jei \u012Fgalintas, tuomet \u0161io tapatyb\u0117s teik\u0117jo pateiktas el. pa\u0161to adresas laikomas patikimu ir, nepaisant bendr\u0173j\u0173 srities nustatym\u0173, n\u0117ra papildomai tikrinamas. gui-order.tooltip=Eili\u0161kum\u0105 GUI lange (pvz. Prisijungimo langas) nurodantis skai\u010Dius -first-broker-login-flow.tooltip=Autentifikacijos eigos pseodunimas, kuris bus su\u017Eadintas \u0161io tapatyb\u0117s teik\u0117jo naudotojui prisijungus pirm\u0105 kart\u0105. Terminas 'pirmas kartas' rei\u0161kia, kad Keycloak sistemoje nebuvo saugomas naudotojo profilis susietas su autentifikuotu \u0161io tapatyb\u0117s teik\u0117jo naudotoju. -post-broker-login-flow.tooltip=Autentifikacijos eigos pseodunimas, kuris bus su\u017Eadintas po kiekvieno prisijungimo naudojant \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. Naudingas tuomet, kai atlikti papildomus tikrinimus (pvz. OTP). Palikite tu\u0161\u010Di\u0105 reik\u0161m\u0119 jei nenorite su\u017Eadinti papildom\u0173 tikrinim\u0173 autentifikatoriumi jungiantis per \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. Tur\u0117kite omenyje, kad autentifikatoriaus realizacijos turi daryti prielaid\u0105, kad ClientSession naudotojas yra tapatyb\u0117s teik\u0117jo nustatytas. +first-broker-login-flow.tooltip=Autentifikacijos eigos pseudonimas, kuris bus su\u017Eadintas \u0161io tapatyb\u0117s teik\u0117jo naudotojui prisijungus pirm\u0105 kart\u0105. Terminas 'pirmas kartas' rei\u0161kia, kad Keycloak sistemoje nebuvo saugomas naudotojo profilis susietas su autentifikuotu \u0161io tapatyb\u0117s teik\u0117jo naudotoju. +post-broker-login-flow.tooltip=Autentifikacijos eigos pseudonimas, kuris bus su\u017Eadintas po kiekvieno prisijungimo naudojant \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. Naudingas tuomet, kai atlikti papildomus tikrinimus (pvz. OTP). Palikite tu\u0161\u010Di\u0105 reik\u0161m\u0119 jei nenorite su\u017Eadinti papildom\u0173 tikrinim\u0173 autentifikatoriumi jungiantis per \u0161\u012F tapatyb\u0117s teik\u0117j\u0105. Tur\u0117kite omenyje, kad autentifikatoriaus realizacijos turi daryti prielaid\u0105, kad ClientSession naudotojas yra tapatyb\u0117s teik\u0117jo nustatytas. openid-connect-config=OpenID prisijungimo konfig\u016Bracija openid-connect-config.tooltip=OIDC SP ir i\u0161orinio IDP konfig\u016Bracija. authorization-url=Autorizacijos URL -authorization-url.tooltip=Autorizacijos Url adresas. +authorization-url.tooltip=Autorizacijos URL adresas. token-url=Prieigos rakt\u0173 URL token-url.tooltip=Prieigos rakt\u0173 URL. logout-url=Atsijungimo URL @@ -662,7 +662,7 @@ ldap-attribute-name-for-uuid=LDAP atributo pavadinimas, kuriame saugomas UUID uuid-ldap-attribute.tooltip=LDAP atributas, kuris naudojamas kaip unikalus LDAP objekt\u0173 identifikatorius (UUID). Daugelis LDAP serveri\u0173 naudoja 'entryUUID', ta\u010Diau pasitaiko ir i\u0161im\u010Di\u0173. Pavyzd\u017Eiui ActiveDirectory naudojamas 'objectGUID' atributas. Jei j\u016Bs\u0173 LDAP serveris nepalaiko UUID atribut\u0173, tuomet galite naudoti bet kur\u012F kit\u0105 atribut\u0105 kuris u\u017Etikrina LDAP naudotoj\u0173 unikalum\u0105. Pavyzd\u017Eiui 'uid' arba 'entryDN'. user-object-classes=Naudotoj\u0173 objekt\u0173 klas\u0117s ldap-user-object-classes.placeholder=LDAP naudotoj\u0173 objekt\u0173 klas\u0117s (skiriamos kableliu) -ldap.user-object-classes.tooltip=LDAP atributo objectClass atskirtos kableliu reikm\u0117s skirtos LDAP naudotojo objektui. Pavyzd\u017Eiui: 'inetOrgPerson, organizationalPerson' . Naujai registruoti Keycloak naudotojai bus \u012Fra\u0161yti \u0161 LDAP su visomis nurodytomis objekt\u0173 klas\u0117mis. Egzistuojantys LDAP naudotoj\u0173 objektai randami tik tuomet, kai jie turi visas \u0161ias nurodytas objekto klases. +ldap.user-object-classes.tooltip=LDAP atributo objectClass atskirtos kableliu reik\u0161m\u0117s skirtos LDAP naudotojo objektui. Pavyzd\u017Eiui: 'inetOrgPerson, organizationalPerson' . Naujai registruoti Keycloak naudotojai bus \u012Fra\u0161yti \u0161 LDAP su visomis nurodytomis objekt\u0173 klas\u0117mis. Egzistuojantys LDAP naudotoj\u0173 objektai randami tik tuomet, kai jie turi visas \u0161ias nurodytas objekto klases. ldap-connection-url=LDAP jungties URL ldap-users-dn=LDAP naudotoj\u0173 DN @@ -686,7 +686,7 @@ custom-user-ldap-filter=Papildomas naudotoj\u0173 LDAP filtras ldap.custom-user-ldap-filter.tooltip=Papildomas LDAP filtras, kuris turi b\u016Bti naudojamas surast\u0173 naudotoj\u0173 nufiltravimui. Palikite tu\u0161\u010Di\u0105 lauk\u0105 jei papildomas filtravimas nereikalingas. \u012Esitikinkite, kad filtras prasideda '(' ir baigiasi ')' simboliais search-scope=Paie\u0161kos apimtis ldap.search-scope.tooltip=Jei pasirinkta vieno lygio paie\u0161ka, tuomet naudotoj\u0173 ie\u0161koma vien tik nurodytame naudotoj\u0173 DN. Kai pasirinkta paie\u0161ka medyje, tuomet naudotoj\u0173 ie\u0161koma visose med\u017Eio \u0161akose. I\u0161samesn\u0117s informacijos ie\u0161kokite LDAP dokumentacij\u0105 -use-truststore-spi=Nuadoti rakt\u0173 saugyklos SPI +use-truststore-spi=Naudoti rakt\u0173 saugyklos SPI ldap.use-truststore-spi.tooltip=Nurodykite, kuomet LDAP jungtis naudos standalone.xml/domain.xml sukonfig\u016Bruot\u0105 patikim\u0173 liudijim\u0173/rakt\u0173 saugyklos SPI. 'Visada' rei\u0161kia, kad bus naudojama visada. 'Niekada' rei\u0161kia, kad sukonfig\u016Bruota liudijim\u0173 saugykla nebus naudojama. 'Tik LDAP' rei\u0161kia, kad saugykla bus naudojama tik su LDAP jungtimis. Pastaba: jei standalone.xml/domain.xml nesukonfig\u016Bruotas, tuomet bus naudojama standartin\u0117 Java cacerts arba 'javax.net.ssl.trustStore' parametre nurodyta liudijim\u0173 saugykla. connection-pooling=Jung\u010Di\u0173 buferizavimas ldap.connection-pooling.tooltip=Ar Keycloak tur\u0117t\u0173 naudoti jung\u010Di\u0173 telkin\u012F jungiantis prie LDAP serverio? @@ -740,7 +740,7 @@ authenticator.alias.tooltip=Konfig\u016Bracijos pavadinimas otp-type=OTP tipas time-based=Paremtas laiku counter-based=Paremtas skaitliuku -otp-type.tooltip='totp' paremtas ribot\u0105 laik\u0105 galiojan\u010Diu vienkartiniu slapta\u017Eod\u017Eiu. 'hotp' ribot\u0105 kart\u0173 galiojan\u010Diu vienkartiniu slapta\u017Eod\u017Eiu. +otp-type.tooltip='totp' paremtas ribot\u0105 laik\u0105 galiojan\u010Diu vienkartiniu slapta\u017Eod\u017Eiu. 'hotp' - ribot\u0105 kart\u0173 galiojan\u010Diu vienkartiniu slapta\u017Eod\u017Eiu. otp-hash-algorithm=OTP mai\u0161os algoritmas otp-hash-algorithm.tooltip=Kuris mai\u0161os algoritmas turi b\u016Bti naudojamas OTP generavimui. number-of-digits=Skaitmen\u0173 skai\u010Dius @@ -761,6 +761,7 @@ login-events=Prisijungimo \u012Fvykiai filter=Filtruoti update=Atnaujinti reset=I\u0161valyti +resource-types=Resurso tipas operation-types=Veiksmas select-operations.placeholder=Pasirinkite veiksmus... resource-path=Resurso kelias @@ -798,7 +799,7 @@ flow.alias.tooltip=\u012Era\u0161ykite sekos rodom\u0105 pavadinim\u0105. top-level-flow-type=Auk\u0161\u010Diausio lygio sekos tipas flow.generic=generic flow.client=client -top-level-flow-type.tooltip=Kokios tipo \u0161i auk\u0161\u010Diausio lygio sritis? 'client' tipas naudojamas klient\u0173 (program\u0173) autentifikacijai. 'generic' naudojamas visais kitais atvejais. +top-level-flow-type.tooltip=Kokio tipo \u0161i auk\u0161\u010Diausio lygio sritis? 'client' tipas naudojamas klient\u0173 (program\u0173) autentifikacijai. 'generic' naudojamas visais kitais atvejais. create-execution-flow=Sukurti vykdymo sek\u0105 flow-type=Sekos tipas flow.form.type=form From bdbc4e1f6db3cd1a63ca9a4c9232f050b4d89033 Mon Sep 17 00:00:00 2001 From: Mohit Suman Date: Tue, 6 Sep 2016 19:34:07 +0530 Subject: [PATCH 10/53] add margin and update the description text --- .../partials/realm-identity-provider.html | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html index 46f86b9e37..2f5ed400b5 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html @@ -10,17 +10,21 @@

    Through Identity Brokering it's easy to allow users to authenticate to Keycloak using external Identity Providers or Social Networks.
    We have built-in support for OpenID Connect and SAML 2.0 as well as a number of social networks such as Google, GitHub, Facebook and Twitter.

    -
    -
    -
    - +

    To get started select a provider from the dropdown below:

    +
    +
    +
    +
    + +
    +
    From fd25dbcb5c93399556d1db55c9f0865a606f6b29 Mon Sep 17 00:00:00 2001 From: Marek Baluch Date: Tue, 6 Sep 2016 18:16:08 +0200 Subject: [PATCH 11/53] RHSSO-423 - Added default value for 'log-dir'. Set to ${project.build.dir}/surefire-reports --- .../util/junit/AggregateResultsReporter.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java b/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java index a0809c126c..2b1bf6ce04 100644 --- a/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java +++ b/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java @@ -86,13 +86,24 @@ public class AggregateResultsReporter extends RunListener { } private File createReportFile() throws Exception { - PropertiesConfiguration config = new PropertiesConfiguration(System.getProperty("testsuite.constants")); - config.setThrowExceptionOnMissing(true); + String logDirPath = null; - final File logDir = new File(config.getString("log-dir")); + try { + PropertiesConfiguration config = new PropertiesConfiguration(System.getProperty("testsuite.constants")); + config.setThrowExceptionOnMissing(true); + logDirPath = config.getString("log-dir"); + } catch (Exception e) { + logDirPath = System.getProperty("project.build.directory"); + if (logDirPath == null) { + throw new RuntimeException("Could not determine the path to the log directory."); + } + logDirPath += File.separator + "surefire-reports"; + } + + final File logDir = new File(logDirPath); logDir.mkdirs(); - final File reportFile = new File(logDir, "junit-report.xml").getAbsoluteFile(); + final File reportFile = new File(logDir, "junit-single-report.xml").getAbsoluteFile(); reportFile.createNewFile(); return reportFile; From 517413d38e04676037afe9620b8ba9649c3a398e Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Tue, 6 Sep 2016 17:32:37 -0300 Subject: [PATCH 12/53] [KEYCLOAK-3129] - Add authorization services endpoints to PermissionsTest --- .../client/resource/ResourcesResource.java | 12 ++ .../admin/PolicyEvaluationService.java | 6 +- .../authorization/admin/PolicyService.java | 2 +- .../admin/ResourceSetService.java | 2 +- .../authorization/admin/ScopeService.java | 5 + .../protection/resource/ResourceService.java | 2 +- .../testsuite/admin/PermissionsTest.java | 133 ++++++++++++++++++ 7 files changed, 158 insertions(+), 4 deletions(-) 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 1aaaa2352c..07438d07f4 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 @@ -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; @@ -42,6 +43,17 @@ public interface ResourcesResource { @Path("{id}") ResourceResource resource(@PathParam("id") String id); + @GET + @NoCache + @Produces(MediaType.APPLICATION_JSON) + List find(@QueryParam("name") String name, + @QueryParam("uri") String uri, + @QueryParam("owner") String owner, + @QueryParam("type") String type, + @QueryParam("scope") String scope, + @QueryParam("first") Integer firstResult, + @QueryParam("max") Integer maxResult); + @GET @NoCache @Produces(MediaType.APPLICATION_JSON) diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java index 4af34adf60..884f2e1abc 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java @@ -47,6 +47,7 @@ import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.representations.AccessToken; import org.keycloak.services.Urls; +import org.keycloak.services.resources.admin.RealmAuth; import javax.ws.rs.Consumes; import javax.ws.rs.POST; @@ -74,20 +75,23 @@ import static java.util.Arrays.asList; public class PolicyEvaluationService { private final AuthorizationProvider authorization; + private final RealmAuth auth; @Context private HttpRequest httpRequest; private final ResourceServer resourceServer; - PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization) { + PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { this.resourceServer = resourceServer; this.authorization = authorization; + this.auth = auth; } @POST @Consumes("application/json") @Produces("application/json") public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) { + this.auth.requireView(); KeycloakIdentity identity = createIdentity(evaluationRequest); EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity); authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(authorization, identity, asyncResponse)); 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 3b9c1942c5..b179378d70 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java @@ -273,7 +273,7 @@ public class PolicyService { @Path("evaluate") public PolicyEvaluationService getPolicyEvaluateResource() { this.auth.requireView(); - PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization); + PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); 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 d31146f7d4..64ef0619c1 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java @@ -177,7 +177,7 @@ public class ResourceSetService { @GET @NoCache @Produces("application/json") - public Response findAll(@QueryParam("name") String name, + public Response find(@QueryParam("name") String name, @QueryParam("uri") String uri, @QueryParam("owner") String owner, @QueryParam("type") String type, 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 44464b4fe9..f050f324f1 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java @@ -107,6 +107,11 @@ public class ScopeService { } Scope scope = storeFactory.getScopeStore().findById(id); + + if (scope == null) { + return Response.status(Status.NOT_FOUND).build(); + } + PolicyStore policyStore = storeFactory.getPolicyStore(); List policies = policyStore.findByScopeIds(Arrays.asList(scope.getId()), resourceServer.getId()); diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java index b02a935238..fdaa12f951 100644 --- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java +++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java @@ -109,7 +109,7 @@ public class ResourceService { } private Set findAll() { - Response response = this.resourceManager.findAll(null, null, null, null, null, -1, -1); + Response response = this.resourceManager.find(null, null, null, null, null, -1, -1); List resources = (List) response.getEntity(); return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet()); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java index 81de89463d..b6fca90ed3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java @@ -17,10 +17,12 @@ package org.keycloak.testsuite.admin; +import org.apache.bcel.generic.RETURN; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; import org.junit.Rule; import org.junit.Test; import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.models.AdminRoles; import org.keycloak.models.Constants; @@ -45,6 +47,10 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserFederationMapperRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.resources.admin.RealmAuth.Resource; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; @@ -68,6 +74,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; +import static org.keycloak.services.resources.admin.RealmAuth.Resource.AUTHORIZATION; +import static org.keycloak.services.resources.admin.RealmAuth.Resource.CLIENT; /** * @author Stian Thorgersen @@ -770,6 +778,123 @@ public class PermissionsTest extends AbstractKeycloakTest { }, Resource.CLIENT, true); } + @Test + public void clientAuthorization() { + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + realm.clients().create(ClientBuilder.create().clientId("foo-authz").build()); + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + foo.setServiceAccountsEnabled(true); + foo.setAuthorizationServicesEnabled(true); + realm.clients().get(foo.getId()).update(foo); + } + }, CLIENT, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + realm.clients().get(foo.getId()).authorization().getSettings(); + } + }, AUTHORIZATION, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + ResourceServerRepresentation settings = authorization.getSettings(); + authorization.update(settings); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.resources().resources(); + } + }, AUTHORIZATION, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.scopes().scopes(); + } + }, AUTHORIZATION, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.policies().policies(); + } + }, AUTHORIZATION, false); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + response.set(authorization.resources().create(new ResourceRepresentation("Test", Collections.emptySet()))); + } + }, AUTHORIZATION, true); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + response.set(authorization.scopes().create(new ScopeRepresentation("Test"))); + } + }, AUTHORIZATION, true); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + PolicyRepresentation representation = new PolicyRepresentation(); + representation.setName("Test PermissionsTest"); + representation.setType("js"); + HashMap config = new HashMap<>(); + config.put("code", ""); + representation.setConfig(config); + response.set(authorization.policies().create(representation)); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.resources().resource("nosuch").update(new ResourceRepresentation()); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.scopes().scope("nosuch").update(new ScopeRepresentation()); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.policies().policy("nosuch").update(new PolicyRepresentation()); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.resources().resource("nosuch").remove(); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.scopes().scope("nosuch").remove(); + } + }, AUTHORIZATION, true); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0); + AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization(); + authorization.policies().policy("nosuch").remove(); + } + }, AUTHORIZATION, true); + } + @Test public void roles() { invoke(new Invocation() { @@ -1543,6 +1668,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.VIEW_EVENTS; case IDENTITY_PROVIDER: return AdminRoles.VIEW_IDENTITY_PROVIDERS; + case AUTHORIZATION: + return AdminRoles.VIEW_AUTHORIZATION; default: throw new RuntimeException("Unexpected resouce"); } @@ -1560,6 +1687,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.MANAGE_EVENTS; case IDENTITY_PROVIDER: return AdminRoles.MANAGE_IDENTITY_PROVIDERS; + case AUTHORIZATION: + return AdminRoles.MANAGE_AUTHORIZATION; default: throw new RuntimeException("Unexpected resouce"); } @@ -1577,6 +1706,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.VIEW_IDENTITY_PROVIDERS; case IDENTITY_PROVIDER: return AdminRoles.VIEW_REALM; + case AUTHORIZATION: + return AdminRoles.VIEW_IDENTITY_PROVIDERS; default: throw new RuntimeException("Unexpected resouce"); } @@ -1594,6 +1725,8 @@ public class PermissionsTest extends AbstractKeycloakTest { return AdminRoles.MANAGE_IDENTITY_PROVIDERS; case IDENTITY_PROVIDER: return AdminRoles.MANAGE_REALM; + case AUTHORIZATION: + return AdminRoles.MANAGE_IDENTITY_PROVIDERS; default: throw new RuntimeException("Unexpected resouce"); } From c860d03a60222585601a73182d157ef98d3a5844 Mon Sep 17 00:00:00 2001 From: mhajas Date: Mon, 5 Sep 2016 10:44:01 +0200 Subject: [PATCH 13/53] Fix forbidden page on eap6 --- .../AbstractSAMLServletsAdapterTest.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java index 2973caa1dc..5bdd305b6a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java @@ -327,8 +327,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd salesPostPassiveServletPage.navigateTo(); if (forbiddenIfNotAuthenticated) { - waitUntilElement(By.xpath("//body")).text().not().contains("principal="); - assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT)); + assertOnForbiddenPage(); } else { waitUntilElement(By.xpath("//body")).text().contains("principal=null"); } @@ -407,9 +406,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd salesPostPassiveServletPage.navigateTo(); if (forbiddenIfNotAuthenticated) { - waitUntilElement(By.xpath("//body")).text().not().contains("principal="); - //Different 403 status page on EAP and Wildfly - assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT)); + assertOnForbiddenPage(); } else { waitUntilElement(By.xpath("//body")).text().contains("principal=null"); } @@ -422,9 +419,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd salesPostPassiveServletPage.navigateTo(); if (forbiddenIfNotAuthenticated) { - waitUntilElement(By.xpath("//body")).text().not().contains("principal="); - //Different 403 status page on EAP and Wildfly - assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT)); + assertOnForbiddenPage(); } else { waitUntilElement(By.xpath("//body")).text().contains("principal=null"); } @@ -724,4 +719,16 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd driver.navigate().to(employee2ServletPage.toString() + "/setCheckRoles?roles=" + roles); employee2ServletPage.logout(); } + + private void assertOnForbiddenPage() { + switch (System.getProperty("app.server")) { + case "eap6": + waitUntilElement(By.xpath("//body")).text().not().contains("principal="); + String source = driver.getPageSource(); + assertTrue(source.isEmpty() || source.contains("")); + break; + default: + waitUntilElement(By.xpath("//body")).text().contains(FORBIDDEN_TEXT); + } + } } From 7c292b1213ec8cb0c5d1f0ce0d6c881a70b7ab4c Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Wed, 7 Sep 2016 08:52:55 +0200 Subject: [PATCH 14/53] KEYCLOAK-3342 Add Identity Provider authenticator --- .../idm/IdentityProviderRepresentation.java | 5 + .../keycloak/migration/MigrationModel.java | 5 - .../migration/MigrationModelManager.java | 101 ++++++----------- .../org/keycloak/migration/ModelVersion.java | 15 +++ ...onTo1_2_0_CR1.java => MigrateTo1_2_0.java} | 9 +- .../migration/migrators/MigrateTo1_3_0.java | 6 +- .../migration/migrators/MigrateTo1_4_0.java | 6 +- .../migration/migrators/MigrateTo1_5_0.java | 6 +- .../migration/migrators/MigrateTo1_6_0.java | 6 +- .../migration/migrators/MigrateTo1_7_0.java | 6 +- .../migration/migrators/MigrateTo1_8_0.java | 6 +- .../migration/migrators/MigrateTo1_9_0.java | 6 +- .../migration/migrators/MigrateTo1_9_2.java | 6 +- .../migration/migrators/MigrateTo2_0_0.java | 6 +- .../migration/migrators/MigrateTo2_1_0.java | 6 +- .../migration/migrators/MigrateTo2_2_0.java | 59 ++++++++++ .../migration/migrators/Migration.java | 32 ++++++ .../models/IdentityProviderModel.java | 2 + .../utils/DefaultAuthenticationFlows.java | 40 +++++++ .../models/utils/RepresentationToModel.java | 13 +++ .../IdentityProviderAuthenticator.java | 103 ++++++++++++++++++ .../IdentityProviderAuthenticatorFactory.java | 102 +++++++++++++++++ .../protocol/AuthorizationEndpointBase.java | 21 ---- .../oidc/endpoints/AuthorizationEndpoint.java | 16 --- ...ycloak.authentication.AuthenticatorFactory | 3 +- .../AbstractAuthenticationTest.java | 2 +- .../admin/authentication/ExecutionTest.java | 4 +- .../admin/authentication/FlowTest.java | 2 +- .../authentication/InitialFlowsTest.java | 4 +- .../admin/authentication/ProvidersTest.java | 1 + .../base/src/test/resources/log4j.properties | 2 + .../broker/IdentityProviderHintTest.java | 18 ++- .../src/test/resources/log4j.properties | 5 +- .../realm-identity-provider-oidc.html | 7 -- .../realm-identity-provider-saml.html | 7 -- .../realm-identity-provider-social.html | 7 -- 36 files changed, 489 insertions(+), 156 deletions(-) rename server-spi/src/main/java/org/keycloak/migration/migrators/{MigrationTo1_2_0_CR1.java => MigrateTo1_2_0.java} (94%) create mode 100644 server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java create mode 100644 server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java create mode 100644 services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java create mode 100644 services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java index 524cbb39ce..2466f12e4f 100755 --- a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java @@ -123,10 +123,15 @@ public class IdentityProviderRepresentation { this.updateProfileFirstLoginMode = updateProfileFirstLoginMode; } + /** + * @deprecated Replaced by configuration option in identity provider authenticator + */ + @Deprecated public boolean isAuthenticateByDefault() { return authenticateByDefault; } + @Deprecated public void setAuthenticateByDefault(boolean authenticateByDefault) { this.authenticateByDefault = authenticateByDefault; } diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java index c2e6dab0ec..a59508b4f4 100755 --- a/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java +++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java @@ -23,11 +23,6 @@ package org.keycloak.migration; * @version $Revision: 1 $ */ public interface MigrationModel { - /** - * Must have the form of major.minor.micro as the version is parsed and numbers are compared - */ - String LATEST_VERSION = "2.1.0"; - String getStoredVersion(); void setStoredVersion(String version); } diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java index 6a2f448b58..e2b55e139e 100755 --- a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -18,6 +18,7 @@ package org.keycloak.migration; import org.jboss.logging.Logger; +import org.keycloak.migration.migrators.MigrateTo1_2_0; import org.keycloak.migration.migrators.MigrateTo1_3_0; import org.keycloak.migration.migrators.MigrateTo1_4_0; import org.keycloak.migration.migrators.MigrateTo1_5_0; @@ -28,7 +29,8 @@ import org.keycloak.migration.migrators.MigrateTo1_9_0; import org.keycloak.migration.migrators.MigrateTo1_9_2; import org.keycloak.migration.migrators.MigrateTo2_0_0; import org.keycloak.migration.migrators.MigrateTo2_1_0; -import org.keycloak.migration.migrators.MigrationTo1_2_0_CR1; +import org.keycloak.migration.migrators.MigrateTo2_2_0; +import org.keycloak.migration.migrators.Migration; import org.keycloak.models.KeycloakSession; /** @@ -38,82 +40,41 @@ import org.keycloak.models.KeycloakSession; public class MigrationModelManager { private static Logger logger = Logger.getLogger(MigrationModelManager.class); + private static final Migration[] migrations = { + new MigrateTo1_2_0(), + new MigrateTo1_3_0(), + new MigrateTo1_4_0(), + new MigrateTo1_5_0(), + new MigrateTo1_6_0(), + new MigrateTo1_7_0(), + new MigrateTo1_8_0(), + new MigrateTo1_9_0(), + new MigrateTo1_9_2(), + new MigrateTo2_0_0(), + new MigrateTo2_1_0(), + new MigrateTo2_2_0(), + }; + public static void migrate(KeycloakSession session) { + ModelVersion latest = migrations[migrations.length-1].getVersion(); MigrationModel model = session.realms().getMigrationModel(); - String storedVersion = model.getStoredVersion(); - if (MigrationModel.LATEST_VERSION.equals(storedVersion)) return; ModelVersion stored = null; - if (storedVersion != null) { - stored = new ModelVersion(storedVersion); + if (model.getStoredVersion() != null) { + stored = new ModelVersion(model.getStoredVersion()); + if (latest.equals(stored)) { + return; + } } - if (stored == null || stored.lessThan(MigrationTo1_2_0_CR1.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.2.0.CR1 updates"); + for (Migration m : migrations) { + if (stored == null || stored.lessThan(m.getVersion())) { + if (stored != null) { + logger.debugf("Migrating older model to %s", m.getVersion()); + } + m.migrate(session); } - new MigrationTo1_2_0_CR1().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_3_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.3.0 updates"); - } - new MigrateTo1_3_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_4_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.4.0 updates"); - } - new MigrateTo1_4_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_5_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.5.0 updates"); - } - new MigrateTo1_5_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_6_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.6.0 updates"); - } - new MigrateTo1_6_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_7_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.7.0 updates"); - } - new MigrateTo1_7_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_8_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.8.0 updates"); - } - new MigrateTo1_8_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_9_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.9.0 updates"); - } - new MigrateTo1_9_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_9_2.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.9.2 updates"); - } - new MigrateTo1_9_2().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo2_0_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 2.0.0 updates"); - } - new MigrateTo2_0_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo2_1_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 2.1.0 updates"); - } - new MigrateTo2_1_0().migrate(session); } - model.setStoredVersion(MigrationModel.LATEST_VERSION); + model.setStoredVersion(latest.toString()); } } diff --git a/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java b/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java index 383edd5e80..884587917e 100755 --- a/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java +++ b/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java @@ -98,4 +98,19 @@ public class ModelVersion { if (comp < 0) return true; return false; } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ModelVersion)) { + return false; + } + + ModelVersion v = (ModelVersion) obj; + return v.getMajor() == major && v.getMinor() == minor && v.getMicro() != micro; + } + + @Override + public String toString() { + return major + "." + minor + "." + micro; + } } diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java similarity index 94% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java rename to server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java index f2a86ce466..d363a10e53 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java @@ -32,8 +32,13 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrationTo1_2_0_CR1 { - public static final ModelVersion VERSION = new ModelVersion("1.2.0.CR1"); +public class MigrateTo1_2_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("1.2.0"); + + @Override + public ModelVersion getVersion() { + return VERSION; + } public void setupBrokerService(RealmModel realm) { ClientModel client = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java index 8d573c7afa..ee337147aa 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java @@ -37,9 +37,13 @@ import javax.naming.directory.SearchControls; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrateTo1_3_0 { +public class MigrateTo1_3_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("1.3.0"); + public ModelVersion getVersion() { + return VERSION; + } public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java index 5fbab26194..9f28f91918 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java @@ -34,9 +34,13 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrateTo1_4_0 { +public class MigrateTo1_4_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.4.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java index 1a7f83e670..6969d5c30c 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java @@ -32,9 +32,13 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrateTo1_5_0 { +public class MigrateTo1_5_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.5.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java index 17ddf1b37f..c3d922258d 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java @@ -27,10 +27,14 @@ import org.keycloak.models.utils.KeycloakModelUtils; /** * @author Marek Posolda */ -public class MigrateTo1_6_0 { +public class MigrateTo1_6_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.6.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { MigrationProvider provider = session.getProvider(MigrationProvider.class); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java index 47abe62a37..3d4a5d5c9f 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java @@ -31,10 +31,14 @@ import org.keycloak.models.utils.DefaultAuthenticationFlows; /** * @author Marek Posolda */ -public class MigrateTo1_7_0 { +public class MigrateTo1_7_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.7.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java index 94e0243c12..549cde9a7c 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java @@ -30,10 +30,14 @@ import org.keycloak.models.utils.KeycloakModelUtils; /** * @author Marek Posolda */ -public class MigrateTo1_8_0 { +public class MigrateTo1_8_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.8.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java index fb24598afa..e91f1265b1 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java @@ -33,10 +33,14 @@ import java.util.Map; /** * @author Marek Posolda */ -public class MigrateTo1_9_0 { +public class MigrateTo1_9_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.9.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { RealmModel realm = session.realms().getRealm(Config.getAdminRealm()); if (realm != null && realm.getDisplayNameHtml() != null && realm.getDisplayNameHtml().equals("Keycloak")) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java index 2473937382..3a41058c81 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java @@ -25,10 +25,14 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -public class MigrateTo1_9_2 { +public class MigrateTo1_9_2 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.9.2"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { for (RealmModel realm : session.realms().getRealms()) { if (realm.getBrowserSecurityHeaders() != null) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java index 23368f3130..d36a8583c7 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java @@ -23,10 +23,14 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; -public class MigrateTo2_0_0 { +public class MigrateTo2_0_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("2.0.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { for (RealmModel realm : session.realms().getRealms()) { migrateAuthorizationServices(realm); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java index 9e7b93130d..995dafb3fb 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java @@ -38,9 +38,13 @@ import java.util.stream.Collectors; * * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. */ -public class MigrateTo2_1_0 { +public class MigrateTo2_1_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("2.1.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { for (RealmModel realm : session.realms().getRealms()) { migrateDefaultRequiredAction(realm); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java new file mode 100644 index 0000000000..1dbcba4200 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java @@ -0,0 +1,59 @@ +/* + * 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.migration.migrators; + +import org.jboss.logging.Logger; +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticatorConfigModel; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.DefaultAuthenticationFlows; + +import java.util.HashMap; +import java.util.Map; + +public class MigrateTo2_2_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("2.2.0"); + + private static final Logger LOG = Logger.getLogger(MigrateTo2_2_0.class); + + public ModelVersion getVersion() { + return VERSION; + } + + public void migrate(KeycloakSession session) { + for (RealmModel realm : session.realms().getRealms()) { + addIdentityProviderAuthenticator(realm); + } + } + + private void addIdentityProviderAuthenticator(RealmModel realm) { + String defaultProvider = null; + for (IdentityProviderModel provider : realm.getIdentityProviders()) { + if (provider.isEnabled() && provider.isAuthenticateByDefault()) { + defaultProvider = provider.getAlias(); + break; + } + } + + DefaultAuthenticationFlows.addIdentityProviderAuthenticator(realm, defaultProvider); + } + +} diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java b/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java new file mode 100644 index 0000000000..e37dad29f9 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java @@ -0,0 +1,32 @@ +/* + * 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.migration.migrators; + +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.KeycloakSession; + +/** + * @author Stian Thorgersen + */ +public interface Migration { + + void migrate(KeycloakSession session); + + ModelVersion getVersion(); + +} diff --git a/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java b/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java index 82e373f77f..2425c7d877 100755 --- a/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java +++ b/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java @@ -120,10 +120,12 @@ public class IdentityProviderModel implements Serializable { this.storeToken = storeToken; } + @Deprecated public boolean isAuthenticateByDefault() { return authenticateByDefault; } + @Deprecated public void setAuthenticateByDefault(boolean authenticateByDefault) { this.authenticateByDefault = authenticateByDefault; } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java index 7b2c61ba93..a738d05af7 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java @@ -276,6 +276,7 @@ public class DefaultAuthenticationFlows { execution.setAuthenticatorFlow(false); realm.addAuthenticatorExecution(execution); + addIdentityProviderAuthenticator(realm, null); AuthenticationFlowModel forms = new AuthenticationFlowModel(); forms.setTopLevel(false); @@ -317,6 +318,45 @@ public class DefaultAuthenticationFlows { realm.addAuthenticatorExecution(execution); } + public static void addIdentityProviderAuthenticator(RealmModel realm, String defaultProvider) { + String browserFlowId = null; + for (AuthenticationFlowModel f : realm.getAuthenticationFlows()) { + if (f.getAlias().equals(DefaultAuthenticationFlows.BROWSER_FLOW)) { + browserFlowId = f.getId(); + break; + } + } + + if (browserFlowId != null) { + for (AuthenticationExecutionModel e : realm.getAuthenticationExecutions(browserFlowId)) { + if ("identity-provider-redirector".equals(e.getAuthenticator())) { + return; + } + } + + AuthenticationExecutionModel execution; + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(browserFlowId); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setAuthenticator("identity-provider-redirector"); + execution.setPriority(25); + execution.setAuthenticatorFlow(false); + + if (defaultProvider != null) { + AuthenticatorConfigModel configModel = new AuthenticatorConfigModel(); + + Map config = new HashMap<>(); + config.put("defaultProvider", defaultProvider); + configModel.setConfig(config); + configModel = realm.addAuthenticatorConfig(configModel); + + execution.setAuthenticatorConfig(configModel.getId()); + } + + realm.addAuthenticatorExecution(execution); + } + } + public static void clientAuthFlow(RealmModel realm) { AuthenticationFlowModel clients = new AuthenticationFlowModel(); clients.setAlias(CLIENT_AUTHENTICATION_FLOW); diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index d1d5b059d7..04e856ba2c 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -556,6 +556,19 @@ public class RepresentationToModel { if (newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW) == null) { DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true); } + + // Added in 2.2 + String defaultProvider = null; + if (rep.getIdentityProviders() != null) { + for (IdentityProviderRepresentation i : rep.getIdentityProviders()) { + if (i.isEnabled() && i.isAuthenticateByDefault()) { + defaultProvider = i.getProviderId(); + break; + } + } + } + + DefaultAuthenticationFlows.addIdentityProviderAuthenticator(newRealm, defaultProvider); } private static void convertDeprecatedSocialProviders(RealmRepresentation rep) { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java new file mode 100644 index 0000000000..795d0c2d6e --- /dev/null +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java @@ -0,0 +1,103 @@ +/* + * 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.authentication.authenticators.browser; + +import org.jboss.logging.Logger; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.Authenticator; +import org.keycloak.constants.AdapterConstants; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.Urls; +import org.keycloak.services.managers.ClientSessionCode; + +import javax.ws.rs.core.Response; +import java.util.List; + +/** + * @author Stian Thorgersen + */ +public class IdentityProviderAuthenticator implements Authenticator { + + private static final Logger LOG = Logger.getLogger(IdentityProviderAuthenticator.class); + + @Override + public void authenticate(AuthenticationFlowContext context) { + if (context.getUriInfo().getQueryParameters().containsKey(AdapterConstants.KC_IDP_HINT)) { + String providerId = context.getUriInfo().getQueryParameters().getFirst(AdapterConstants.KC_IDP_HINT); + if (providerId == null || providerId.equals("")) { + LOG.tracef("Skipping: kc_idp_hint query parameter is empty"); + context.attempted(); + } else { + LOG.tracef("Redirecting: %s set to %s", AdapterConstants.KC_IDP_HINT, providerId); + redirect(context, providerId); + } + } else if (context.getAuthenticatorConfig() != null && context.getAuthenticatorConfig().getConfig().containsKey(IdentityProviderAuthenticatorFactory.DEFAULT_PROVIDER)) { + String defaultProvider = context.getAuthenticatorConfig().getConfig().get(IdentityProviderAuthenticatorFactory.DEFAULT_PROVIDER); + LOG.tracef("Redirecting: default provider set to %s", defaultProvider); + redirect(context, defaultProvider); + } else { + LOG.tracef("No default provider set or %s query parameter provided", AdapterConstants.KC_IDP_HINT); + context.attempted(); + } + } + + private void redirect(AuthenticationFlowContext context, String providerId) { + List identityProviders = context.getRealm().getIdentityProviders(); + for (IdentityProviderModel identityProvider : identityProviders) { + if (identityProvider.isEnabled() && providerId.equals(identityProvider.getAlias())) { + String accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode(); + Response response = Response.temporaryRedirect( + Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode)) + .build(); + + LOG.debugf("Redirecting to %s", providerId); + context.forceChallenge(response); + return; + } + } + + LOG.warnf("Provider not found or not enabled for realm %s", providerId); + context.attempted(); + } + + @Override + public void action(AuthenticationFlowContext context) { + } + + @Override + public boolean requiresUser() { + return false; + } + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + return true; + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + } + + @Override + public void close() { + } + +} diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java new file mode 100644 index 0000000000..635c95e3d7 --- /dev/null +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java @@ -0,0 +1,102 @@ +/* + * 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.authentication.authenticators.browser; + +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.Collections; +import java.util.List; + +import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE; + +/** + * @author Stian Thorgersen + */ +public class IdentityProviderAuthenticatorFactory implements AuthenticatorFactory { + + protected static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { + AuthenticationExecutionModel.Requirement.ALTERNATIVE, AuthenticationExecutionModel.Requirement.DISABLED + }; + + protected static final String DEFAULT_PROVIDER = "defaultProvider"; + + @Override + public String getDisplayType() { + return "Identity Provider Redirector"; + } + + @Override + public String getReferenceCategory() { + return null; + } + + @Override + public boolean isConfigurable() { + return true; + } + + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public boolean isUserSetupAllowed() { + return true; + } + + @Override + public String getHelpText() { + return "Redirects to default Identity Provider or Identity Provider specified with kc_idp_hint query parameter"; + } + + @Override + public List getConfigProperties() { + ProviderConfigProperty rep = new ProviderConfigProperty(DEFAULT_PROVIDER, "Default Identity Provider", "To automatically redirect to an identity provider set to the alias of the identity provider", STRING_TYPE, null); + return Collections.singletonList(rep); + } + + @Override + public Authenticator create(KeycloakSession session) { + return new IdentityProviderAuthenticator(); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "identity-provider-redirector"; + } + +} diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java index bfbf7a1468..12dba27e2c 100755 --- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java +++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java @@ -17,8 +17,6 @@ package org.keycloak.protocol; -import java.util.List; - import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -31,14 +29,11 @@ import org.keycloak.events.Details; import org.keycloak.events.EventBuilder; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.LoginProtocol.Error; import org.keycloak.services.ServicesLogger; -import org.keycloak.services.Urls; import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.resources.LoginActionsService; /** @@ -95,15 +90,6 @@ public abstract class AuthorizationEndpointBase { * @return response to be returned to the browser */ protected Response handleBrowserAuthenticationRequest(ClientSessionModel clientSession, LoginProtocol protocol, boolean isPassive, boolean redirectToAuthentication) { - - List identityProviders = realm.getIdentityProviders(); - for (IdentityProviderModel identityProvider : identityProviders) { - if (identityProvider.isEnabled() && identityProvider.isAuthenticateByDefault()) { - // TODO if we are isPassive we should propagate this flag to default identity provider also if possible - return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode()); - } - } - AuthenticationFlowModel flow = getAuthenticationFlow(); String flowId = flow.getId(); AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.AUTHENTICATE_PATH); @@ -147,11 +133,4 @@ public abstract class AuthorizationEndpointBase { return realm.getBrowserFlow(); } - protected Response buildRedirectToIdentityProvider(String providerId, String accessCode) { - logger.debug("Automatically redirect to identity provider: " + providerId); - return Response.temporaryRedirect( - Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, this.realm.getName(), accessCode)) - .build(); - } - } \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java index 1b4db2951a..3f18b27c3a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java @@ -29,11 +29,9 @@ import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; -import org.keycloak.forms.login.LoginFormsProvider; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.RealmModel; import org.keycloak.protocol.AuthorizationEndpointBase; import org.keycloak.protocol.oidc.OIDCLoginProtocol; @@ -46,7 +44,6 @@ import org.keycloak.protocol.oidc.utils.RedirectUtils; import org.keycloak.services.ErrorPageException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; -import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.util.CacheControlUtil; @@ -313,19 +310,6 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { } private Response buildAuthorizationCodeAuthorizationResponse() { - String idpHint = request.getIdpHint(); - - if (idpHint != null && !"".equals(idpHint)) { - IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint); - - if (identityProviderModel == null) { - return session.getProvider(LoginFormsProvider.class) - .setError(Messages.IDENTITY_PROVIDER_NOT_FOUND, idpHint) - .createErrorPage(); - } - return buildRedirectToIdentityProvider(idpHint, new ClientSessionCode(realm, clientSession).getCode()); - } - this.event.event(EventType.LOGIN); clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE); diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 97c92994fb..fa7ee28eea 100755 --- a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -20,6 +20,7 @@ org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory org.keycloak.authentication.authenticators.browser.OTPFormAuthenticatorFactory org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory org.keycloak.authentication.authenticators.browser.SpnegoAuthenticatorFactory +org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticatorFactory org.keycloak.authentication.authenticators.directgrant.ValidateOTP org.keycloak.authentication.authenticators.directgrant.ValidatePassword org.keycloak.authentication.authenticators.directgrant.ValidateUsername @@ -33,4 +34,4 @@ org.keycloak.authentication.authenticators.broker.IdpConfirmLinkAuthenticatorFac org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory org.keycloak.authentication.authenticators.broker.IdpUsernamePasswordFormFactory org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticatorFactory -org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticator +org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticator \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java index f115c49326..c32ba08618 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java @@ -104,7 +104,7 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest { } void compareExecution(AuthenticationExecutionExportRepresentation expected, AuthenticationExecutionExportRepresentation actual) { - Assert.assertEquals("Execution flowAlias - " + actual.getAuthenticator(), expected.getFlowAlias(), actual.getFlowAlias()); + Assert.assertEquals("Execution flowAlias - " + actual.getFlowAlias(), expected.getFlowAlias(), actual.getFlowAlias()); Assert.assertEquals("Execution authenticator - " + actual.getAuthenticator(), expected.getAuthenticator(), actual.getAuthenticator()); Assert.assertEquals("Execution userSetupAllowed - " + actual.getAuthenticator(), expected.isUserSetupAllowed(), actual.isUserSetupAllowed()); Assert.assertEquals("Execution authenticatorFlow - " + actual.getAuthenticator(), expected.isAutheticatorFlow(), actual.isAutheticatorFlow()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java index 42015edce0..d7caba46d5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java @@ -94,7 +94,7 @@ public class ExecutionTest extends AbstractAuthenticationTest { // we'll need auth-cookie later AuthenticationExecutionInfoRepresentation authCookieExec = findExecutionByProvider("auth-cookie", executionReps); - compareExecution(newExecInfo("Review Profile", "idp-review-profile", true, 0, 3, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec); + compareExecution(newExecInfo("Review Profile", "idp-review-profile", true, 0, 4, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec); // remove execution authMgmtResource.removeExecution(exec.getId()); @@ -164,7 +164,7 @@ public class ExecutionTest extends AbstractAuthenticationTest { // Note: there is no checking in addExecution if requirement is one of requirementChoices // Thus we can have OPTIONAL which is neither ALTERNATIVE, nor DISABLED - compareExecution(newExecInfo("Cookie", "auth-cookie", false, 0, 2, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec); + compareExecution(newExecInfo("Cookie", "auth-cookie", false, 0, 3, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java index fc50bc6916..90f88745a9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java @@ -203,7 +203,7 @@ public class FlowTest extends AbstractAuthenticationTest { // adjust expected values before comparing browser.setAlias("Copy of browser"); browser.setBuiltIn(false); - browser.getAuthenticationExecutions().get(2).setFlowAlias("Copy of browser forms"); + browser.getAuthenticationExecutions().get(3).setFlowAlias("Copy of browser forms"); compareFlows(browser, copyOfBrowser); // get new flow directly and compare diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java index 3640af5288..ee79c27213 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java @@ -125,12 +125,14 @@ public class InitialFlowsTest extends AbstractAuthenticationTest { AuthenticationFlowRepresentation flow = newFlow("browser", "browser based authentication", "basic-flow", true, true); addExecExport(flow, null, false, "auth-cookie", false, null, ALTERNATIVE, 10); addExecExport(flow, null, false, "auth-spnego", false, null, DISABLED, 20); + addExecExport(flow, null, false, "identity-provider-redirector", false, null, ALTERNATIVE, 25); addExecExport(flow, "forms", false, null, true, null, ALTERNATIVE, 30); List execs = new LinkedList<>(); addExecInfo(execs, "Cookie", "auth-cookie", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}); addExecInfo(execs, "Kerberos", "auth-spnego", false, 0, 1, DISABLED, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED}); - addExecInfo(execs, "forms", null, false, 0, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}); + addExecInfo(execs, "Identity Provider Redirector", "identity-provider-redirector", true, 0, 2, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}); + addExecInfo(execs, "forms", null, false, 0, 3, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}); addExecInfo(execs, "Username Password Form", "auth-username-password-form", false, 1, 0, REQUIRED, null, new String[]{REQUIRED}); addExecInfo(execs, "OTP Form", "auth-otp-form", false, 1, 1, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED}); expected.add(new FlowExecutions(flow, execs)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java index 858e06ef45..4e07e6fbcc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java @@ -146,6 +146,7 @@ public class ProvidersTest extends AbstractAuthenticationTest { addProviderInfo(result, "direct-grant-validate-username", "Username Validation", "Validates the username supplied as a 'username' form parameter in direct grant request"); addProviderInfo(result, "http-basic-authenticator", "HTTP Basic Authentication", "Validates username and password from Authorization HTTP header"); + addProviderInfo(result, "identity-provider-redirector", "Identity Provider Redirector", "Redirects to default Identity Provider or Identity Provider specified with kc_idp_hint query parameter"); addProviderInfo(result, "idp-confirm-link", "Confirm link existing account", "Show the form where user confirms if he wants " + "to link identity provider with existing account or rather edit user profile data retrieved from identity provider to avoid conflict"); addProviderInfo(result, "idp-create-user-if-unique", "Create User If Unique", "Detect if there is existing Keycloak account " + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties index 04c79fecd3..ce33acfc86 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties @@ -61,3 +61,5 @@ log4j.logger.org.hibernate=off log4j.logger.org.jboss.resteasy=warn log4j.logger.org.apache.directory.api=warn log4j.logger.org.apache.directory.server.core=warn + +log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace \ No newline at end of file diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java index 7c1f47beb0..36c291f02b 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java @@ -20,19 +20,19 @@ package org.keycloak.testsuite.broker; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.KeycloakServer; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.OAuthGrantPage; import org.keycloak.testsuite.rule.AbstractKeycloakRule; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; -import org.keycloak.testsuite.KeycloakServer; -import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -94,6 +94,16 @@ public class IdentityProviderHintTest { assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth")); - assertEquals("Could not find an identity provider with the identifier.", this.driver.findElement(By.className("instruction")).getText()); + System.out.println(driver.getPageSource()); + assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); + } + + private AuthenticationExecutionInfoRepresentation findExecution(RealmResource realm) { + for (AuthenticationExecutionInfoRepresentation e : realm.flows().getExecutions("browser")) { + if (e.getProviderId().equals("identity-provider-redirector")) { + return e; + } + } + return null; } } diff --git a/testsuite/integration/src/test/resources/log4j.properties b/testsuite/integration/src/test/resources/log4j.properties index 5d5369c20b..f0ff6ac6e3 100755 --- a/testsuite/integration/src/test/resources/log4j.properties +++ b/testsuite/integration/src/test/resources/log4j.properties @@ -74,4 +74,7 @@ log4j.logger.org.apache.directory.server.core=warn log4j.logger.org.apache.directory.server.ldap.LdapProtocolHandler=error # Enable to view HttpClient connection pool activity -#log4j.logger.org.apache.http.impl.conn=debug \ No newline at end of file +#log4j.logger.org.apache.http.impl.conn=debug + +# Enable to view details from identity provider authenticator +# log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html index 81690be0aa..bbbf9b9ba5 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html @@ -34,13 +34,6 @@
    {{:: 'identity-provider.enabled.tooltip' | translate}}
    -
    - -
    - -
    - {{:: 'identity-provider.authenticate-by-default.tooltip' | translate}} -
    diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html index f9b8c740e9..eaf4439f03 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html @@ -31,13 +31,6 @@
    {{:: 'identity-provider.enabled.tooltip' | translate}}
    -
    - -
    - -
    - {{:: 'identity-provider.authenticate-by-default.tooltip' | translate}} -
    diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html index 9599d98905..6c7ceb79e6 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html @@ -70,13 +70,6 @@
    {{:: 'trust-email.tooltip' | translate}}
    -
    - -
    - -
    - {{:: 'identity-provider.authenticate-by-default.tooltip' | translate}} -
    From 16282aeb7bad3046349748859e6323c785a98919 Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 8 Sep 2016 08:36:31 +0200 Subject: [PATCH 15/53] KEYCLOAK-3537 Username not shown when validation error on Account profile page --- .../account/freemarker/model/AccountBean.java | 6 +++++- .../testsuite/account/AccountTest.java | 21 +++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java index 857bfc04d9..ceaaf94449 100755 --- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java +++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java @@ -73,7 +73,11 @@ public class AccountBean { } public String getUsername() { - return profileFormData != null ? profileFormData.getFirst("username") : user.getUsername(); + if (profileFormData != null && profileFormData.containsKey("username")) { + return profileFormData.getFirst("username"); + } else { + return user.getUsername(); + } } public String getEmail() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java index e4dfe0eea2..35d11d15f4 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -377,12 +377,15 @@ public class AccountTest extends TestRealmKeycloakTest { } @Test - public void changeProfile() { + public void changeProfile() throws Exception { + setEditUsernameAllowed(false); + profilePage.open(); loginPage.login("test-user@localhost", "password"); events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent(); + Assert.assertEquals("test-user@localhost", profilePage.getUsername()); Assert.assertEquals("Tom", profilePage.getFirstName()); Assert.assertEquals("Brady", profilePage.getLastName()); Assert.assertEquals("test-user@localhost", profilePage.getEmail()); @@ -391,6 +394,7 @@ public class AccountTest extends TestRealmKeycloakTest { profilePage.updateProfile("", "New last", "new@email.com"); Assert.assertEquals("Please specify first name.", profilePage.getError()); + Assert.assertEquals("test-user@localhost", profilePage.getUsername()); Assert.assertEquals("", profilePage.getFirstName()); Assert.assertEquals("New last", profilePage.getLastName()); Assert.assertEquals("new@email.com", profilePage.getEmail()); @@ -417,6 +421,7 @@ public class AccountTest extends TestRealmKeycloakTest { profilePage.clickCancel(); + Assert.assertEquals("test-user@localhost", profilePage.getUsername()); Assert.assertEquals("Tom", profilePage.getFirstName()); Assert.assertEquals("Brady", profilePage.getLastName()); Assert.assertEquals("test-user@localhost", profilePage.getEmail()); @@ -426,6 +431,7 @@ public class AccountTest extends TestRealmKeycloakTest { profilePage.updateProfile("New first", "New last", "new@email.com"); Assert.assertEquals("Your account has been updated.", profilePage.getSuccess()); + Assert.assertEquals("test-user@localhost", profilePage.getUsername()); Assert.assertEquals("New first", profilePage.getFirstName()); Assert.assertEquals("New last", profilePage.getLastName()); Assert.assertEquals("new@email.com", profilePage.getEmail()); @@ -436,18 +442,21 @@ public class AccountTest extends TestRealmKeycloakTest { // reset user for other tests profilePage.updateProfile("Tom", "Brady", "test-user@localhost"); events.clear(); + + // Revert + setEditUsernameAllowed(true); } - private void setEditUsernameAllowed() { + private void setEditUsernameAllowed(boolean allowed) { RealmRepresentation testRealm = testRealm().toRepresentation(); - testRealm.setEditUsernameAllowed(true); + testRealm.setEditUsernameAllowed(allowed); testRealm().update(testRealm); } @Test public void changeUsername() { // allow to edit the username in realm - setEditUsernameAllowed(); + setEditUsernameAllowed(true); profilePage.open(); loginPage.login("test-user@localhost", "password"); @@ -504,7 +513,7 @@ public class AccountTest extends TestRealmKeycloakTest { @Test public void changeUsernameLoginWithOldUsername() { addUser("change-username", "change-username@localhost"); - setEditUsernameAllowed(); + setEditUsernameAllowed(true); profilePage.open(); loginPage.login("change-username", "password"); @@ -530,7 +539,7 @@ public class AccountTest extends TestRealmKeycloakTest { @Test public void changeEmailLoginWithOldEmail() { addUser("change-email", "change-username@localhost"); - setEditUsernameAllowed(); + setEditUsernameAllowed(true); profilePage.open(); loginPage.login("change-username@localhost", "password"); From df3079852e2c555adee32a27c795d11312a6af4c Mon Sep 17 00:00:00 2001 From: didiez Date: Fri, 2 Sep 2016 11:26:32 +0200 Subject: [PATCH 16/53] Prevent clearing all registered sessions when invalidating some by sessionId --- .../adapters/springsecurity/management/HttpSessionManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/management/HttpSessionManager.java b/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/management/HttpSessionManager.java index 7472d79b8d..cb8a764bbb 100644 --- a/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/management/HttpSessionManager.java +++ b/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/management/HttpSessionManager.java @@ -69,6 +69,5 @@ public class HttpSessionManager implements HttpSessionListener, UserSessionManag session.invalidate(); } } - sessions.clear(); } } From 4fd0238ca9831e77a0142d19f3b37ae1f7318666 Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 8 Sep 2016 12:29:24 +0200 Subject: [PATCH 17/53] KEYCLOAK-3542 Not possible to enable bruteForceProtection for realm --- .../models/utils/RepresentationToModel.java | 22 ++++++--- .../testsuite/admin/realm/RealmTest.java | 48 +++++++++++++++---- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 75b4b73b33..ac285a8fe1 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -741,6 +741,21 @@ public class RepresentationToModel { if (rep.getRealm() != null) { renameRealm(realm, rep.getRealm()); } + + // Import attributes first, so the stuff saved directly on representation (displayName, bruteForce etc) has bigger priority + if (rep.getAttributes() != null) { + Set attrsToRemove = new HashSet<>(realm.getAttributes().keySet()); + attrsToRemove.removeAll(rep.getAttributes().keySet()); + + for (Map.Entry entry : rep.getAttributes().entrySet()) { + realm.setAttribute(entry.getKey(), entry.getValue()); + } + + for (String attr : attrsToRemove) { + realm.removeAttribute(attr); + } + } + if (rep.getDisplayName() != null) realm.setDisplayName(rep.getDisplayName()); if (rep.getDisplayNameHtml() != null) realm.setDisplayNameHtml(rep.getDisplayNameHtml()); if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); @@ -843,13 +858,6 @@ public class RepresentationToModel { if (rep.getClientAuthenticationFlow() != null) { realm.setClientAuthenticationFlow(realm.getFlowByAlias(rep.getClientAuthenticationFlow())); } - - if (rep.getAttributes() != null) { - for (Map.Entry entry : rep.getAttributes().entrySet()) { - realm.setAttribute(entry.getKey(), entry.getValue()); - } - } - } // Basic realm stuff diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index 5bf2cc1651..53a361516c 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -239,15 +239,6 @@ public class RealmTest extends AbstractAdminTest { assertEquals(Boolean.FALSE, rep.isRegistrationEmailAsUsername()); assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed()); - // attributes - rep.getAttributes().put("foo", "bar"); - - realm.update(rep); - assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM); - - rep = realm.toRepresentation(); - assertEquals("bar", rep.getAttributes().get("foo")); - } @Test @@ -277,6 +268,45 @@ public class RealmTest extends AbstractAdminTest { assertEquals(2, rep.getSupportedLocales().size()); } + @Test + public void updateRealmAttributes() { + // first change + RealmRepresentation rep = new RealmRepresentation(); + rep.setAttributes(new HashMap<>()); + rep.getAttributes().put("foo1", "bar1"); + rep.getAttributes().put("foo2", "bar2"); + + rep.setBruteForceProtected(true); + rep.setDisplayName("dn1"); + + realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM); + + rep = realm.toRepresentation(); + + assertEquals("bar1", rep.getAttributes().get("foo1")); + assertEquals("bar2", rep.getAttributes().get("foo2")); + assertTrue(rep.isBruteForceProtected()); + assertEquals("dn1", rep.getDisplayName()); + + // second change + rep.setBruteForceProtected(false); + rep.setDisplayName("dn2"); + rep.getAttributes().put("foo1", "bar11"); + rep.getAttributes().remove("foo2"); + + realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM); + + rep = realm.toRepresentation(); + + assertFalse(rep.isBruteForceProtected()); + assertEquals("dn2", rep.getDisplayName()); + + assertEquals("bar11", rep.getAttributes().get("foo1")); + assertFalse(rep.getAttributes().containsKey("foo2")); + } + @Test public void getRealmRepresentation() { RealmRepresentation rep = realm.toRepresentation(); From 36bb94afb85473842a086b24fcfa983600a0139d Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Thu, 8 Sep 2016 07:36:00 +0200 Subject: [PATCH 18/53] Environment dependent provider --- .../EnvironmentDependentProviderFactory.java | 33 +++++++++++++++++++ .../DefaultKeycloakSessionFactory.java | 15 +++++++-- .../PlainTextPasswordProviderFactory.java | 11 ++++++- 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 server-spi/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java b/server-spi/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java new file mode 100644 index 0000000000..b4e993a2c9 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java @@ -0,0 +1,33 @@ +/* + * 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.provider; + +/** + * Providers that are only supported in some environments can implement this interface to be able to determine if they + * should be available or not. + * + * @author Stian Thorgersen + */ +public interface EnvironmentDependentProviderFactory { + + /** + * @return true if the provider is supported and should be available, false otherwise + */ + boolean isSupported(); + +} diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index 45bef3c3ea..a1521bd659 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -20,6 +20,7 @@ import org.keycloak.Config; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEventListener; @@ -193,8 +194,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr } Config.Scope scope = Config.scope(spi.getName(), provider); - if (scope.getBoolean("enabled", true)) { - + if (isEnabled(factory, scope)) { factory.init(scope); if (spi.isInternal() && !isInternal(factory)) { @@ -208,7 +208,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr } else { for (ProviderFactory factory : pm.load(spi)) { Config.Scope scope = Config.scope(spi.getName(), factory.getId()); - if (scope.getBoolean("enabled", true)) { + if (isEnabled(factory, scope)) { factory.init(scope); if (spi.isInternal() && !isInternal(factory)) { @@ -223,7 +223,16 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr } } return factoryMap; + } + private boolean isEnabled(ProviderFactory factory, Config.Scope scope) { + if (!scope.getBoolean("enabled", true)) { + return false; + } + if (factory instanceof EnvironmentDependentProviderFactory) { + return ((EnvironmentDependentProviderFactory) factory).isSupported(); + } + return true; } protected void loadSPIs(ProviderManager pm, List spiList) { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java index cdb324ca5c..dd281d17f8 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java @@ -21,12 +21,15 @@ import org.keycloak.hash.PasswordHashProvider; import org.keycloak.hash.PasswordHashProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.EnvironmentDependentProviderFactory; + +import java.io.File; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class PlainTextPasswordProviderFactory implements PasswordHashProviderFactory { +public class PlainTextPasswordProviderFactory implements PasswordHashProviderFactory, EnvironmentDependentProviderFactory { @Override public PasswordHashProvider create(KeycloakSession session) { return new PlainTextPasswordProvider(); @@ -51,4 +54,10 @@ public class PlainTextPasswordProviderFactory implements PasswordHashProviderFac public String getId() { return "text"; } + + // TODO REMOVE THIS + @Override + public boolean isSupported() { + return !new File("/tmp/disable-text-hash").exists(); + } } From f24d73e8d6cbc8d1882dbe4a056a011ca2db558c Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Thu, 8 Sep 2016 14:04:12 +0200 Subject: [PATCH 19/53] KEYCLOAK-3538 Update examples to refer to standalone.xml for config --- examples/providers/authenticator/README.md | 11 ++++----- examples/providers/domain-extension/README.md | 10 ++++---- .../providers/event-listener-sysout/README.md | 24 ++++++++++--------- examples/providers/event-store-mem/README.md | 24 +++++++++---------- .../providers/federation-provider/README.md | 11 ++++----- .../BasePropertiesFederationFactory.java | 2 +- examples/providers/rest/README.md | 10 ++++---- examples/themes/README.md | 22 ++++++++--------- 8 files changed, 57 insertions(+), 57 deletions(-) diff --git a/examples/providers/authenticator/README.md b/examples/providers/authenticator/README.md index 1edf0ed693..54dc752734 100755 --- a/examples/providers/authenticator/README.md +++ b/examples/providers/authenticator/README.md @@ -6,13 +6,12 @@ of Keycloak. To deploy, build this directory then take the jar and copy it to KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.secret-question --resources=target/authenticator-required-action-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-services,org.jboss.resteasy.resteasy-jaxrs,javax.ws.rs.api" -Then registering the provider by editing keycloak-server.json and adding the module to the providers field: - - "providers": [ - .... - "module:org.keycloak.examples.secret-question" - ], +Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element: + + ... + module:org.keycloak.examples.secret-question + You then have to copy the secret-question.ftl and secret-question-config.ftl files to the themes/base/login directory. diff --git a/examples/providers/domain-extension/README.md b/examples/providers/domain-extension/README.md index e1aa2cdb62..51e2b3c56a 100644 --- a/examples/providers/domain-extension/README.md +++ b/examples/providers/domain-extension/README.md @@ -6,12 +6,12 @@ To run, deploy as a module by running: $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist" -Then registering the provider by editing keycloak-server.json and adding the module to the providers field: +Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element: - "providers": [ - .... - "module:org.keycloak.examples.domain-extension-example" - ], + + ... + module:org.keycloak.examples.domain-extension-example + Then start (or restart) the server. diff --git a/examples/providers/event-listener-sysout/README.md b/examples/providers/event-listener-sysout/README.md index 57519f3e2c..8e1a0850ad 100644 --- a/examples/providers/event-listener-sysout/README.md +++ b/examples/providers/event-listener-sysout/README.md @@ -5,23 +5,25 @@ To deploy copy target/event-listener-sysout-example.jar to providers directory. KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi" -Then registering the provider by editing keycloak-server.json and adding the module to the providers field: +Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element: - "providers": [ - .... - "module:org.keycloak.examples.event-sysout" - ], + + ... + module:org.keycloak.examples.event-sysout + Then start (or restart) the server. Once started open the admin console, select your realm, then click on Events, followed by config. Click on Listeners select box, then pick sysout from the dropdown. After this try to logout and login again to see events printed to System.out. The example event listener can be configured to exclude certain events, for example to exclude REFRESH_TOKEN and -CODE_TO_TOKEN events add the following to keycloak-server.json: +CODE_TO_TOKEN events add the following to `standalone.xml`: ... - "eventsListener": { - "sysout": { - "exclude": [ "REFRESH_TOKEN", "CODE_TO_TOKEN" ] - } - } + + + + + + diff --git a/examples/providers/event-store-mem/README.md b/examples/providers/event-store-mem/README.md index d533fdad69..682ff42b78 100644 --- a/examples/providers/event-store-mem/README.md +++ b/examples/providers/event-store-mem/README.md @@ -5,24 +5,24 @@ To deploy copy target/event-store-mem-example.jar to providers directory. Altern KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi" -Then registering the provider by editing keycloak-server.json and adding the module to the providers field: +Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element: - "providers": [ - .... - "module:org.keycloak.examples.event-inmem" - ], + + ... + module:org.keycloak.examples.event-inmem + -Then edit standalone/configuration/keycloak-server.json, change: +Then edit `standalone/configuration/standalone.xml`, change: - "eventsStore": { - "provider": "jpa" - } + + jpa + to: - "eventsStore": { - "provider": "in-mem" - } + + in-mem + Then start (or restart)the server. Once started open the admin console, select your realm, then click on Events, followed by config. Set the toggle for Enabled to ON. After this try to logout and login again then open the Events tab diff --git a/examples/providers/federation-provider/README.md b/examples/providers/federation-provider/README.md index c8b443079d..c90e791547 100755 --- a/examples/providers/federation-provider/README.md +++ b/examples/providers/federation-provider/README.md @@ -6,13 +6,12 @@ key pairs. To deploy, build this directory then take the jar and copy it to pro KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi" -Then registering the provider by editing keycloak-server.json and adding the module to the providers field: - - "providers": [ - .... - "module:org.keycloak.examples.userprops" - ], +Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element: + + ... + module:org.keycloak.examples.userprops + You will then have to restart the authentication server. diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java index 2d5abc3f22..e9ec451af5 100755 --- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java +++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/BasePropertiesFederationFactory.java @@ -96,7 +96,7 @@ public abstract class BasePropertiesFederationFactory implements UserFederationP } /** - * You can import additional plugin configuration from keycloak-server.json here. + * You can import additional plugin configuration from standalone.xml here. * * @param config */ diff --git a/examples/providers/rest/README.md b/examples/providers/rest/README.md index 5124f88b1d..5ee9327132 100644 --- a/examples/providers/rest/README.md +++ b/examples/providers/rest/README.md @@ -5,12 +5,12 @@ To deploy copy target/hello-rest-example.jar to providers directory. Alternative $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,javax.ws.rs.api" -Then registering the provider by editing keycloak-server.json and adding the module to the providers field: +Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element: - "providers": [ - .... - "module:org.keycloak.examples.hello-rest-example" - ], + + ... + module:org.keycloak.examples.hello-rest-example + Then start (or restart) the server. Once started open http://localhost:8080/realms/master/hello and you should see the message _Hello master_. You can also invoke the endpoint for other realms by replacing `master` with the realm name in the above url. \ No newline at end of file diff --git a/examples/themes/README.md b/examples/themes/README.md index ea160bc650..5089ca1e7f 100644 --- a/examples/themes/README.md +++ b/examples/themes/README.md @@ -17,14 +17,14 @@ Alternatively you can deploy as modules. This can be done by first running: mvn clean install $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.example.themes --resources=target/keycloak-example-themes.jar" -Then open $KEYCLOAK_HOME/standalone/configuration/keycloak-server.json and register the theme module by adding: - - "theme": { - "module": { - "modules": [ "org.keycloak.example.themes" ] - } - } +Then open `standalone/configuration/standalone.xml` and register the theme module by adding: + + ... + + org.keycloak.example.themes + + Address Theme ------------------- @@ -45,11 +45,11 @@ Change Logo Theme To enable the theme open the admin console, select your realm, click on `Theme`. In the dropdowns for `Login Theme`, `Account Theme` and `Admin Console Theme` select `logo-example`. Click `Save` and login to the realm to see the new theme in action. -To change the theme for the welcome pages open `standalone/configuration/keycloak-server.json` find the config for `theme` and add 'welcomeTheme': +To change the theme for the welcome pages open `standalone/configuration/standalone.xml` find the config for `theme` and add 'welcomeTheme': - "theme": { + ... - "welcomeTheme": "logo-example" - }, + logo-example + One thing to note is that to change the admin console for the master admin console (`/auth/admin`) you need to change the theme for the master realm. Changing the admin console theme for any other realms will only change the admin console for that specific realm (for example `/auth/admin/myrealm/console`). From fa8f60a5f0408432b084dd91ba8688af0a440870 Mon Sep 17 00:00:00 2001 From: Vlasta Ramik Date: Thu, 8 Sep 2016 15:25:17 +0200 Subject: [PATCH 20/53] remove jta=false from default datasource definition --- .../servers/auth-server/jboss/common/datasource.xsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/datasource.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/datasource.xsl index 37ce56f4d0..bf199cb88a 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/datasource.xsl +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/datasource.xsl @@ -43,7 +43,7 @@ - + From 11245701d27bf19ef20f948359d92510e5aedf7d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 8 Sep 2016 07:36:33 -0300 Subject: [PATCH 21/53] Check if SSSD is available via DBUS --- .../java/cx/ath/matthew/LibraryLoader.java | 8 ++++- .../src/main/java/org/freedesktop/DBus.java | 30 ++++++++++-------- .../freedesktop/sssd/infopipe/InfoPipe.java | 4 ++- .../sssd/SSSDFederationProviderFactory.java | 10 ++++-- .../keycloak/federation/sssd/api/Sssd.java | 31 ++++++++++++++++--- .../DefaultKeycloakSessionFactory.java | 1 + .../testsuite/admin/UserFederationTest.java | 2 +- .../PlainTextPasswordProviderFactory.java | 11 +------ 8 files changed, 64 insertions(+), 33 deletions(-) diff --git a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java index ffdf02dd6e..095d214467 100644 --- a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java +++ b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java @@ -31,7 +31,7 @@ public class LibraryLoader { private static final String VERSION = "0.0.8"; private static boolean loadSucceeded; - public static void load() { + public static LibraryLoader load() { for (String path : PATHS) { try { System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION)); @@ -45,5 +45,11 @@ public class LibraryLoader { if (!loadSucceeded) LOGGER.log(Level.WARNING, "libunix_dbus_java not found\n" + "Please, make sure you have the package libunix-dbus-java installed."); + + return new LibraryLoader(); + } + + public boolean succeed() { + return loadSucceeded; } } diff --git a/federation/sssd/src/main/java/org/freedesktop/DBus.java b/federation/sssd/src/main/java/org/freedesktop/DBus.java index 1aa180a4ab..b7a16877ae 100644 --- a/federation/sssd/src/main/java/org/freedesktop/DBus.java +++ b/federation/sssd/src/main/java/org/freedesktop/DBus.java @@ -30,18 +30,22 @@ import java.util.List; import java.util.Map; public interface DBus extends DBusInterface { - public static final int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01; - public static final int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02; - public static final int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04; - public static final int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1; - public static final int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2; - public static final int DBUS_REQUEST_NAME_REPLY_EXISTS = 3; - public static final int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4; - public static final int DBUS_RELEASE_NAME_REPLY_RELEASED = 1; - public static final int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2; - public static final int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3; - public static final int DBUS_START_REPLY_SUCCESS = 1; - public static final int DBUS_START_REPLY_ALREADY_RUNNING = 2; + + String BUSNAME = "org.freedesktop.DBus"; + String OBJECTPATH = "/org/freedesktop/DBus"; + + int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01; + int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02; + int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04; + int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1; + int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2; + int DBUS_REQUEST_NAME_REPLY_EXISTS = 3; + int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4; + int DBUS_RELEASEME_REPLY_RELEASED = 1; + int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2; + int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3; + int DBUS_START_REPLY_SUCCESS = 1; + int DBUS_START_REPLY_ALREADY_RUNNING = 2; /** * All DBus Applications should respond to the Ping method on this interface @@ -527,4 +531,4 @@ public interface DBus extends DBusInterface { } } } -} +} \ No newline at end of file diff --git a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java index 8791170caf..6152d26a25 100644 --- a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java +++ b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java @@ -32,6 +32,8 @@ import java.util.Map; public interface InfoPipe extends DBusInterface { String OBJECTPATH = "/org/freedesktop/sssd/infopipe"; + String BUSNAME = "org.freedesktop.sssd.infopipe"; + @DBusMemberName("GetUserAttr") Map getUserAttributes(String user, List attr); @@ -39,4 +41,4 @@ public interface InfoPipe extends DBusInterface { @DBusMemberName("GetUserGroups") List getUserGroups(String user); -} +} \ No newline at end of file diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java index 6a287a7ad8..3140e9e48a 100755 --- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java @@ -19,6 +19,7 @@ package org.keycloak.federation.sssd; import org.jboss.logging.Logger; import org.keycloak.Config; +import org.keycloak.federation.sssd.api.Sssd; import org.keycloak.federation.sssd.impl.PAMAuthenticator; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; @@ -26,6 +27,7 @@ import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderFactory; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationSyncResult; +import org.keycloak.provider.EnvironmentDependentProviderFactory; import java.util.Date; import java.util.HashSet; @@ -35,7 +37,7 @@ import java.util.Set; * @author Bruno Oliveira * @version $Revision: 1 $ */ -public class SSSDFederationProviderFactory implements UserFederationProviderFactory { +public class SSSDFederationProviderFactory implements UserFederationProviderFactory, EnvironmentDependentProviderFactory { private static final String PROVIDER_NAME = "sssd"; private static final Logger logger = Logger.getLogger(SSSDFederationProvider.class); @@ -99,4 +101,8 @@ public class SSSDFederationProviderFactory implements UserFederationProviderFact return new PAMAuthenticator(username, factors); } -} + @Override + public boolean isSupported() { + return Sssd.isAvailable(); + } +} \ No newline at end of file diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java index a5bb57a308..fd9dc6752e 100644 --- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java @@ -17,6 +17,8 @@ package org.keycloak.federation.sssd.api; +import cx.ath.matthew.LibraryLoader; +import org.freedesktop.DBus; import org.freedesktop.dbus.DBusConnection; import org.freedesktop.dbus.Variant; import org.freedesktop.dbus.exceptions.DBusException; @@ -35,8 +37,6 @@ import java.util.Vector; */ public class Sssd { - public static final String BUSNAME = "org.freedesktop.sssd.infopipe"; - public static User user() { return SingletonHolder.USER_OBJECT; } @@ -45,6 +45,7 @@ public class Sssd { return SingletonHolder.INFOPIPE_OBJECT; } + public static void disconnect() { SingletonHolder.DBUS_CONNECTION.disconnect(); } @@ -67,10 +68,10 @@ public class Sssd { static { try { DBUS_CONNECTION = DBusConnection.getConnection(DBusConnection.SYSTEM); - INFOPIPE_OBJECT = DBUS_CONNECTION.getRemoteObject(BUSNAME, InfoPipe.OBJECTPATH, InfoPipe.class); - USER_OBJECT = DBUS_CONNECTION.getRemoteObject(BUSNAME, User.OBJECTPATH, User.class); + INFOPIPE_OBJECT = DBUS_CONNECTION.getRemoteObject(InfoPipe.BUSNAME, InfoPipe.OBJECTPATH, InfoPipe.class); + USER_OBJECT = DBUS_CONNECTION.getRemoteObject(InfoPipe.BUSNAME, User.OBJECTPATH, User.class); } catch (DBusException e) { - e.printStackTrace(); + logger.error("Failed to obtain D-Bus connection", e); } } } @@ -108,4 +109,24 @@ public class Sssd { } return userGroups; } + + public static boolean isAvailable(){ + boolean sssdAvailable = false; + try { + if (LibraryLoader.load().succeed()) { + DBusConnection connection = DBusConnection.getConnection(DBusConnection.SYSTEM); + DBus dbus = connection.getRemoteObject(DBus.BUSNAME, DBus.OBJECTPATH, DBus.class); + sssdAvailable = Arrays.asList(dbus.ListNames()).contains(InfoPipe.BUSNAME); + if (!sssdAvailable) { + logger.debugv("SSSD is not available in your system. Federation provider will be disabled."); + } else { + sssdAvailable = true; + } + connection.disconnect(); + } + } catch (DBusException e) { + logger.error("Failed to check the status of SSSD", e); + } + return sssdAvailable; + } } diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index a1521bd659..3965f714d8 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -205,6 +205,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr logger.debugv("Loaded SPI {0} (provider = {1})", spi.getName(), provider); } + } else { for (ProviderFactory factory : pm.load(spi)) { Config.Scope scope = Config.scope(spi.getName(), factory.getId()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java index 7a939c3518..8cef8a1bbc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java @@ -51,7 +51,7 @@ public class UserFederationTest extends AbstractAdminTest { @Test public void testProviderFactories() { List providerFactories = userFederation().getProviderFactories(); - Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable", "sssd"); + Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable"); // Builtin provider without properties UserFederationProviderFactoryRepresentation ldapProvider = userFederation().getProviderFactory("ldap"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java index dd281d17f8..cdb324ca5c 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/PlainTextPasswordProviderFactory.java @@ -21,15 +21,12 @@ import org.keycloak.hash.PasswordHashProvider; import org.keycloak.hash.PasswordHashProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.provider.EnvironmentDependentProviderFactory; - -import java.io.File; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class PlainTextPasswordProviderFactory implements PasswordHashProviderFactory, EnvironmentDependentProviderFactory { +public class PlainTextPasswordProviderFactory implements PasswordHashProviderFactory { @Override public PasswordHashProvider create(KeycloakSession session) { return new PlainTextPasswordProvider(); @@ -54,10 +51,4 @@ public class PlainTextPasswordProviderFactory implements PasswordHashProviderFac public String getId() { return "text"; } - - // TODO REMOVE THIS - @Override - public boolean isSupported() { - return !new File("/tmp/disable-text-hash").exists(); - } } From 7af16fc7475385796c849eb65e109a43f2a955cb Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Fri, 9 Sep 2016 01:03:09 -0300 Subject: [PATCH 22/53] [KEYCLOAK-3534] - Authorization tab appears too soon in admin console --- .../AuthorizationClientExample.java | 22 +++++++++++-------- .../admin/PolicyEvaluationService.java | 7 ++++++ .../PolicyEvaluationResponse.java | 5 ++++- .../messages/admin-messages_en.properties | 16 +++++++------- .../resources/js/authz/authz-controller.js | 9 ++++++++ .../admin/resources/js/controllers/clients.js | 1 + .../resources/templates/kc-tabs-client.html | 2 +- 7 files changed, 43 insertions(+), 19 deletions(-) diff --git a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java index 887a461057..493637aeaa 100644 --- a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java +++ b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java @@ -43,7 +43,7 @@ public class AuthorizationClientExample { } private static void introspectRequestingPartyToken() { - // create a new instance based on the configuration define at keycloak-authz.json + // create a new instance based on the configuration defined in keycloak-authz.json AuthzClient authzClient = AuthzClient.create(); // query the server for a resource with a given name @@ -51,8 +51,9 @@ public class AuthorizationClientExample { .resource() .findByFilter("name=Default Resource"); - // obtian a Entitlement API Token in order to get access to the Entitlement API. - // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement + // obtain an Entitlement API Token in order to get access to the Entitlement API. + // this token is just an access token issued to a client on behalf of an user + // with a scope = kc_entitlement String eat = getEntitlementAPIToken(authzClient); // create an entitlement request @@ -63,7 +64,8 @@ public class AuthorizationClientExample { request.addPermission(permission); - // send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user + // send the entitlement request to the server in order to + // obtain an RPT with all permissions granted to the user EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request); String rpt = response.getRpt(); @@ -79,7 +81,7 @@ public class AuthorizationClientExample { } private static void createResource() { - // create a new instance based on the configuration define at keycloak-authz.json + // create a new instance based on the configuration defined in keycloak-authz.json AuthzClient authzClient = AuthzClient.create(); // create a new resource representation with the information we want @@ -111,8 +113,9 @@ public class AuthorizationClientExample { // create a new instance based on the configuration define at keycloak-authz.json AuthzClient authzClient = AuthzClient.create(); - // obtian a Entitlement API Token in order to get access to the Entitlement API. - // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement + // obtain an Entitlement API Token in order to get access to the Entitlement API. + // this token is just an access token issued to a client on behalf of an user + // with a scope = kc_entitlement String eat = getEntitlementAPIToken(authzClient); // create an entitlement request @@ -123,7 +126,8 @@ public class AuthorizationClientExample { request.addPermission(permission); - // send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user + // send the entitlement request to the server in order to obtain a RPT + // with all permissions granted to the user EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request); String rpt = response.getRpt(); @@ -133,7 +137,7 @@ public class AuthorizationClientExample { } private static void obtainAllEntitlements() { - // create a new instance based on the configuration define at keycloak-authz.json + // create a new instance based on the configuration defined in keycloak-authz.json AuthzClient authzClient = AuthzClient.create(); // obtian a Entitlement API Token in order to get access to the Entitlement API. diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java index 884f2e1abc..076f9af628 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java @@ -171,6 +171,13 @@ public class PolicyEvaluationService { collect.addAll(storeFactory.getResourceStore().findByScope(scope.getId()).stream().map(resource12 -> new ResourcePermission(resource12, asList(scope), resourceServer)).collect(Collectors.toList())); } + collect.addAll(storeFactory.getResourceStore().findByResourceServer(resourceServer.getId()).stream().map(new Function() { + @Override + public ResourcePermission apply(Resource resource) { + return new ResourcePermission(resource, resource.getScopes(), resourceServer); + } + }).collect(Collectors.toList())); + return collect.stream(); } }).collect(Collectors.toList()); diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java index d320a644f9..4f0c64a8a9 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java +++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java @@ -132,7 +132,10 @@ public class PolicyEvaluationResponse { scopes.add(scope); } if (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT)) { - result.getAllowedScopes().add(scope); + List allowedScopes = result.getAllowedScopes(); + if (!allowedScopes.contains(scope)) { + allowedScopes.add(scope); + } } } } diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 2ebed9f7c4..998eff8a94 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -986,13 +986,13 @@ authz-required=Required authz-import-config.tooltip=Import a JSON file containing authorization settings for this resource server. authz-policy-enforcement-mode=Policy Enforcement Mode -authz-policy-enforcement-mode.tooltip=The policy enforcement mode dictates how policies are enforced when evaluating authorization requests. 'Enforcing' means requests are denied by default even when there is no policy associated with a given resource. 'Permissive' means requests are allowed even when there is no policy associated with a given resource. 'Disabled' completely disables the evaluation of policies and allow access to any resource. +authz-policy-enforcement-mode.tooltip=The policy enforcement mode dictates how policies are enforced when evaluating authorization requests. 'Enforcing' means requests are denied by default even when there is no policy associated with a given resource. 'Permissive' means requests are allowed even when there is no policy associated with a given resource. 'Disabled' completely disables the evaluation of policies and allows access to any resource. authz-policy-enforcement-mode-enforcing=Enforcing authz-policy-enforcement-mode-permissive=Permissive authz-policy-enforcement-mode-disabled=Disabled authz-remote-resource-management=Remote Resource Management -authz-remote-resource-management.tooltip=Should resources be managed remotely by the resource server? If false, resources can only be managed from this admin console. +authz-remote-resource-management.tooltip=Should resources be managed remotely by the resource server? If false, resources can be managed only from this admin console. authz-export-settings=Export Settings authz-export-settings.tooltip=Export and download all authorization settings for this resource server. @@ -1035,7 +1035,7 @@ authz-policy-logic.tooltip=The logic dictates how the policy decision should be authz-policy-apply-policy=Apply Policy authz-policy-apply-policy.tooltip=Specifies all the policies that must be applied to the scopes defined by this policy or permission. authz-policy-decision-strategy=Decision Strategy -authz-policy-decision-strategy.tooltip=The decision strategy dictates how the policies associated with a given permission are evaluated and how a final decision is obtained. 'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive. 'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive. 'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative. +authz-policy-decision-strategy.tooltip=The decision strategy dictates how the policies associated with a given permission are evaluated and how a final decision is obtained. 'Affirmative' means that at least one policy must evaluate to a positive decision in order for the final decision to be also positive. 'Unanimous' means that all policies must evaluate to a positive decision in order for the final decision to be also positive. 'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative. authz-policy-decision-strategy-affirmative=Affirmative authz-policy-decision-strategy-unanimous=Unanimous authz-policy-decision-strategy-consensus=Consensus @@ -1059,15 +1059,15 @@ authz-policy-time-not-before.tooltip=Defines the time before which the policy MU authz-policy-time-not-on-after=Not On or After authz-policy-time-not-on-after.tooltip=Defines the time after which the policy MUST NOT be granted. Only granted if current date/time is before or equal to this value. authz-policy-time-day-month=Day of Month -authz-policy-time-day-month.tooltip=Defines the day of month before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the day of month before/equal which the policy MUST be granted. In this case, the policy would be granted if current day of month is between/equal the two values you provided. +authz-policy-time-day-month.tooltip=Defines the day of month which the policy MUST be granted. You can also provide a range by filling the second field. In this case, permission is granted only if current day of month is between or equal to the two values you provided. authz-policy-time-month=Month -authz-policy-time-month.tooltip=Defines the month before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the month before/equal which the policy MUST be granted. In this case, the policy would be granted if current month is between/equal the two values you provided. +authz-policy-time-month.tooltip=Defines the month which the policy MUST be granted. You can also provide a range by filling the second field. In this case, permission is granted only if current month is between or equal to the two values you provided. authz-policy-time-year=Year -authz-policy-time-year.tooltip=Defines the year before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the year before/equal which the policy MUST be granted. In this case, the policy would be granted if current year is between/equal the two values you provided. +authz-policy-time-year.tooltip=Defines the year which the policy MUST be granted. You can also provide a range by filling the second field. In this case, permission is granted only if current year is between or equal to the two values you provided. authz-policy-time-hour=Hour -authz-policy-time-hour.tooltip=Defines the hour before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the hour before/equal which the policy MUST be granted. In this case, the policy would be granted if current hour is between/equal the two values you provided. +authz-policy-time-hour.tooltip=Defines the hour which the policy MUST be granted. You can also provide a range by filling the second field. In this case, permission is granted only if current hour is between or equal to the two values you provided. authz-policy-time-minute=Minute -authz-policy-time-minute.tooltip=Defines the minute before/equal which policy MUST be granted. You can also provide a range period by filling the second field with the minute before/equal which the policy MUST be granted. In this case, the policy would be granted if current minute is between/equal the two values you provided. +authz-policy-time-minute.tooltip=Defines the minute which the policy MUST be granted. You can also provide a range by filling the second field. In this case, permission is granted only if current minute is between or equal to the two values you provided. # Authz Drools Policy Detail authz-add-drools-policy=Add Drools Policy 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 72b6c43c06..f4f87aa340 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 @@ -9,6 +9,7 @@ module.controller('ResourceServerCtrl', function($scope, realm, ResourceServer) module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $location, $upload, $modal, realm, ResourceServer, client, AuthzDialog, Notifications) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; ResourceServer.get({ realm : $route.current.params.realm, @@ -82,6 +83,7 @@ module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $l module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerResource, client) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.query = { realm: realm.realm, @@ -134,6 +136,7 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerResource, ResourceServerScope, AuthzDialog, Notifications) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) { $scope.scopes = data; @@ -265,6 +268,7 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope, client) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.query = { realm: realm.realm, @@ -317,6 +321,7 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerScope, AuthzDialog, Notifications) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; var $instance = this; @@ -426,6 +431,7 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.policyProviders = []; $scope.query = { @@ -498,6 +504,7 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.policyProviders = []; $scope.query = { @@ -1200,6 +1207,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.decisionStrategies = ['AFFIRMATIVE', 'UNANIMOUS', 'CONSENSUS']; $scope.logics = ['POSITIVE', 'NEGATIVE']; @@ -1365,6 +1373,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $location, realm, clients, roles, ResourceServer, client, ResourceServerResource, ResourceServerScope, User, Notifications) { $scope.realm = realm; $scope.client = client; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.clients = clients; $scope.roles = roles; $scope.authzRequest = {}; diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index 3bbd471758..c015dfa448 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -812,6 +812,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates, $scope.samlEncrypt = false; $scope.samlForcePostBinding = false; $scope.samlForceNameIdFormat = false; + $scope.showAuthorizationTab = client.authorizationServicesEnabled; function updateProperties() { if (!$scope.client.attributes) { diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html index a0d88c645d..96d9adaa52 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html @@ -19,7 +19,7 @@ {{:: 'scope' | translate}} {{:: 'scope.tooltip' | translate}} -
  • {{:: 'authz-authorization' | translate}}
  • +
  • {{:: 'authz-authorization' | translate}}
  • {{:: 'revocation' | translate}}
  • From 9d79a847c88aaf31e7154a329691339f0be2d17b Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 9 Sep 2016 09:47:32 +0200 Subject: [PATCH 23/53] KEYCLOAK-3547 Incorrect jpa-changelog-2.2.0.xml --- .../jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml index a9b60787ca..adc49fbf3d 100755 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.2.0.xml @@ -18,13 +18,12 @@ - + + - - From ccb9433e5cfaeb0e8cd1288a6f5a0bfa3aedf5a7 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 9 Sep 2016 11:31:31 +0200 Subject: [PATCH 24/53] KEYCLOAK-3535 Tweak logging for SSSD --- .../sssd/src/main/java/cx/ath/matthew/LibraryLoader.java | 9 --------- .../main/java/org/keycloak/federation/sssd/api/Sssd.java | 2 ++ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java index 095d214467..4088d46ebb 100644 --- a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java +++ b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java @@ -16,16 +16,11 @@ */ package cx.ath.matthew; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * @author Bruno Oliveira. */ public class LibraryLoader { - private static final Logger LOGGER = Logger.getLogger(LibraryLoader.class.getSimpleName()); - private static final String[] PATHS = {"/usr/lib/", "/usr/lib64/", "/usr/local/lib/", "/opt/local/lib/"}; private static final String LIBRARY_NAME = "libunix_dbus_java"; private static final String VERSION = "0.0.8"; @@ -40,12 +35,8 @@ public class LibraryLoader { } catch (UnsatisfiedLinkError e) { loadSucceeded = false; } - } - if (!loadSucceeded) LOGGER.log(Level.WARNING, "libunix_dbus_java not found\n" + - "Please, make sure you have the package libunix-dbus-java installed."); - return new LibraryLoader(); } diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java index fd9dc6752e..065eb98842 100644 --- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java @@ -123,6 +123,8 @@ public class Sssd { sssdAvailable = true; } connection.disconnect(); + } else { + logger.debugv("libunix_dbus_java not found. Federation provider will be disabled."); } } catch (DBusException e) { logger.error("Failed to check the status of SSSD", e); From 4780a3453a791f15ba62fe0f12934271346fe02d Mon Sep 17 00:00:00 2001 From: mhajas Date: Thu, 8 Sep 2016 15:59:26 +0200 Subject: [PATCH 25/53] Remove example-dist dependency --- .../adapter/AbstractExampleAdapterTest.java | 14 ++--- .../AbstractDemoExampleAdapterTest.java | 2 +- .../AbstractFuseExampleAdapterTest.java | 17 +++--- .../tests/other/adapters/pom.xml | 57 +++++++++++++++---- 4 files changed, 63 insertions(+), 27 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java index 9e7f4f1355..065477a407 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java @@ -17,11 +17,6 @@ package org.keycloak.testsuite.adapter; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.file.Paths; - import org.apache.commons.io.IOUtils; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; @@ -29,6 +24,11 @@ import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Assert; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Paths; + /** * * @author tkyjovsk @@ -50,11 +50,11 @@ public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest { Assert.assertNotNull("Property ${examples.version.suffix} must bet set.", EXAMPLES_VERSION_SUFFIX); System.out.println(EXAMPLES_VERSION_SUFFIX); + EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/example-realms"; + if (!System.getProperty("unpacked.container.folder.name","").isEmpty()) { - EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-examples"; TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-test-apps"; } else { - EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX; TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/Keycloak-" + EXAMPLES_VERSION_SUFFIX + "-test-apps"; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java index b1df6e0a34..4db4a3c46a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java @@ -98,7 +98,7 @@ public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdap @Override public void addAdapterTestRealms(List testRealms) { testRealms.add( - loadRealm(new File(EXAMPLES_HOME_DIR + "/preconfigured-demo/testrealm.json"))); + loadRealm(new File(EXAMPLES_HOME_DIR + "/demo-template/testrealm.json"))); } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java index cc137242fe..b0583fb70a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java @@ -17,22 +17,23 @@ package org.keycloak.testsuite.adapter.example; -import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest; -import java.io.File; -import java.util.List; import org.jboss.arquillian.graphene.page.Page; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import org.junit.Test; import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.testsuite.auth.page.account.Account; -import static org.keycloak.testsuite.util.IOUtil.loadRealm; -import static org.keycloak.testsuite.adapter.AbstractExampleAdapterTest.EXAMPLES_HOME_DIR; +import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest; import org.keycloak.testsuite.adapter.page.fuse.AdminInterface; import org.keycloak.testsuite.adapter.page.fuse.CustomerListing; import org.keycloak.testsuite.adapter.page.fuse.CustomerPortalFuseExample; import org.keycloak.testsuite.adapter.page.fuse.ProductPortalFuseExample; +import org.keycloak.testsuite.auth.page.account.Account; + +import java.io.File; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; +import static org.keycloak.testsuite.util.IOUtil.loadRealm; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf; import static org.keycloak.testsuite.util.WaitUtils.pause; diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index 15fc1c4836..300491d6cf 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -67,7 +67,8 @@ ${containers.home}/app-server-${app.server} true - + ${main.basedir}/examples + @@ -87,6 +88,23 @@ + + org.commonjava.maven.plugins + directory-maven-plugin + 0.1 + + + directories + + highest-basedir + + initialize + + main.basedir + + + + org.codehaus.mojo xml-maven-plugin @@ -311,20 +329,13 @@ - example-realms + test-apps-realms generate-test-resources unpack - - org.keycloak - keycloak-examples-dist - ${project.version} - zip - **/*realm.json,**/testsaml.json - org.keycloak.testsuite integration-arquillian-test-apps-dist @@ -336,9 +347,33 @@ ${examples.home} true - + - + + + maven-resources-plugin + 3.0.1 + + + example-realms + generate-test-resources + + copy-resources + + + ${examples.home}/example-realms + true + + + ${examples.basedir} + true + **/*realm.json,**/testsaml.json + + + + + + maven-surefire-plugin From fa02277e6e2e998786f51b6c67c32695be24e9e4 Mon Sep 17 00:00:00 2001 From: Vaclav Muzikar Date: Fri, 9 Sep 2016 12:23:08 +0200 Subject: [PATCH 26/53] KEYCLOAK-3552 Add some missing tests for OIDC Dynamic Profile --- .../testsuite/admin/realm/RealmTest.java | 27 ++++++++++++++----- .../oidc/OIDCWellKnownProviderTest.java | 11 ++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index 53a361516c..926d31d5ad 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -60,12 +60,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Stian Thorgersen @@ -563,6 +558,26 @@ public class RealmTest extends AbstractAdminTest { assertEquals(certificate, realm.toRepresentation().getCertificate()); } + @Test + public void rotateRealmKeys() { + RealmRepresentation realmRep = realm.toRepresentation(); + String publicKey = realmRep.getPublicKey(); + String cert = realmRep.getCertificate(); + assertNotNull(publicKey); + assertNotNull(cert); + + RealmRepresentation newRealmRep = new RealmRepresentation(); + newRealmRep.setRealm(REALM_NAME); + newRealmRep.setPublicKey("GENERATE"); + realm.update(newRealmRep); + + realmRep = realm.toRepresentation(); + assertNotNull(realmRep.getPublicKey()); + assertNotNull(realmRep.getCertificate()); + assertNotEquals(publicKey, realmRep.getPublicKey()); + assertNotEquals(cert, realmRep.getCertificate()); + } + @Test public void clearRealmCache() { RealmRepresentation realmRep = realm.toRepresentation(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java index 7fea65748b..4592eea076 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java @@ -36,6 +36,8 @@ import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentatio import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.representations.IDToken; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.services.clientregistration.ClientRegistrationService; +import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory; import org.keycloak.services.resources.RealmsResource; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; @@ -79,6 +81,15 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest { Assert.assertEquals(oidcConfig.getUserinfoEndpoint(), OIDCLoginProtocolService.userInfoUrl(UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT)).build("test").toString()); Assert.assertEquals(oidcConfig.getJwksUri(), oauth.getCertsUrl("test")); + String registrationUri = UriBuilder + .fromUri(OAuthClient.AUTH_SERVER_ROOT) + .path(RealmsResource.class) + .path(RealmsResource.class, "getClientsService") + .path(ClientRegistrationService.class, "provider") + .build("test", OIDCClientRegistrationProviderFactory.ID) + .toString(); + Assert.assertEquals(oidcConfig.getRegistrationEndpoint(), registrationUri); + // Support standard + implicit + hybrid flow assertContains(oidcConfig.getResponseTypesSupported(), OAuth2Constants.CODE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token"); assertContains(oidcConfig.getGrantTypesSupported(), OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT); From 59674e44bfea9f50c56884f9f8c7b2363cd0dbdc Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 9 Sep 2016 15:11:35 +0200 Subject: [PATCH 27/53] Update HackingOnKeycloak.md --- misc/HackingOnKeycloak.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/HackingOnKeycloak.md b/misc/HackingOnKeycloak.md index 590a301983..902d1edf97 100644 --- a/misc/HackingOnKeycloak.md +++ b/misc/HackingOnKeycloak.md @@ -59,5 +59,6 @@ Here's a quick check list for a good pull request (PR): * A JIRA associated with your PR (include the JIRA issue number in commit comment) * All tests in testsuite pass * Do a rebase on upstream master +* We only accept contributions to the master branch. The exception to this is if the fix is for the latest CR release and Final has not yet been released, in which case you can send the PR to both the corresponding branch and the master branch. -Once you're happy with your changes go to GitHub and create a PR. +Once you're happy with your changes go to GitHub and create a PR to the master branch. From 05ae84d5335ccb675bacc57b2f0c4323ee76e70c Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 9 Sep 2016 17:08:38 +0200 Subject: [PATCH 28/53] Added version for keycloak-fuse-adapter-dist --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index a2de58fb42..58e343968e 100755 --- a/pom.xml +++ b/pom.xml @@ -1073,6 +1073,18 @@ ${project.version} zip + + org.keycloak + keycloak-fuse-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-fuse-adapter-dist + ${project.version} + tar.gz + org.keycloak keycloak-wildfly-adapter-dist From 5eb36f4e0fc90b3ec9b4a5198253a0eb07322f42 Mon Sep 17 00:00:00 2001 From: filipelautert Date: Fri, 9 Sep 2016 16:34:59 -0300 Subject: [PATCH 29/53] [KEYCLOAK-3446] Add pt_BR localization for admin screens (#3173) * Add client.name as a second parameter to the title expressions in login template * Fixing tooltip. * pt_BR localization for admin screens. * Reverting login.ftl * Added all tooltip messages - even the ones not translated. Translated around 150 messages todas. * More translations. * Fixing wrong edit. --- .../messages/admin-messages_pt_BR.properties | 1094 +++++++++++++++++ .../admin/messages/messages_pt_BR.properties | 15 + 2 files changed, 1109 insertions(+) diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties index e69de29bb2..beacd2c483 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties @@ -0,0 +1,1094 @@ +#encoding: utf-8 +consoleTitle=Console de administração do Keycloak + +# Common messages +enabled=Habilitado +name=Nome +displayName=Nome de exibição +displayNameHtml=Nome de exibição HTML +save=Salvar +cancel=Cancelar +onText=Sim +offText=Não +client=Cliente +clients=Clientes +clear=Limpar +selectOne=Selecione um... + +true=Sim +false=Não + +endpoints=Endpoints + +# Realm settings +realm-detail.enabled.tooltip=Usuários e clientes somente podem acessar um Realm se ele estiver habilitado +realm-detail.oidc-endpoints.tooltip=Exibe a configuração dos endpoint do OpenID Connect +registrationAllowed=Cadastro de usuário +registrationAllowed.tooltip=Habilita/desabilita a página de cadastro. Um link para a página de cadastro também será exibido na tela de login. +registrationEmailAsUsername=Email como nome de usuário +registrationEmailAsUsername.tooltip=Se habilitado o campo 'nome de usuário' será ocultado no formulário de cadastro e o e-mail será usado como nome de usuário para o novo cadastro. +editUsernameAllowed=Editar nome de usuário +editUsernameAllowed.tooltip=Se habilitado, o campo nome de usuário é editável, senão será apenas leitura. +resetPasswordAllowed=Esqueci a senha +resetPasswordAllowed.tooltip=Exibe um link na página de login para o usuário clicar quando houver esquecido suas credenciais. +rememberMe=Lembrar me +rememberMe.tooltip=Exibe um checkbox na página de login para permitir ao usuário continuar logado entre restarts do browser até que a sessão expire. +verifyEmail=Verificar e-mail +verifyEmail.tooltip=Requer que o usuário verifique seu endereço de e-mail na primeira vez que efetuar login. +sslRequired=SSL requerido +sslRequired.option.all=todas requisições +sslRequired.option.external=requisições externas +sslRequired.option.none=nunca +sslRequired.tooltip=É necessário SSL? 'Nunca' significa que HTTPS não é requerido para nenhum endereço IP cliente. 'Requisições externas' significa que localhost e IPs privados podem acessar sem HTTPS. 'Todas requisições' significa que HTTPS é requerido para todos os endereços IPs. +publicKey=Chave pública +privateKey=Chave privada +gen-new-keys=Gerar novas chaves +certificate=Certificado +host=Host +smtp-host=Host SMTP +port=Porta +smtp-port=Porta SMTP (valor padrão: 25) +from=Remetente +sender-email-addr=Endereço de e-mail do remetente +enable-ssl=Habilitar SSL +enable-start-tls=Habilitar StartTLS +enable-auth=Habilitar autenticação +username=Usuário +login-username=Nome de usuário para login +password=Senha +login-password=Senha para login +login-theme=Tema de login +login-theme.tooltip=Selecione o tema para páginas de login, TOTP, grant, cadastro e recuperar senha. +account-theme=Tema para conta +account-theme.tooltip=Selecione o tema para as páginas de administração de conta do usuário. +admin-console-theme=Tema para console de administração +select-theme-admin-console=Selecione o tema para o console de administração. +email-theme=Tema de e-mail +select-theme-email=Selecione o tema para os e-mail que são enviados pelo servidor. +i18n-enabled=Habilitar internacionalização +supported-locales=Locais disponíveis +supported-locales.placeholder=Digite um local e pressione Enter +default-locale=Local padrão +realm-cache-clear=Realm Cache +realm-cache-clear.tooltip=Remove todas as entradas do cache de realm (isto irá remover as entradas para todos os realms) +user-cache-clear=Cache de usuário +user-cache-clear.tooltip=Remove todas as entradas do cache de usuário (isto irá remover as entradas de todos os realms) +revoke-refresh-token=Revogar Token de Atualização +revoke-refresh-token.tooltip=Se habilitado os tokens de atualização podem ser utilizados somente uma vez. Caso contrário os tokens de atualização não são revogados quando utilizados e podem ser utilizados várias vezes. +sso-session-idle=Sessão SSO inativa +sso-session-idle.tooltip=Tempo que uma sessão pode ficar inativa antes de expirar. Tokens e sessões de navegador são invalidados quando uma sessão é expirada. +seconds=Segundos +minutes=Minutos +hours=Horas +days=Dias +sso-session-max=Sessão SSO Máxima +sso-session-max.tooltip=Tempo máximo antes que uma sessão seja expirada. Tokens e sessões de navegador são invalidados quando uma sessão é expirada. +offline-session-idle=Sessão Offline Inativa +offline-session-idle.tooltip=Tempo que uma sessão offline pode ficar inativa antes de expirar. Você precisa utilizar um token de atualização offline pelo menos uma vez neste período, caso contrário a sessão offline será expirada. +access-token-lifespan=Duração do Token de Acesso +access-token-lifespan.tooltip=Tempo máximo antes que um token de acesso expire. Recomenda-se que este valor seja menor em relação ao tempo de inativação do inativação do SSO. +access-token-lifespan-for-implicit-flow=Duração do token de acesso para fluxos Implícitos +access-token-lifespan-for-implicit-flow.tooltip=Tempo máximo antes que um token de acesso emitido durante o Fluxo Implícito do OpenID Connect expire. Recomenda-se que este valor seja menor em relação ao tempo de inativação do SSO. Não há posibilidade de atualizar este token durante o fluxo implícito, sendo este o motivo de existir um tempo limite diferente para a 'Duração do Token de Acesso'. +client-login-timeout=Tempo limite para login do Cliente +client-login-timeout.tooltip=Tempo máximo que um cliente tem para finalizar o procolo do token de acesso. Normalmente deve ser 1 minuto. +login-timeout=Tempo máximo do Login +login-timeout.tooltip=Tempo máximo que um usuário tempo para completar o login. É recomendado que seja relativamente longo - 30 minutos ou mais. +login-action-timeout=Tempo limite da ação de Login +login-action-timeout.tooltip=Tempo máximo que um usuário tem para completar as ações relacionadas ao login como atualizar senhas ou configurar totp. É recomendado que seja relativamente longo - 5 minutos ou mais. +headers=Cabeçalhos +brute-force-detection=Detecção de ataque de Força Bruta +x-frame-options=X-Frame-Options +x-frame-options-tooltip=O valor padrão impede páginas de serem incluídas via non-origin iframes (clique no label para mais informações) +content-sec-policy=Content-Security-Policy +content-sec-policy-tooltip=O valor padrão impede páginas de serem incluídas via non-origin iframes (clique no label para mais informações) +content-type-options=X-Content-Type-Options +content-type-options-tooltip=O valor padrão impede Internet Explorer and Google Chrome de realizarem MIME-sniffing em uma resposta diferente do content-type declarado (clique no label para mais informações) +max-login-failures=Falhas de login +max-login-failures.tooltip=Quantas falhas de login antes que a espera seja habilitada. +wait-increment=Incremento de Espera +wait-increment.tooltip=Quando a quantidade de falhas for alcançada, quanto tempo o usuário deve aguardar antes de tentar novamente? +quick-login-check-millis=Verificação de Quick Login em Milli Seconds +quick-login-check-millis.tooltip=Se uma falha ocorre concorrentemente neste período, travar a conta do usuário. +min-quick-login-wait=Espera mínima após Quick Login +min-quick-login-wait.tooltip=Quanto tempo aguardar após uma falha de quick login. +max-wait=Espera máxima +max-wait.tooltip=Tempo máximo que um usuário deverá aguardar após uma falha de quick login. +failure-reset-time=Tempo para zerar falhas +failure-reset-time.tooltip=Quando o contador de falhas será resetado? +realm-tab-login=Login +realm-tab-keys=Chaves +realm-tab-email=E-mail +realm-tab-themes=Temas +realm-tab-cache=Cache +realm-tab-tokens=Tokens +realm-tab-client-initial-access=Tokens de Acesso inicial +realm-tab-security-defenses=Defesas +realm-tab-general=Geral +add-realm=Adicionar realm + +#Session settings +realm-sessions=Sessões do Realm +revocation=Revogação +logout-all=Deslogar todos +active-sessions=Sessões Ativas +sessions=Sessões +not-before=Não antes de +not-before.tooltip=Revogar qualquer token emitido antes desta data. +set-to-now=Definir como agora +push=Enviar +push.tooltip=Para cada cliente que possui uma URL de administrador, notificá-los da nova política de revogação. + +#Protocol Mapper +usermodel.prop.label=Propriedade +usermodel.attr.label=Atributo do usuário +userSession.modelNote.label=Nota da sessão de usuário +multivalued.label=Múltiplos valores +selectRole.label=Selecione o Role +tokenClaimName.label=Nome do Token Claim +jsonType.label=Tipo JSON do Claim +includeInIdToken.label=Adicionar ao token de ID +includeInAccessToken.label=Adicionar ao token de acesso +includeInUserInfo.label=Adicionar à informação do usuário +usermodel.clientRoleMapping.clientId.label=ID do cliente +usermodel.clientRoleMapping.rolePrefix.label=Prefixo para o role de Cliente +usermodel.realmRoleMapping.rolePrefix.label=Prefixo do Realm Role + +# client details +search.placeholder=Pesquisar... +create=Criar +import=Importar +client-id=ID do cliente +base-url=URL Base +actions=Ações +not-defined=Não definido +edit=Editar +delete=Excluir +no-results=Sem resultados +no-clients-available=Nenhum cliente disponível +add-client=Adicionar cliente +select-file=Selecionar arquivo +view-details=Ver detalhes +clear-import=Cancelar importação +client-id.tooltip=Especifica o ID referenciado em URI e tokens. Por exemplo 'meu-cliente'. Para SAML também representa o valor do emissor esperado dos authn requests +client.name.tooltip=Especifica o nome de exibição do cliente. Por exemplo 'Meu Cliente'. Também aceita chaves para valores localizados. Por exemplo: ${meu_cliente} +client.description.tooltip=Especifica a descrição do cliente. Por exemplo 'Meu cliente para TimeSheets'. Também aceita chaves para valores localizados. Por exemplo: ${meu_cliente_descricao} +client.enabled.tooltip=Clientes desabilitados não podem realizar login ou obter tokens de acesso. +consent-required=Consentimento exigido +consent-required.tooltip=Se habilitado os usuários devem consentir com o acesso ao cliente. +client-protocol=Protocolo cliente +client-protocol.tooltip='OpenID connect' permite aos Clientes verificarem a identidade do usuário final baseado na autenticação realizada por um servidor de Autorização. 'SAML' permite cenários de autenticação e autorização web-based incluindo cross-domain single sign-on (SSO) e utiliza tokens de segurança contendo assertions para trafegar informações. +access-type=Tipo de acesso +access-type.tooltip=Clientes 'Confidential' requerem um secret para iniciar o protocolo de login. Clientes 'Public' não necessitam de secret. Clientes 'Bearer-only' são web services que nunca iniciam um login. +standard-flow-enabled=Fluxo padrão habilitado +standard-flow-enabled.tooltip=Isto habilita a autenticação baseada em redirecionamento com código de autorização padrão do OpenID Connect. Em termos de especificações OpenID Connect ou OAuth2, isto habilita suporte ao 'Fluxo de Código de Autorização' para este cliente. +implicit-flow-enabled=Fluxo implícito habilitado +implicit-flow-enabled.tooltip=Isto habilita suporte a autenticação baseada em redirecionamento sem código de autorização. Em tempos de especificações OpenID Connect ou OAuth2, isto habilita suporte do 'Fluxo Implícito' para este cliente. +direct-access-grants-enabled=Grants de Acesso direto habilitado +service-accounts-enabled=Contas de serviço habilitadas +include-authnstatement=Incluir AuthnStatement +sign-documents=Assinar documentos +sign-assertions=Assinar assertions +signature-algorithm=Algoritmo de assinatura +canonicalization-method=Método de Canonicalization +encrypt-assertions=Encriptar Assertions +client-signature-required=Assinatura do cliente requerida +force-post-binding=Forçar Binding via POST +front-channel-logout=Front Channel Logout +force-name-id-format=Forçar formato do NameID +name-id-format=Formato do NameID +valid-redirect-uris=URIs de redirecionamento válidas +admin-url=URL do administrador +master-saml-processing-url=URL de processamento SAML principal +idp-sso-url-ref=Nome de URL para SSO iniciado via IDP +idp-sso-relay-state=Estado de relay para SSO iniciado via IDP +fine-saml-endpoint-conf=Configuração de endpoint para configuração fina do SAML +assertion-consumer-post-binding-url=URL para conexão post para o serviço consumidor de Assertions +assertion-consumer-redirect-binding-url=URL para conexão de redirecionamento do serviço consumidor de Assertions +logout-service-post-binding-url=URL de conexão POST para o serviço de logout +logout-service-post-binding-url.tooltip=URL de conexão POST para o serviço de logout +logout-service-redir-binding-url=URL de conexão para o redirecionamento do serviço de logout + +# client import +import-client=Importar cliente +format-option=Formato +import-file=Importar arquivo + +# client tabs +settings=Configurações +credentials=Credenciais +saml-keys=Chaves SAML +roles=Roles +mappers=Mapeamentos +scope=Escopo +offline-access=Acesso offline +installation=Instalação +service-account-roles=Roles de contas de serviço + +# client credentials +client-authenticator=Autenticador do cliente +no-client-certificate-configured=Nenhum certificado cliente configurado +gen-new-keys-and-cert=Gerar novas chaves e certificados +import-certificate=Importar certificado +gen-client-private-key=Gerar chave privada do cliente +generate-private-key=Gerar chave privada +archive-format=Formato do arquivo +key-alias=Alias da chave +key-password=Senha da chave +store-password=Salvar senha +generate-and-download=Gerar e fazer download +client-certificate-import=Importar certificado do cliente +import-client-certificate=Importar certificado do cliente +secret=Segredo +regenerate-secret=Recriar segredo +registrationAccessToken=Token de acesso para registro +registrationAccessToken.regenerate=Regerar token de acesso para registro +add-role=Adicionar Role +role-name=Nome do Role +composite=Composto +description=Descrição +no-client-roles-available=Nenhum role de cliente disponível +scope-param-required=Parâmetro de escopo requerido +composite-roles=Roles compostos +realm-roles=Roles do Realm +available-roles=Roles disponíveis +add-selected=Adicionar selecionados +associated-roles=Roles associados +remove-selected=Remover selecionados +client-roles=Roles de clientes +select-client-to-view-roles=Selecione o cliente para ver os roles do cliente +add-builtin=Adicionar Builtin +category=Categoria +type=Tipo +no-mappers-available=Nenhum mapeamento disponível +add-builtin-protocol-mappers=Adicionar mapeamentos de protocolo Builtin +add-builtin-protocol-mapper=Adicionar mapeamentos de protocolo Builtin + +scope-mappings=Mapeamentos do Escopo +full-scope-allowed=Permitir Escopo completo +assigned-roles=Roles associados +effective-roles=Roles efetivos +basic-configuration=Configuração básica +node-reregistration-timeout=Tempo limite para re-registro de nó +registered-cluster-nodes=Nós de cluster registrados +register-node-manually=Registrar nó manualmente +test-cluster-availability=Testar disponibilidade do cluster +last-registration=Último registro +node-host=Host +no-registered-cluster-nodes=Nenhum nó registrado disponível +cluster-nodes=Nós do cluster +add-node=Adicionar nó +show-sessions=Exibir sessões +user=Usuário +from-ip=Do IP +session-start=Início da sessão +first-page=Primeira página +previous-page=Página anterior +next-page=Próxima página +select-a-format=Selecione um formato +download=Download +offline-tokens=Tokens offline +show-offline-tokens=Exibir tokens offline +token-issued=Token emitido +last-access=Último acesso +last-refresh=Último refresh +key-export=Exportar chave +key-import=Importar chave +export-saml-key=Exportar chave SAML +import-saml-key=Importar chave SAML +realm-certificate-alias=Alias do certificado do Realm +signing-key=Chave de assinatura +saml-signing-key=Chave de assinatura SAML +private-key=Chave privada +generate-new-keys=Gerar novas chaves +export=Exportar +encryption-key=Chave de encriptação +service-accounts=Contas de serviço +service-account-is-not-enabled-for=Contas de serviço não estão habilitadas para {{client}} +create-protocol-mappers=Criar mapeamentos de protocolo +create-protocol-mapper=Criar mapeamento de protocolo +protocol=Protocolo +protocol.tooltip=Protocolo... +id=ID +mapper.name.tooltip=Nome do mapeamento +consent-text=Texto para consentimento +mapper-type=Tipo de mapeamento +# realm identity providers +identity-providers=Provedores de identificação +table-of-identity-providers=Tabela de provedores de identidade +add-provider.placeholder=Adicionar provedor... +provider=Provedor +first-broker-login-flow=Fluxo do primeiro login +post-broker-login-flow=Fluxo pós login +redirect-uri=URI de redirecionamento +alias=Alias +authenticate-by-default=Autenticar por padrão +store-tokens=Salvar Tokens +stored-tokens-readable=Leitura de tokens salvos +trust-email=Confiar no e-mail recebido +gui-order=Ordem na tela +gui-order.tooltip=Número definindo a ordem do provedor na GUI (ex na página de Login). +openid-connect-config=Configuração OpenID Connect +authorization-url=URL de autorização +token-url=URL do Token +logout-url=URL de logout +backchannel-logout=Backchannel Logout +user-info-url=URL de informações do usuário +client-secret=Secret do Cliente +show-secret=Exibir secret +hide-secret=Esconder secret +issuer=Emissor +default-scopes=Escopos padrão +prompt=Prompt +unspecified.option=Não especificado +none.option=Nenhum +consent.option=Consentimento +login.option=Login +select-account.option=select_account +validate-signatures=Validar assinaturas +validating-public-key=Chave pública para validação +import-external-idp-config=Importar configuração de IDP externo +import-from-url=Importar de URL +import-from-file=Importar de arquivo +saml-config=Configuração SAML +single-signon-service-url=URL de serviço do Single Sign On +single-logout-service-url=URL de serviço de Single Logout +nameid-policy-format=Política de formato NameID +http-post-binding-response=Responder com HTTP-POST +http-post-binding-for-authn-request=Utilizar HTTP-POST binding para AuthnRequest +want-authn-requests-signed=Esperar AuthnRequests assinados +force-authentication=Forçar autenticação +validate-signature=Validar assinatura +validating-x509-certificate=Validar certificados X509 +key=Chave + +# User federation +sync-ldap-roles-to-keycloak=Sincronizar os roles do LDAP para o Keycloak +sync-keycloak-roles-to-ldap=Sincronizar os roles do Keycloak para o LDAP +sync-ldap-groups-to-keycloak=Sincronizar os grupos do LDAP para o Keycloak +sync-keycloak-groups-to-ldap=Sincronizar os grupos do Keycloak para o LDAP + +realms=Realms +realm=Realm + +identity-provider-mappers=Mapeamentos de provedores de identificação +create-identity-provider-mapper=Criar mapeamento de provedores de identificação +add-identity-provider-mapper=Adicionar mapeamento de provedor de identificação + +expires=Expira em +expiration=Duração +expiration.tooltip=Especifica por quanto tempo o token deve ser válido +count=Quantidade +count.tooltip=Especifica quantos clientes podem ser criados usando o token +remainingCount=Quantidade restante +created=Criado em +back=Voltar +initial-access-tokens=Tokens de acesso inicial +add-initial-access-tokens=Adicionar token de acesso inicial +initial-access-token=Token de acesso inicial +initial-access.copyPaste.tooltip=Copie/cole o token de acesso inicial antes de sair desta página pois não é possível recuperá-lo depois +continue=Continuar +initial-access-token.confirm.title=Copiar o token de acesso inicial +initial-access-token.confirm.text=Por favor copie e cole o token de acesso inicial antes de confirmar pois não é possível recuperá-lo depois + +client-templates=Modelos de cliente + +groups=Grupos + +default-roles=Roles padrão +no-realm-roles-available=Nenhum role de realm disponível + +users=Usuários +realm-default-roles=Roles padrão do Realm +client-default-roles=Roles padrão do Cliente +partial-import=Importação parcial + +file=Arquivo +exported-json-file=Arquivo json exportado +import-from-realm=Importar de realm +import-users=Importar usuários +import-groups=Importar grupos +import-clients=Importar clientes +import-identity-providers=Importar provedores de identificação +import-realm-roles=Importar roles do realm +import-client-roles=Importar roles de cliente +if-resource-exists=Se um recurso já existir +fail=Falhar +skip=Pular +overwrite=Sobrescrever + +action=Ações +role-selector=Seletor de roles + +select-a-role=Selecione um role +select-realm-role=Selecione um role de realm +select-client-role=Selecione um role de cliente + +client-template=Modelos de Cliente +client-saml-endpoint=Cliente SAML Endpoint +add-client-template=Adicionar modelo de cliente + +manage=Administração +authentication=Autenticação +user-federation=Federação de usuários +events=Eventos +realm-settings=Configurações do Realm +configure=Configuração +select-realm=Selecione um realm +add=Adicionar + + +add-user-federation-provider=Adicionar provedor de federação de usuários +required-settings=Configurações obrigatórias +provider-id=ID do provedor +console-display-name=Nome de exibição no console +priority=Prioridade +sync-settings=Configurações de sincronização +periodic-full-sync=Syncronização completa periódica +full-sync-period=Período +periodic-changed-users-sync=Sincronização periódica de usuários alterados +changed-users-sync-period=Período +synchronize-changed-users=Sincronizar usuários alterados +synchronize-all-users=Sincronizar todos os usuários +kerberos-realm=Realm do Kerberos +server-principal=Principal do servidor +keytab=KeyTab +debug=Debug +allow-password-authentication=Permitir autenticação via senha +edit-mode=Modo de edição +update-profile-first-login=Atualizar Profile no primeiro login +sync-registrations=Sincronizar contas +vendor=Vendor +username-ldap-attribute=Atributo LDAP para Username +ldap-attribute-name-for-username=Atributo LDAP para Username +rdn-ldap-attribute=Atributo LDAP para RDN +ldap-attribute-name-for-user-rdn=Atributo LDAP para RDN +uuid-ldap-attribute=Atributo LDAP para UUID +ldap-attribute-name-for-uuid=Atributo LDAP para UUID +user-object-classes=Classes do objeto User + +ldap-connection-url=URL de conexão ao LDAP +ldap-users-dn=DN dos usuários no LDAP +ldap-bind-dn=DN para bind no LDAP +ldap-bind-credentials=Credenciais para conectar ao LDAP +ldap-filter=Filtro do LDAP + +connection-url=URL de conexão +test-connection=Testar conexão +users-dn=Users DN +authentication-type=Tipo de autenticação +bind-dn=Bind DN +bind-credential=Senha para conexão +test-authentication=Testar autenticação +custom-user-ldap-filter=Filtro de usuários LDAP customizado +search-scope=Escopo de pesquisa +use-truststore-spi=Utilizar Truststore SPI +connection-pooling=Pooling de conexões +kerberos-integration=Integração com Kerberos +allow-kerberos-authentication=Permitir autenticação Kerberos +use-kerberos-for-password-authentication=Utilizar Kerberos para autenticação via senha +batch-size=Tamanho do lote +user-federation-mappers=Mapeamentos de federação de usuário +create-user-federation-mapper=Criar mapeamento de federação de usuário +add-user-federation-mapper=Adicionar mapeamento de federação de usuário +provider-name=Nome do provedor +no-user-federation-providers-configured=Nenhum federação de usuários configurada. +add-identity-provider=Adicionar provedor de identificação +add-identity-provider-link=adicionar link para provedor de identificação +identity-provider=Provedor de identificação +identity-provider-user-id=ID de usuário do provedor de identificação +identity-provider-username=Nome de usuário do provedor de identificação +pagination=Paginação + +browser-flow=Fluxo de browser +registration-flow=Fluxo de registro +direct-grant-flow=Fluxo de Direct Grant +reset-credentials=Reiniciar credenciais +client-authentication=Autenticação do cliente +new=Novo +copy=Copiar +add-execution=Adicionar execução +add-flow=Adicionar fluxo +auth-type=Tipo +requirement=Condição +config=Configuração +no-executions-available=Nenhuma execução disponível +authentication-flows=Fluxos de autenticação +create-authenticator-config=Criar configuração de autenticação +otp-type=Tipo OTP +time-based=Baseado em tempo +counter-based=Baseado em contador +otp-hash-algorithm=Algoritmo de hash OTP +number-of-digits=Quantidade de dígitos +look-ahead-window=Look Ahead Window +initial-counter=Contador inicial +otp-token-period=Período de token OTP +table-of-password-policies=Tabela de política de senhas +add-policy.placeholder=Adicionar política... +policy-type=Tipo da política +policy-value=Valor da política +admin-events=Eventos de adminstração +login-events=Eventos de login +filter=Filtro +update=Atualizar +reset=Reiniciar +operation-types=Tipos de operações +select-operations.placeholder=Selecionar operações... +resource-path=Path do recurso +date-(from)=Data (De) +date-(to)=Data (Até) +authentication-details=Detalhes para autenticação +ip-address=Endereço IP +time=Tempo +operation-type=Tipo de operação +auth=Autenticação +representation=Representação +register=Registro +required-action=Ação requerida +default-action=Ação padrão +no-required-actions-configured=Nenhuma ação requerida configurada +defaults-to-id=ID é o padrão +flows=Fluxos +bindings=Ligações +required-actions=Ações requeridas +password-policy=Política de senha +otp-policy=Política OTP +user-groups=Grupos de usuário +default-groups=Grupos Padrão +cut=Recortar +paste=Colar + +create-group=Criar grupo +create-authenticator-execution=Criar execução de autenticação +create-form-action-execution=Criar execução de ação de formulário +create-top-level-form=Criar formulário de nível superior +top-level-flow-type=Tipo do fluxo de nível superior +flow.generic=genérico +flow.client=cliente +create-execution-flow=Criar fluxo de execução +flow-type=Flow Type +flow.form.type=formulário +flow.generic.type=genérico +form-provider=Provedor de formulário +select-a-type.placeholder=selecione um tipo +available-groups=Grupos disponíveis +value=Valor +table-of-group-members=Tabela de membros do grupo +last-name=Sobrenome +first-name=Primeiro nome +email=E-mail +toggle-navigation=Alternar navegação +manage-account=Administrar a conta +sign-out=Sign Out +server-info=Informação do servidor +resource-not-found=Recurso não encontrado... +resource-not-found.instruction=Não foi possível encontrar o recurso solicitado. Por favor verifique se a URL solicitada está correta. +go-to-the-home-page=Ir para a página inicial » +page-not-found=Página não encontrada... +page-not-found.instruction=Não foi possível encontrar a página solicitada. Por favor verifique se a URL solicitada está correta. +select-event-types.placeholder=Selecione os tipos de eventos... +select-an-action.placeholder=Selecione uma ação... +admin-events-settings=Configuração de eventos de administração +save-events=Salvar eventos +include-representation=Incluir representação +server-version=Versão do servidor +info=Informações +providers=Provedores +server-time=Hora do servidor +server-uptime=Uptime do servidor +memory=Memória +total-memory=Memória total +free-memory=Memória livre +used-memory=Memória utilizada +system=Sistema +current-working-directory=Diretório de trabalho atual +java-version=Versão do Java +java-vendor=Java Vendor +java-runtime=Java Runtime +java-vm=Java VM +java-vm-version=Versão da Java VM +java-home=Java Home +user-name=Usuário +user-timezone=Zona horária do usuário +user-locale=Locale do usuário +system-encoding=Enconding do sistema +operating-system=Sistema operacional +os-architecture=Arquitetura do OS +spi=SPI +granted-roles=Roles concedidos +granted-protocol-mappers=Protocol Mappers concedidos +additional-grants=Concessões adicionais +revoke=Revogar +new-password=Nova senha +password-confirmation=Confirmação de senha +reset-password=Reiniciar senha +remove-totp=Remover TOTP +reset-actions=Ações para reiniciar +reset-actions-email=Ações para reiniciar e-mail +send-email=Enviar e-mail +add-user=Adicionar usuário +created-at=Criado em +user-enabled=Usuário ativo +user-temporarily-locked=Usuário temporariamente desativado +unlock-user=Liberar usuário +federation-link=Link para federação +email-verified=E-mail verificado +required-user-actions=Ações necessárias do usuário +locale=Locale +select-one.placeholder=Selecione um... +impersonate=Personificar +impersonate-user=Personificar usuário +identity-provider-alias=Alias do Provedor de Identificação +provider-user-id=Provider User ID +provider-username=Provider Username +no-identity-provider-links-available=Nenhum link para provedor de identificação disponível +group-membership=Grupos associados +leave=Sair +table-of-realm-users=Tabela de usuários do Realm +view-all-users=Exibir todos os usuários +unlock-users=Liberar usuários +no-users-available=Nenhum usuário disponível +users.instruction=Por favor faça uma pesquisa, ou clique em Exibir todos os usuários +consents=Consentimentos +started=Iniciado +logout-all-sessions=Logout todas as sessões +logout=Logout +new-name=Novo nome +ok=Ok +attributes=Atributos +role-mappings=Mapeamento de roles +members=Membros +details=Detalhes +identity-provider-links=Links de provedores de identificação. +register-required-action=Registrar ação necessária +gender=Gênero +address=Endereço +phone=Telefone +profile-url=URL do perfil +picture-url=URL da foto +website=Website +import-keys-and-cert=Importar chave e certificado +upload-keys=Carregar chaves +download-keys-and-cert=Download chave e certificado +no-value-assigned.placeholder=Nenhum valor associado +remove=Remover +no-group-members=Nenhum membro +temporary=Temporária +join=Participar +event-type=Tipo de evento +events-config=Configurar eventos +event-listeners=Listeners de eventos +login-events-settings=Configuração de eventos de login +clear-events=Limpar eventos +saved-types=Tipos salvos +clear-admin-events=Limpar eventos administrativos +clear-changes=Cancelar mudanças +error=Erro + +# Authz +# Authz Common +authz-authorization=Autorização +authz-owner=Proprietário +authz-uri=URI +authz-scopes=Escopos +authz-resource=Recurso +authz-resource-type=Tipo de recurso +authz-resources=Recursos +authz-scope=Escopo +authz-authz-scopes=Autorização de escopos +authz-policies=Políticas +authz-permissions=Permissões +authz-evaluate=Avaliar +authz-icon-uri=URI do ícone +authz-select-scope=Selecione um escopo +authz-select-resource=Selecione um recurso +authz-associated-policies=Políticas associadas +authz-any-resource=Qualquer recurso +authz-any-scope=Qualquer escopo +authz-any-role=Qualquer role +authz-policy-evaluation=Avaliação da política +authz-select-client=Selecione um cliente +authz-select-user=Selecione um usuário +authz-entitlements=Direitos +authz-no-resources=Nenhum recurso +authz-result=Resultado +authz-authorization-services-enabled=Autorização habilitada +authz-required=Obrigatório + +# Authz Settings + +authz-policy-enforcement-mode=Modo de execução da política +authz-policy-enforcement-mode-enforcing=Restritiva +authz-policy-enforcement-mode-permissive=Permissiva +authz-policy-enforcement-mode-disabled=Desabilitada + +authz-remote-resource-management=Administração remota de recursos + +authz-export-settings=Exportar configurações + +# Authz Resource List +authz-no-resources-available=Nenhum recurso disponível. +authz-no-scopes-assigned=Nenhum escopo associado. +authz-no-type-defined=Nenhum tipo definido. +authz-no-permission-assigned=Nenhuma permissão associada. +authz-no-policy-assigned=Nenhuma política associada. +authz-create-permission=Criar permissão + +# Authz Resource Detail +authz-add-resource=Adicionar recurso + +# Authz Scope List +authz-add-scope=Adicionar escopo +authz-no-scopes-available=Nenhum escopo disponível. + +# Authz Scope Detail + +# Authz Policy List +authz-all-types=Todos os tipos +authz-create-policy=Criar política +authz-no-policies-available=Nenhuma política disponível + +# Authz Policy Detail +authz-policy-logic=Lógica +authz-policy-logic-positive=Positiva +authz-policy-logic-negative=Negativa +authz-policy-apply-policy=Aplicar política +authz-policy-decision-strategy=Estratégia de decisão +authz-policy-decision-strategy-affirmative=Afirmativa +authz-policy-decision-strategy-unanimous=Unânime +authz-policy-decision-strategy-consensus=Consensual +authz-select-a-policy=Selecionar uma política + +# Authz Role Policy Detail +authz-add-role-policy=Adicionar política de Role +authz-no-roles-assigned=Nenhum role associado + +# Authz User Policy Detail +authz-add-user-policy=Adicionar política de usuário +authz-no-users-assigned=Nenhum usuário associado + +# Authz Time Policy Detail +authz-add-time-policy=Adicionar política de tempo +authz-policy-time-not-on-after=Não em ou depois + +# Authz Drools Policy Detail +authz-add-drools-policy=Adicionar política Drools +authz-policy-drools-maven-artifact-resolve=Resolver +authz-policy-drools-maven-artifact=Artefato maven de política +authz-policy-drools-module=Módulo +authz-policy-drools-session=Sessão +authz-policy-drools-update-period=Atualizar período + +# Authz JS Policy Detail +authz-add-js-policy=Adicionar política Javascript +authz-policy-js-code=Código + + +# Authz Aggregated Policy Detail +authz-aggregated=Agregado +authz-add-aggregated-policy=Adicionar política agregada + +# Authz Permission List +authz-no-permissions-available=Nenhuma permissão disponível + +# Authz Permission Detail + +# Authz Resource Permission Detail +authz-add-resource-permission=Adicionar permissão para recurso +authz-permission-resource-apply-to-resource-type=Aplicar ao tipo de recurso + +# Authz Scope Permission Detail +authz-add-scope-permission=Adicionar permissão de escopo + +# Authz Evaluation +authz-evaluation-identity-information=Informação de identidade +authz-evaluation-new=Nova avaliação +authz-evaluation-re-evaluate=Re-avaliar +authz-evaluation-previous=Avaliação anterior +authz-evaluation-contextual-info=Informação contextual +authz-evaluation-contextual-attributes=Atributos contextuais +authz-evaluation-evaluate=Avaliar +authz-evaluation-any-resource-with-scopes=Qualquer recurso com escopo(s) +authz-evaluation-no-result=Não foi possível obter nenhum resultado para o pedido de autorização provida. Verifique os recurso(s) ou escopo(s) providos estão associados com alguma política. +authz-evaluation-no-policies-resource=Nenhma política foi encontrada para este recurso. +authz-evaluation-authorization-data=Resposta +authz-show-authorization-data=Exibir dados da autorização + +usermodel.prop.tooltip=Nome do método da propriedade na interface UserModel. Por exemplo, o valor 'email' iria referenciar o método UserModel.getEmail() . +usermodel.attr.tooltip=Nome do atributo do usuário que é uma chave de atributo no mapa UserModel.attribute. +userSession.modelNote.tooltip=Nome da nota de sessão do usuário salva no mapa UserSessionModel.note. +multivalued.tooltip=Indica se um atributo suporta múltiplos valores. Se verdadeiro, então a lista de todos os valores desse atributo será definida como o claim. Se falso, então apenas o primeiro valor será utilizado. +selectRole.tooltip=Entre com o role na caixa à esquerda, ou clique neste botão para navegar e selecionar o role desejado. +tokenClaimName.tooltip=Nome do claim para inserir no token. Pode ser um nome completo (fully qualified) como 'address.street'. Neste caso, um objeto json aninhado será criado. +jsonType.tooltip=Tipo JSON que deve ser utilizado para popular o claim json no token. Os valores válidos são Long, int boolean e String. +includeInIdToken.tooltip=O claim deve ser adicionado ao token de ID? +includeInAccessToken.tooltip=O claim deve ser adicionado ao token de acesso? +includeInUserInfo.tooltip=O claim deve ser adicionado à informação do usuário? +usermodel.clientRoleMapping.clientId.tooltip=ID do cliente para mapeamentos de roles +usermodel.clientRoleMapping.rolePrefix.tooltip=Um prefixo para cada role do cliente (opcional) +usermodel.realmRoleMapping.rolePrefix.tooltip=Um prefixo para cada Realm Role (opcional). +clients.tooltip=Os clientes são aplicativos de browser e serviços web confiáveis em um realm. Esses clientes podem solicitar login. Você também pode definir roles específicos do cliente. +authz-policy-role-clients.tooltip= Selecione um cliente a fim de filtrar os roles de cliente que podem ser aplicados a esta política. +direct-access-grants-enabled.tooltip=Habilita o suporte para concessões de acesso direto (Direct Access Grants), o que significa que o cliente tem acesso ao nome de usuário/senha e negocia diretamente com o servidor Keycloak pelo token de acesso. Em termos de especificações OAuth2, habilita suporte de "Resource Owner Password Credentials Grant" para este cliente. +service-accounts-enabled.tooltip=Permite autenticar este cliente no Keycloak e recuperar tokens de acesso dedicados para este cliente. Em termos da especificações OAuth2, habilita suporte para 'Client Credentials Grants' para este cliente. +include-authnstatement.tooltip=Deve ser adicionado um statement especificando o método e timestamp nas respostas de login? +sign-documents.tooltip=Devem os documentos SAML serem assinados pelo realm? +sign-assertions.tooltip=Devem as asserções dentro dos documentos SAML serem assinadas? Esta configuração não é necessária se o documento já está sendo assinado. +signature-algorithm.tooltip=O algoritmo de assinatura a ser utilizado para assinar documentos. +canonicalization-method.tooltip=Canonicalization Method para assinaturas XML. +encrypt-assertions.tooltip=Devem as asserções SAML serem encriptadas com a chave pública do cliente usando AES? +client-signature-required.tooltip=O cliente irá assinar os pedidos e respostas saml? E eles devem ser validados? +force-post-binding.tooltip=Sempre utilizar POST para respostas. +front-channel-logout.tooltip=Quando marcado, o logout requer um redirecionamento do browser para o cliente. Caso contrário o servidor executo uma invocação em background para o logout. +force-name-id-format.tooltip=Ignora o NameID de assunto solicitado e utiliza o configurado no console de administração. +name-id-format.tooltip=O formato de Name ID para utilizar como assunto. +root-url.tooltip=URL raiz adicionada à URLs relativas +valid-redirect-uris.tooltip=Padrão de URI válido para onde um navegador pode redirecionar depois de um login bem-sucedido ou sair. Wildcards simples são permitidos, por exemplo 'http://example.com/*'. Caminhos relativos podem ser especificados também, ex: /my/relative/path/*. Caminhos relativos são relativos à URL raiz do cliente, ou se nenhum for especificado a URL raiz do servidor é usado. Para SAML, é necessário definir padrões de URI válidos se você está contando com a URL do serviço consumidor incorporada com a solicitação de login. +base-url.tooltip=URL padrão para utilizar quando o servidor de autenticação necessita redirecionar ou linkar para o cliente. +admin-url.tooltip=URL para a inteface administrativa do cliente. Defina este valor se o cliente suporta a API do adaptador REST. Esta API rest permite que o servidor de autenticação envie políticas de revogação e outras tarefas administrativas. Geralmente este valor é definido apontando para a URL base do cliente. +master-saml-processing-url.tooltip=Se configurado, esta URL será utilizada para todos os bindings do "SP's Assertion Consumer" e "Single Logout Services". Ela pode ser sobreescriva idnvidualmente para cada ligação e serviço na Configuração Detalhada do Endpoint SAML. +idp-sso-url-ref.tooltip=Nome do fragmento URL para referenciar o cliente quando você deseja um SSO iniciado por IDP. Deixar este campo vazio irá desabilitar SSO iniciado por IDP. A URL que você irá referenciar do seu browser será: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name} +idp-sso-relay-state.tooltip=O estado de Relay que você deseja enviar com um pedido SAML quando você deseja realizar SSO iniciado por IDP. +web-origins.tooltip=Permitir origens CORS. Para permitir todas as URIs de redirecionamento de origem válidas adicionar '+'. Para permitir todas as origens adicionar '*'. +fine-saml-endpoint-conf.tooltip=Expanda esta sessão para configurar URLs específics para 'Assertion Consumer' e 'Single Logout Service'. +assertion-consumer-post-binding-url.tooltip=URL de ligação SAML via post para as asserções de consumidor de serviços do cliente (respostas de login). Você pode deixar este campo em branco se você não tiver uma URL para esta ligação. +assertion-consumer-redirect-binding-url.tooltip=URL de ligação SAML de redirecionamento para as asserções de consumidor de serviços do cliente (respostas de login). Você pode deixar este campo em branco se você não tiver uma URL para esta ligação. +logout-service-binding-post-url.tooltip=URL de ligação SAML via post para o serviço de logout único do cliente. Voce pode deixar este campo em branco se estiver usando uma ligação diferente. +logout-service-redir-binding-url.tooltip=URL de ligação SAML de redirecionamento para o serviço de logout único do cliente. Voce pode deixar este campo em branco s e estiver usando uma ligação diferente. +mappers.tooltip=Mapeamentos de protocolo executam transformações em tokens e documentos. Eles podem realizar coisas como mapear dados de usuários para claims de protocolo, ou apenas transformar qualquer solicitação entre o cliente e o servidor de autenticação. +scope.tooltip=Escopos de mapeamento permitem que você restrinja quais mapeamentos de roles de usuário são inclusos nos tokens de acesso solicitado pelo cliente. +ldap.search-scope.tooltip=Para um nível nós pesquisamos somente os usuários nos DNs especificados pelo campo User DNs. Para subtree, nós pesquisamos na sub-árvore completa. Verifique a documentação do LDAP para mais detalhes. +authz-permission-scope-scope.tooltip=Define que esta permissões deve ser aplicada para um ou mais escopos. +sessions.tooltip=Exibir as sessões ativas para este cliente. Permite que você veja quais usuários estão ativos e quando eles logaram. +active-sessions.tooltip=Total de sessões de usuário ativas para este cliente. +show-sessions.tooltip=Atenção, esta é uma operação potencialmente cara dependendo do número de sessões ativas. +offline-access.tooltip=Exibe as sessões offline para este cliente. Permite que você veja quantos usuários obtém tokens offline e quando eles os obtiveram. PAra revogar todos os tokens do cliente, vá para a aba Revogações e defina o valor do campo 'não antes de' para 'agora'. +installation.tooltip=Ferramenta de auxílio para gerar vários formatos de adaptadores de cliente que você poderá fazer download depois ou copiar e colar para configurar seus cliente. +service-account-roles.tooltip=Permite que você autentique mapeamentos de roles para as contas de serviço dedicadas à este cliente. +client-authenticator.tooltip=Autenticador de Cliente usado para autenticar este cliente ao servidor Keycloak +certificate.tooltip=Certificado do cliente para validar JWT emitidos pelo cliente e assinados pela chave privada do cliente da sua keystore. +validating-x509-certificate.tooltip=O certificado em formato PEM que deve ser usado para verificar assinaturas. +archive-format.tooltip=Keystore Java ou arquivo em formato PKCS12. +key-alias.tooltip=Alias do arquivo para sua chave privada e certificado. +key-password.tooltip=Senha para acessar a chave privada no certificado. +store-password.tooltip=Senha para acessar o arquivo em si. +jwt-import.key-alias.tooltip=Alias do arquivo para o seu certificado. +registrationAccessToken.tooltip=O token de acesso para registro provê acesso aos cliente para o serviço de registro cliente. +scope-param-required.tooltip=Este role somente será concedido se os parâmetros de escopo com os nomes dos roles forem utilizados durante a autorização/solicitação de token. +composite-roles.tooltip=Quando este role é associado/removido de um usuário, qualquer role associado com ele também será adicionado/removido implicitamente. +composite.associated-realm-roles.tooltip=Roles de nível de realm associados com este role composto. +composite.available-realm-roles.tooltip=Roles de nível de realm disponíveis para este role composto. +available-roles.tooltip=Roles para este cliente que você pode associar a este role composto. +scope.available-roles.tooltip=Roles do Realm que podem ser associados a este escopo. +service-account.available-roles.tooltip=Roles do Realm que podem ser associados a contas de serviço. +client.associated-roles.tooltip=Roles do Cliente associados a este role composto. +full-scope-allowed.tooltip=Permite a você desabilitar todas as restrições. +assigned-roles.tooltip=Roles do Realm associados ao escopo. +service-account.assigned-roles.tooltip=Roles do Realm associados a conta de serviço. +group.assigned-roles.tooltip=Roles do Realm mapeados ao grupo. +realm.effective-roles.tooltip=Roles do Realm associados que podem ter sido herdados de um role composto. +select-client-roles.tooltip=Selecione o cliente para visualizar os roles de cliente. +assign.available-roles.tooltip=Roles de Cliente disponíveis para associação. +client.assigned-roles.tooltip=Roles de Cliente associados. +client.effective-roles.tooltip=Roles de cliente associados que podem ter sido herdados de um role composto. +node-reregistration-timeout.tooltip=Intervalo para especificar o tempo máximo para nós clientes de cluster registrados se re-registrarem. Se os nós do cluster não enviarem solicitações de re-registro dentro deste intervalo eles serão deregistrados do Keycloak. +client-revoke.not-before.tooltip=Revocar qualquer token emitido antes desta data para este cliente. +client-revoke.push.tooltip=Se a URL de administração estiver configurada para este cliente, envie esta política para este cliente. +offline-tokens.tooltip=Número total de tokens offline para este cliente. +show-offline-tokens.tooltip=Atenção, esta é uma operação potencialmente cara dependendo do número de tokens offline. +realm-certificate-alias.tooltip=O certificado do Realm também é guardado em arquivo. Este é o alias para ele. +saml-encryption-key.tooltip=Chave de encriptação SAML. +mapper.consent-required.tooltip=Ao conceder acesso temporário, deve o usuário consentir em prover esta informação para o cliente? +consent-text.tooltip=Texto para exibir na página de consentimento +mapper-type.tooltip=Tipo do mapeamento +redirect-uri.tooltip=A url de redirecionamento para usar quando da configuração do provedor de identidade. +identity-provider.alias.tooltip=O alias é o identificador único de um provedor de identidade e também é utilizado para construir a uri de redirecionamento. +identity-provider.enabled.tooltip=Habilita/Desabilita este provedor de identidade. +identity-provider.authenticate-by-default.tooltip=Indica se este provedor deve ser tentado por padrão para a autenticação mesmo ante de exibir a tela de login. +identity-provider.store-tokens.tooltip=Habilita/desabilita se os tokens deve ser guardados depois de autenticar os usuários. +identity-provider.stored-tokens-readable.tooltip=Habilita/desabilita se novos usuários podem ler quaisquer tokens salvo. Isto irá adicionar o role broker.read-token. +update-profile-on-first-login.tooltip=Define condições onde um usuário precisa atualizar o seu perfil durante o primeiro login. +trust-email.tooltip=Se habilitado então o e-mail provido por este provedor não será verificado mesmo que a verificação esteja habilitada para este realm. +first-broker-login-flow.tooltip=Alias do fluxo de autenticação que será invocado depois do primeiro login com este provedor de identificação. O termo 'Primeiro Login' significa que ainda não existe uma conta no Keycloak ligada a esta conta autenticada neste provedor. +post-broker-login-flow.tooltip=Alias do fluxo de autenticação que será invocado depois de cada login com esse provedor de identificação. É útil se você pretende adicionar verificações adicionais de cada usuário autenticado com este provedor (por exemplo OTP). Deixa vazio se você não deseja que nenhum autenticador adicionar seja invocado depois do login com este provedor de identificação. Note também que as implementações de autenticação devem assumir que o usuários já está definido na ClientSessioncom e com o provedor de identidade já definido. +openid-connect-config.tooltip=OIDC SP e configuração externa IDP. +authorization-url.tooltip=A URL de autorização. +token-url.tooltip=A URL do Token. +identity-provider.logout-url.tooltip='End session endpoint' para utilizar para realizar logour dos usuários do IDP externo. +backchannel-logout.tooltip=O IDP externo suporta logou via backchannel? +user-info-url.tooltip=A Url de informações de usuário. Opcional. +identity-provider.client-id.tooltip=O cliente ou identificador do cliente registrado junto ao provedor de identificação. +client-secret.tooltip=O cliente ou senha do cliente registrado junto ao provedor de identificação. +social.client-secret.tooltip=A senha do cliente registrado junto ao provedor de identificação. +issuer.tooltip=O identificador de emissor para o emissor da resposta. Se não for provido nenhuma validação será realizada. +identity-provider.default-scopes.tooltip=Os escopos que serão enviados ao solicitar autorização. Pode ser uma lista de escopos separadas por espaço. Valor padrão é 'openid'. +prompt.tooltip=Especifica se o Servidor de Autorização solicita ao Usuário Final reautenticação e consentimento. +identity-provider.validate-signatures.tooltip=Habilita/Desabilita a validação de assinatura de IDP externo. +identity-provider.validating-public-key.tooltip=A chave pública em formato PEM que deve ser usada para verificar assinaturas de IDP externos. +import-external-idp-config.tooltip=Permite que vocÊ carregue metadata de IDP externos de um arquivo de configuração ou baixando a partir de uma URL. +identity-provider.import-from-url.tooltip=Importar metadata de um descritor de descoberta remoto do IDP. +identity-provider.import-from-file.tooltip=Importar metadata fr um descritor de descoberta baixado do IDP. +identity-provider.saml-config.tooltip=SAML SP e configuração de IDP externo. +saml.single-signon-service-url.tooltip=A Url que deve ser utilizada para enviar solicitações de autenticação (SAML AuthnRequest). +saml.single-logout-service-url.tooltip=A Url que deve ser utilizada para enviar solicitações de logout. +nameid-policy-format.tooltip=Especifica a referência de URI correspondente a um formato de nome identificador. O padrão é urn:oasis:names:tc:SAML:2.0:nameid-format:persistent. +http-post-binding-response.tooltip=Indica se deve se responder a solicitações utilizando HTTP-POST. Se falso, HTTP-REDIRECT será utilizado. +http-post-binding-for-authn-request.tooltip=Indica se o AuthnRequest deve ser enviado utilizando HTTP-POST. Se falso, HTTP-REDIRECT será utilizado. +want-authn-requests-signed.tooltip=Indicate se um provedor de identificação deve experar um AuthnRequest assinado. +identity-provider.force-authentication.tooltip=Indica se um provedor de identificação deve autenticar o apresentador diretamente ao invés de confiar em um contexto de segurança anterior. +saml.validate-signature.tooltip=Habilita/Desabilita validação de assinaturas de respostas SAML. +saml.import-from-url.tooltip=Importar metadata de um descritor de entidade IDP SAML remoto. +social.client-id.tooltip=O identificador do cliente registrado com o provedor de identificação. +social.default-scopes.tooltip=Os escopos que serão enviados ao solicitar autorização. Veja a documentação para valores possíveis, separador e valores padrão. +stackoverflow.key.tooltip=A chave de cliente obtida do registro no Stack Overflow. +client-templates.tooltip=Modelos de cliente permitem que você defina configurações comuns que serão compartilhadas entre múltiplos clientes. +group.add-selected.tooltip=Roles do Realm que serão associadas ao grupo. +group.effective-roles.tooltip=Todos os mapeamentos de roles do Realm. Alguns roles exibidos podem ter sido herdados de um role composto mapeado. +group.available-roles.tooltip=Roles associáveis deste cliente. +group.assigned-roles-client.tooltip=Mapeamentos de roles para este cliente. +group.effective-roles-client.tooltip=Mapeamentos de roles para este cliente. Alguns roles exibidos podem ter sido herdados de um role composto mapeado. +user.add-selected.tooltip=Roles do Realm que podem ser associados ao usuário. +user.assigned-roles.tooltip=Roles do Realm mapeados para o usuário. +user.effective-roles.tooltip=Todos os mapeamentos de roles do Realm. Alguns roles exibidos podem ter sido herdados de um role composto mapeado. +user.available-roles.tooltip=Roles associáveis deste cliente. +user.assigned-roles-client.tooltip=Mapeamentos de roles para este cliente. +user.effective-roles-client.tooltip=Mapeamentos de Role para este cliente. Alguns roles exibidos podem ter sido herdados de um role composto mapeado. +default.available-roles.tooltip=Roles do nível de Realm que podem ser associados. +realm-default-roles.tooltip=Roles do nível de Realm associados a novos usuários. +default.available-roles-client.tooltip=Roles para este cliente que são associáveis por padrão. +client-default-roles.tooltip=Roles para este cliente que são associados como roles padrão. +composite.available-roles.tooltip=Roles do nível de Realm associáveis com este role composto. +composite.associated-roles.tooltip=Roles do nível de Realm associados com este role composto. +composite.available-roles-client.tooltip=Roles para este cliente que podem ser associados com este role composto. +composite.associated-roles-client.tooltip=Roles do cliente associados com este role composto. +partial-import.tooltip=Importação parcial permite que você importe usuários, clientes, e outros recursos de um arquivo json previamente exportado. +if-resource-exists.tooltip=Especifica o que deve ser feito se você tentar importar um recurso já existente. +realm-roles.tooltip=Roles do Realm que podem ser selecionados. +authz-policy-role-realm-roles.tooltip=Especifica quais role(s) de *realm* são permitidos por esta política. +client-roles.tooltip=Roles do cliente que podem ser selecionados. +authz-policy-role-client-roles.tooltip=Especifica quais role(s) do *cliente* são permitidos por esta política. +client-template.tooltip=Modelo de cliente do qual ete cliente herda as configurações. +client-template.name.tooltip=Nome do modelo de cliente. Deve ser único neste Realm. +client-template.description.tooltip=Descrição do modelo de cliente. +client-template.protocol.tooltip=Qual configuração de protocolo SSO será provida por este modelo de cliente. +console-display-name.tooltip=Nome de exibição do provedor quando linkado no console de administração. +priority.tooltip=Prioridade do provedor quando da busca de usuários. Valores mais baixos são utilizados primeiro. +periodic-full-sync.tooltip=Habilitar ou não a sincronização completa periódica dos usuários deste provedor. +ldap.periodic-full-sync.tooltip=Habilitar ou não a sincronização completa dos usuários do LDAP para o Keycloak. +full-sync-period.tooltip=Intervalo para a sincronização completa em segundos. +periodic-changed-users-sync.tooltip=Habilitar ou não a sincronização de usuários novos ou alterados do provedor para o Keycloak. +ldap.periodic-changed-users-sync.tooltip=Habilitar ou não a sincronização de usuários novos ou alterados do LDAP para o Keycloak. +changed-users-sync-period.tooltip=Intervalo para sincronização dos usuários alterados ou novos do provedor em segundos. +ldap.changed-users-sync-period.tooltip=Intervalo para sincronização dos usuários alterados ou novos do LDAP em segundos. +kerberos-realm.tooltip=Nome do realm kerberos. Por exemplo FOO.ORG +server-principal.tooltip=Nome completo do principal do servidor para o serviço HTTP incluindo o servidor e nome do domínio. Por exemplo HTTP/host.foo.org@FOO.ORG +keytab.tooltip=Localização do arquivo KeyTab do Kerberos contendo as credenciais do principal do servidor. Por exemplo /etc/krb5.keytab +debug.tooltip=Habilita/Desabilita log de nível debug para a saída padrão para Krb5LoginModule. +allow-password-authentication.tooltip=Habilita/Desabilita a possibilidade de autenticação via usuário/senha contra o banco Kerberos +edit-mode.tooltip=READ_ONLY significa que atualizações de senhas não são permitidas e o usuário sempre autenticará com a senha do Kerberos. UNSYNCED significa que o usuário pode alterar a senha no banco do Keycloak e essa senha será utilizda ao invés da senha do Kerberos. +ldap.edit-mode.tooltip=READ_ONLY é um LDAP somente leitura. WRITABLE significa que os dados serão sicronizados de volta para o LDAP on demand. UNSYNCED significa que os dados do usuário serão importados, mas não sicronizados de volta para o LDAP. +update-profile-first-login.tooltip=Atualizar o perfil no primeiro login +ldap.sync-registrations.tooltip=Os novos usuários criados devem ser criados no LDAP? A prioridade afeta qual provedor é utilizado para sincronizar o novo usuário. +ldap.vendor.tooltip=LDAP vendor (provedor) +username-ldap-attribute.tooltip=Nome do atributo do LDAP que será mapeado como nome do usuário no Keycloak. Para muitos servidores LDAP este valor deve ser 'uid'. Para o Active Directory pode ser 'sAMAccountName' ou 'cn'. O atributo deve ser preenchido para todos os registros de usuários do LDAP que você deseja importar do LDAP para o Keycloak. +rdn-ldap-attribute.tooltip=Nome do atributo LDAP que é utilizado como RDN (atributo topo) do DN do usuário típico. Geralmente é o mesmo que o atributo do nome do usuário, mas isto não é obrigatório. Por exemplo para o Active Directory é comum utilizar 'cn' como atributo RDN quando o atributo do nome de usuário pode ser 'sAMAccountName', +uuid-ldap-attribute.tooltip=Nome do atributo LDAP que é utilizado como identificador único do objeto(UUID) para objetos no LDAP. Para muitos servidores LDAP o valor é 'entryUUID', porém alguns são diferentes. Por exemplo para o Active Directory este valor para objetos no LDAP deve ser 'objectGUID'. Se o seu servidor LDAP realmente não suporta a noção de UUID, vocÊ pode usar qualquer outro atributo que seja único na árvore de usuários do LDAP. Por exemplo 'uid' ou 'entryDN'. +ldap.user-object-classes.tooltip=Todos os valores de objectClass para usuários no LDAP separados por vírgula. Por exemplo: 'inetOrgPerson, organizationalPerson'. Usuários criados no Keycloak serão enviados para o LDAP com todas essas classes de objetos associadas e dados de usuários do LDAP somente serão localizados se ele contiverem todas estas classes de objeto. +ldap.connection-url.tooltip=Conexão URL para o seu servidor LDAP +ldap.users-dn.tooltip=DN completo da árvore LDAP onde os usuários estão. Este DN é o pai dos usuários do LDAP. Por exemplo pode ser 'ou=users,dc=example,dc=com' entendendo que o usuário típico irá ter um DN como 'uid=john,ou=users,dc=example,dc=com'. +ldap.authentication-type.tooltip=Tipo de autenticação no LDAP. No momento apenas os mecanismos 'none' (anonymous LDAP authentication) ou 'simple' (Credencial de bind + senha para bind) estão disponíveis. +ldap.bind-dn.tooltip=DN do administrador do LDAP, que será utilizado pelo Keycloak para acessar o servidor LDAP. +ldap.bind-credential.tooltip=Senha do administrador do LDAP +ldap.custom-user-ldap-filter.tooltip=Additional LDAP Filter for filtering searched users. Leave this empty if you don't need additional filter. Make sure that it starts with '(' and ends with ')' +ldap.use-truststore-spi.tooltip=Specifies whether LDAP connection will use the truststore SPI with the truststore configured in keycloak-server.json. 'Always' means that it will always use it. 'Never' means that it won't use it. 'Only for ldaps' means that it will use if your connection URL use ldaps. Note even if keycloak-server.json is not configured, the default Java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used. +ldap.connection-pooling.tooltip=Does Keycloak should use connection pooling for accessing LDAP server +ldap.pagination.tooltip=Does the LDAP server support pagination. +ldap.allow-kerberos-authentication.tooltip=Enable/disable HTTP authentication of users with SPNEGO/Kerberos tokens. The data about authenticated users will be provisioned from this LDAP server +ldap.use-kerberos-for-password-authentication.tooltip=Use Kerberos login module for authenticate username/password against Kerberos server instead of authenticating against LDAP server with Directory Service API +ldap.batch-size.tooltip=Count of LDAP users to be imported from LDAP to Keycloak within single transaction. +identity-provider-user-id.tooltip=Unique ID of the user on the Identity Provider side +identity-provider-username.tooltip=Username on the Identity Provider side +browser-flow.tooltip=Select the flow you want to use for browser authentication. +registration-flow.tooltip=Select the flow you want to use for registration. +direct-grant-flow.tooltip=Select the flow you want to use for direct grant authentication. +reset-credentials.tooltip=Select the flow you want to use when the user has forgotten their credentials. +client-authentication.tooltip=Select the flow you want to use for authentication of clients. +authenticator.alias.tooltip=Name of the configuration +otp-type.tooltip=totp is Time-Based One Time Password. 'hotp' is a counter base one time password in which the server keeps a counter to hash against. +otp-hash-algorithm.tooltip=What hashing algorithm should be used to generate the OTP. +otp.number-of-digits.tooltip=How many digits should the OTP have? +otp.look-ahead-window.tooltip=How far ahead should the server look just in case the token generator and server are out of time sync or counter sync? +otp.initial-counter.tooltip=What should the initial counter value be? +otp-token-period.tooltip=How many seconds should an OTP token be valid? Defaults to 30 seconds. +admin-events.tooltip=Displays saved admin events for the realm. Events are related to admin account, for example a realm creation. To enable persisted events go to config. +clear-admin-events.tooltip=Deletes all admin events in the database. +resource-path.tooltip=Filter by resource path. Supports wildcards '*' to match a single part of the path and '**' matches multiple parts. For example 'realms/*/clients/asbc' matches client with id asbc in any realm, while or 'realms/master/**' matches anything in the master realm. +auth.default-action.tooltip=If enabled, any new user will have this required action assigned to it. +groups.default-groups.tooltip=Set of groups that new users will automatically join. +flow.alias.tooltip=Specifies display name for the flow. +top-level-flow-type.tooltip=What kind of top level flow is it? Type 'client' is used for authentication of clients (applications) when generic is for users and everything else +flow-type.tooltip=What kind of form is it +default-groups.tooltip=Newly created or registered users will automatically be added to these groups +available-groups.tooltip=Select a group you want to add as a default. +membership.available-groups.tooltip=Groups a user can join. Select a group and click the join button. +events.tooltip=Displays saved events for the realm. Events are related to user accounts, for example a user login. To enable persisted events go to config. +login.save-events.tooltip=If enabled login events are saved to the database which makes events available to the admin and account management consoles. +admin.save-events.tooltip=If enabled admin events are saved to the database which makes events available to the admin console. +events-config.tooltip=Displays configuration options to enable persistence of user and admin events. +event-listeners.tooltip=Configure what listeners receive events for the realm. +clear-events.tooltip=Deletes all events in the database. +events.expiration.tooltip=Sets the expiration for events. Expired events are periodically deleted from the database. +saved-types.tooltip=Configure what event types are saved. +include-representation.tooltip=Include JSON representation for create and update requests. +credentials.temporary.tooltip=If enabled user is required to change password on next login +credentials.remove-totp.tooltip=Remove one time password generator for user. +credentials.reset-actions.tooltip=Set of actions to execute when sending the user a Reset Actions Email. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator. +credentials.reset-actions-email.tooltip=Sends an email to user with an embedded link. Clicking on link will allow the user to execute the reset actions. They will not have to login prior to this. For example, set the action to update password, click this button, and the user will be able to change their password without logging in. +user-enabled.tooltip=A disabled user cannot login. +user-temporarily-locked.tooltip=The user may have been locked due to failing to login too many times. +email-verified.tooltip=Has the user's email been verified? +required-user-actions.tooltip=Require an action when the user logs in. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator. +impersonate-user.tooltip=Login as this user. If user is in same realm as you, your current login session will be logged out before you are logged in as this user. +group-membership.tooltip=Groups user is a member of. Select a listed group and click the Leave button to leave the group. +import-keys-and-cert.tooltip=Upload the client's key pair and cert. +authz-icon-uri.tooltip=An URI pointing to an icon. +authz-authorization-services-enabled.tooltip=Enable/Disable fine-grained authorization support for a client +authz-import-config.tooltip=Import a JSON file containing authorization settings for this resource server. +authz-policy-enforcement-mode.tooltip=The policy enforcement mode dictates how policies are enforced when evaluating authorization requests. 'Enforcing' means requests are denied by default even when there is no policy associated with a given resource. 'Permissive' means requests are allowed even when there is no policy associated with a given resource. 'Disabled' completely disables the evaluation of policies and allow access to any resource. +authz-remote-resource-management.tooltip=Should resources be managed remotely by the resource server? If false, resources can only be managed from this admin console. +authz-export-settings.tooltip=Export and download all authorization settings for this resource server. +authz-resource-name.tooltip=An unique name for this resource. The name can be used to uniquely identify a resource, useful when querying for a specific resource. +authz-resource-owner.tooltip=The owner of this resource. +authz-resource-type.tooltip=The type of this resource. It can be used to group different resource instances with the same type. +authz-resource-uri.tooltip=An URI that can also be used to uniquely identify this resource. +authz-resource-scopes.tooltip=The scopes associated with this resource. +authz-scope-name.tooltip=An unique name for this scope. The name can be used to uniquely identify a scope, useful when querying for a specific scope. +authz-policy-name.tooltip=The name of this policy. +authz-policy-description.tooltip=A description for this policy. +authz-policy-logic.tooltip=The logic dictates how the policy decision should be made. If 'Positive', the resulting effect (permit or deny) obtained during the evaluation of this policy will be used to perform a decision. If 'Negative', the resulting effect will be negated, in other words, a permit becomes a deny and vice-versa. +authz-policy-apply-policy.tooltip=Specifies all the policies that must be applied to the scopes defined by this policy or permission. +authz-policy-decision-strategy.tooltip=The decision strategy dictates how the policies associated with a given policy are evaluated and how a final decision is obtained. 'Affirmative' means that at least one policy must evaluate to a positive decision in order to the overall decision be also positive. 'Unanimous' means that all policies must evaluate to a positive decision in order to the overall decision be also positive. 'Consensus' means that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same, the final decision will be negative. +authz-policy-user-users.tooltip=Specifies which user(s) are allowed by this policy. +authz-policy-time-not-before.tooltip=Defines the time before which the policy MUST NOT be granted. Only granted if current date/time is after or equal to this value. +authz-policy-time-not-on-after.tooltip=Defines the time after which the policy MUST NOT be granted. Only granted if current date/time is before or equal to this value. +authz-policy-drools-maven-artifact.tooltip=A Maven GAV pointing to an artifact from where the rules would be loaded from. Once you have provided the GAV, you can click *Resolve* to load both *Module* and *Session* fields. +authz-policy-drools-module.tooltip=The module used by this policy. You must provide a module in order to select a specific session from where rules will be loaded from. +authz-policy-drools-session.tooltip=The session used by this policy. The session provides all the rules to evaluate when processing the policy. +authz-policy-drools-update-period.tooltip=Specifies an interval for scanning for artifact updates. +authz-policy-js-code.tooltip=The JavaScript code providing the conditions for this policy. +authz-permission-name.tooltip=The name of this permission. +authz-permission-description.tooltip=A description for this permission. +authz-permission-resource-apply-to-resource-type.tooltip=Specifies if this permission would be applied to all resources with a given type. In this case, this permission will be evaluated for all instances of a given resource type. +authz-permission-resource-resource.tooltip=Specifies that this permission must be applied to a specific resource instance. +authz-permission-resource-type.tooltip=Specifies that this permission must be applied to all resources instances of a given type. +authz-permission-scope-resource.tooltip=Restrict the scopes to those associated with the selected resource. If not selected all scopes would be available. +authz-evaluation-identity-information.tooltip=The available options to configure the identity information that will be used when evaluating policies. +authz-evaluation-client.tooltip=Select the client making this authorization request. If not provided, authorization requests would be done based on the client you are in. +authz-evaluation-user.tooltip=Select an user whose identity is going to be used to query permissions from the server. +authz-evaluation-role.tooltip=Select the roles you want to associate with the selected user. +authz-evaluation-contextual-info.tooltip=The available options to configure any contextual information that will be used when evaluating policies. +authz-evaluation-contextual-attributes.tooltip=Any attribute provided by a running environment or execution context. +authz-evaluation-permissions.tooltip=The available options to configure the permissions to which policies will be applied. +authz-evaluation-result.tooltip=The overall result for this permission request. +authz-evaluation-scopes.tooltip=The list of allowed scopes. +authz-evaluation-policies.tooltip=Details about which policies were evaluated and their decisions. +authz-evaluation-authorization-data.tooltip=Represents a token carrying authorization data as a result of the processing of an authorization request. This representation is basically what Keycloak issues to clients asking for permissions. Check the 'authorization' claim for the permissions that were granted based on the current authorization request. diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties b/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties index e69de29bb2..e6f2faab8c 100644 --- a/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties +++ b/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties @@ -0,0 +1,15 @@ +#encoding: utf-8 +invalidPasswordMinLengthMessage=Senha inválida: deve conter ao menos {0} caracteres. +invalidPasswordMinLowerCaseCharsMessage=Senha inválida: deve conter ao menos {0} caracteres minúsculos. +invalidPasswordMinDigitsMessage=Senha inválida: deve conter ao menos {0} digitos numéricos. +invalidPasswordMinUpperCaseCharsMessage=Senha inválida: deve conter ao menos {0} caracteres maiúsculos. +invalidPasswordMinSpecialCharsMessage=Senha inválida: deve conter ao menos {0} caracteres especiais. +invalidPasswordNotUsernameMessage=Senha inválida: não deve ser igual ao nome de usuário. +invalidPasswordRegexPatternMessage=Senha inválida: falha ao passar por padrões. +invalidPasswordHistoryMessage=Senha inválida: não deve ser igual às últimas {0} senhas. + +ldapErrorInvalidCustomFilter=Filtro LDAP customizador não inicia com "(" ou não termina com ")". +ldapErrorMissingClientId=ID do cliente precisa ser definido na configuração quando mapeamentos de Roles do Realm não é utilizado. +ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Não é possível preservar herança de grupos e usar tipo de associação de UID ao mesmo tempo. +ldapErrorCantWriteOnlyForReadOnlyLdap=Não é possível definir modo de somente escrita quando o provedor LDAP não é WRITABLE +ldapErrorCantWriteOnlyAndReadOnly=Não é possível definir somente escrita e somente leitura ao mesmo tempo From 44f57b8273ead64d10bf81d79ded793baf85fb58 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Fri, 9 Sep 2016 17:48:32 -0300 Subject: [PATCH 30/53] [KEYCLOAK-3446] - Some minor changes --- .../base/admin/messages/admin-messages_pt_BR.properties | 6 +++--- .../theme/base/admin/messages/messages_pt_BR.properties | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties index beacd2c483..5130658234 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_pt_BR.properties @@ -1,5 +1,5 @@ #encoding: utf-8 -consoleTitle=Console de administração do Keycloak +consoleTitle=Console de Administração do Keycloak # Common messages enabled=Habilitado @@ -13,7 +13,7 @@ offText=Não client=Cliente clients=Clientes clear=Limpar -selectOne=Selecione um... +selectOne=Selecione Um... true=Sim false=Não @@ -22,7 +22,7 @@ endpoints=Endpoints # Realm settings realm-detail.enabled.tooltip=Usuários e clientes somente podem acessar um Realm se ele estiver habilitado -realm-detail.oidc-endpoints.tooltip=Exibe a configuração dos endpoint do OpenID Connect +realm-detail.oidc-endpoints.tooltip=Exibe a configuração dos endpoints do OpenID Connect registrationAllowed=Cadastro de usuário registrationAllowed.tooltip=Habilita/desabilita a página de cadastro. Um link para a página de cadastro também será exibido na tela de login. registrationEmailAsUsername=Email como nome de usuário diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties b/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties index e6f2faab8c..b26e46508b 100644 --- a/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties +++ b/themes/src/main/resources/theme/base/admin/messages/messages_pt_BR.properties @@ -8,8 +8,11 @@ invalidPasswordNotUsernameMessage=Senha inválida: não deve ser igual ao nome d invalidPasswordRegexPatternMessage=Senha inválida: falha ao passar por padrões. invalidPasswordHistoryMessage=Senha inválida: não deve ser igual às últimas {0} senhas. -ldapErrorInvalidCustomFilter=Filtro LDAP customizador não inicia com "(" ou não termina com ")". +ldapErrorInvalidCustomFilter=Filtro LDAP não inicia com "(" ou não termina com ")". ldapErrorMissingClientId=ID do cliente precisa ser definido na configuração quando mapeamentos de Roles do Realm não é utilizado. ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Não é possível preservar herança de grupos e usar tipo de associação de UID ao mesmo tempo. -ldapErrorCantWriteOnlyForReadOnlyLdap=Não é possível definir modo de somente escrita quando o provedor LDAP não é WRITABLE +ldapErrorCantWriteOnlyForReadOnlyLdap=Não é possível definir modo de somente escrita quando o provedor LDAP não suporta escrita ldapErrorCantWriteOnlyAndReadOnly=Não é possível definir somente escrita e somente leitura ao mesmo tempo + +clientRedirectURIsFragmentError=URIs de redirecionamento não podem conter fragmentos +clientRootURLFragmentError=URL raiz não pode conter fragmentos \ No newline at end of file From bf6246f5c14021fdd4b6a5451ea84d14b2b6555e Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 6 Sep 2016 20:15:00 +0200 Subject: [PATCH 31/53] KEYCLOAK-905 Realm keys rotation support on adapters --- .../adapters/AdapterDeploymentContext.java | 88 ++++++-------- .../BearerTokenRequestAuthenticator.java | 3 +- .../keycloak/adapters/CookieTokenStore.java | 3 +- .../keycloak/adapters/HttpAdapterUtils.java | 79 +++++++++++++ .../adapters/HttpClientAdapterException.java | 32 ++++++ .../keycloak/adapters/KeycloakDeployment.java | 29 +++-- .../adapters/KeycloakDeploymentBuilder.java | 10 +- .../adapters/OAuthRequestAuthenticator.java | 3 +- .../adapters/PreAuthActionsHandler.java | 6 +- .../RefreshableKeycloakSecurityContext.java | 3 +- .../KeycloakAdapterPolicyEnforcer.java | 7 +- .../jaas/AbstractKeycloakLoginModule.java | 6 +- .../rotation/AdapterRSATokenVerifier.java | 64 +++++++++++ .../rotation/HardcodedPublicKeyLocator.java | 40 +++++++ .../rotation/JWKPublicKeyLocator.java | 107 ++++++++++++++++++ .../adapters/rotation/PublicKeyLocator.java | 37 ++++++ .../KeycloakDeploymentBuilderTest.java | 12 +- .../resources/keycloak-no-credentials.json | 1 - .../src/test/resources/keycloak.json | 3 +- .../adapters/installed/KeycloakInstalled.java | 3 +- .../java/org/keycloak/RSATokenVerifier.java | 25 +++- .../constants/ServiceUrlConstants.java | 1 + .../java/org/keycloak/jose/jwk/JWKParser.java | 11 +- .../adapters/config/AdapterConfig.java | 11 +- .../java/org/keycloak/util/JWKSUtils.java | 45 ++++++++ .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/resources/META-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../example/ProductServiceAccountServlet.java | 3 +- .../WEB-INF/keycloak-client-secret.json | 1 - .../WEB-INF/keycloak-client-signed-jwt.json | 1 - .../OSGI-INF/blueprint/blueprint.xml | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/resources/WEB-INF/keycloak.json | 1 - .../main/resources/META-INF/spring/beans.xml | 1 - .../external-config-keycloak.json | 1 - .../fuse-admin/keycloak-direct-access.json | 1 - .../src/main/resources/WEB-INF/keycloak.json | 1 - .../js-console/src/main/webapp/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/webapp/WEB-INF/keycloak.json | 1 - .../src/main/resources/tenant1-keycloak.json | 1 - .../src/main/resources/tenant2-keycloak.json | 1 - .../java/org/keycloak/models/Constants.java | 3 + .../models/utils/RepresentationToModel.java | 2 +- .../KeycloakOIDCClientInstallation.java | 1 - ...kOIDCJbossSubsystemClientInstallation.java | 1 - .../protocol/oidc/utils/JWKSUtils.java | 4 +- .../services/managers/ClientManager.java | 2 - .../resources/admin/RealmAdminResource.java | 5 +- .../adapter/filter/AdapterActionsFilter.java | 37 ++++-- .../adapter/page/AbstractShowTokensPage.java | 6 + .../DeploymentArchiveProcessor.java | 3 - .../adapter/AbstractServletsAdapterTest.java | 3 +- .../AbstractDemoServletsAdapterTest.java | 61 ++++++++++ .../AbstractOfflineServletsAdapterTest.java | 2 +- .../admin/client/InstallationTest.java | 4 +- .../client/AdapterInstallationConfigTest.java | 5 - .../customer-portal/WEB-INF/keycloak.json | 4 +- .../input-portal/WEB-INF/keycloak.json | 2 +- .../token-min-ttl/WEB-INF/keycloak.json | 4 +- .../token-min-ttl/WEB-INF/web.xml | 6 + 78 files changed, 668 insertions(+), 152 deletions(-) create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java create mode 100644 core/src/main/java/org/keycloak/util/JWKSUtils.java diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java index d7985b0714..925a0dba15 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/AdapterDeploymentContext.java @@ -17,26 +17,21 @@ package org.keycloak.adapters; -import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; import org.jboss.logging.Logger; import org.keycloak.adapters.authentication.ClientCredentialsProvider; +import org.keycloak.adapters.authorization.PolicyEnforcer; +import org.keycloak.adapters.rotation.PublicKeyLocator; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.SslRequired; import org.keycloak.enums.TokenStore; import org.keycloak.representations.adapters.config.AdapterConfig; -import org.keycloak.representations.idm.PublishedRealmRepresentation; -import org.keycloak.util.JsonSerialization; import org.keycloak.common.util.KeycloakUriBuilder; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.net.URI; -import java.security.PublicKey; import java.util.Map; /** @@ -57,7 +52,7 @@ public class AdapterDeploymentContext { * during the application deployment's life cycle. * * @param deployment A KeycloakConfigResolver, possibly missing the Auth - * Server URL and/or Realm Public Key + * Server URL */ public AdapterDeploymentContext(KeycloakDeployment deployment) { this.deployment = deployment; @@ -79,7 +74,6 @@ public class AdapterDeploymentContext { /** * For single-tenant deployments, it complements KeycloakDeployment * by resolving a relative Auth Server's URL based on the current request - * and, if needed, will lazily resolve the Realm's Public Key. * * For multi-tenant deployments, defers the resolution of KeycloakDeployment * to the KeycloakConfigResolver . @@ -98,8 +92,8 @@ public class AdapterDeploymentContext { if (deployment.getAuthServerBaseUrl() == null) return deployment; KeycloakDeployment resolvedDeployment = resolveUrls(deployment, facade); - if (resolvedDeployment.getRealmKey() == null) { - resolveRealmKey(resolvedDeployment); + if (resolvedDeployment.getPublicKeyLocator() == null) { + throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs"); } return resolvedDeployment; } @@ -115,45 +109,6 @@ public class AdapterDeploymentContext { } } - public void resolveRealmKey(KeycloakDeployment deployment) { - if (deployment.getClient() == null) { - throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs"); - } - HttpGet get = new HttpGet(deployment.getRealmInfoUrl()); - try { - HttpResponse response = deployment.getClient().execute(get); - int status = response.getStatusLine().getStatusCode(); - if (status != 200) { - close(response); - throw new RuntimeException("Unable to resolve realm public key remotely, status = " + status); - } - HttpEntity entity = response.getEntity(); - if (entity == null) { - throw new RuntimeException("Unable to resolve realm public key remotely. There was no entity."); - } - InputStream is = entity.getContent(); - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - int c; - while ((c = is.read()) != -1) { - os.write(c); - } - byte[] bytes = os.toByteArray(); - String json = new String(bytes); - PublishedRealmRepresentation rep = JsonSerialization.readValue(json, PublishedRealmRepresentation.class); - deployment.setRealmKey(rep.getPublicKey()); - } finally { - try { - is.close(); - } catch (IOException ignored) { - - } - } - } catch (IOException e) { - throw new RuntimeException("Unable to resolve realm public key remotely", e); - } - } - /** * This delegate is used to store temporary, per-request metadata like request resolved URLs. * Ever method is delegated except URL get methods and isConfigured() @@ -207,6 +162,11 @@ public class AdapterDeploymentContext { return (this.unregisterNodeUrl != null) ? this.unregisterNodeUrl : delegate.getUnregisterNodeUrl(); } + @Override + public String getJwksUrl() { + return (this.jwksUrl != null) ? this.jwksUrl : delegate.getJwksUrl(); + } + @Override public String getResourceName() { return delegate.getResourceName(); @@ -223,13 +183,13 @@ public class AdapterDeploymentContext { } @Override - public PublicKey getRealmKey() { - return delegate.getRealmKey(); + public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) { + delegate.setPublicKeyLocator(publicKeyLocator); } @Override - public void setRealmKey(PublicKey realmKey) { - delegate.setRealmKey(realmKey); + public PublicKeyLocator getPublicKeyLocator() { + return delegate.getPublicKeyLocator(); } @Override @@ -466,6 +426,26 @@ public class AdapterDeploymentContext { public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) { delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive); } + + @Override + public PolicyEnforcer getPolicyEnforcer() { + return delegate.getPolicyEnforcer(); + } + + @Override + public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) { + delegate.setPolicyEnforcer(policyEnforcer); + } + + @Override + public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) { + delegate.setMinTimeBetweenJwksRequests(minTimeBetweenJwksRequests); + } + + @Override + public int getMinTimeBetweenJwksRequests() { + return delegate.getMinTimeBetweenJwksRequests(); + } } protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) { diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java index 9af6214ba2..70b90b1044 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java @@ -19,6 +19,7 @@ package org.keycloak.adapters; import org.jboss.logging.Logger; import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.spi.AuthChallenge; import org.keycloak.adapters.spi.AuthOutcome; import org.keycloak.adapters.spi.HttpFacade; @@ -84,7 +85,7 @@ public class BearerTokenRequestAuthenticator { protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) { try { - token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); + token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); } catch (VerificationException e) { log.error("Failed to verify token", e); challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage()); diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java index 7f66dbf2e6..2093645533 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/CookieTokenStore.java @@ -22,6 +22,7 @@ import java.io.IOException; import org.jboss.logging.Logger; import org.keycloak.KeycloakPrincipal; import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.common.VerificationException; import org.keycloak.constants.AdapterConstants; @@ -73,7 +74,7 @@ public class CookieTokenStore { try { // Skip check if token is active now. It's supposed to be done later by the caller - AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false, true); + AccessToken accessToken = AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true); IDToken idToken; if (idTokenString != null && idTokenString.length() > 0) { try { diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java new file mode 100644 index 0000000000..e01f7dc32e --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpAdapterUtils.java @@ -0,0 +1,79 @@ +/* + * 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.adapters; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpRequestBase; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class HttpAdapterUtils { + + + public static T sendJsonHttpRequest(KeycloakDeployment deployment, HttpRequestBase httpRequest, Class clazz) throws HttpClientAdapterException { + try { + HttpResponse response = deployment.getClient().execute(httpRequest); + int status = response.getStatusLine().getStatusCode(); + if (status != 200) { + close(response); + throw new HttpClientAdapterException("Unexpected status = " + status); + } + HttpEntity entity = response.getEntity(); + if (entity == null) { + throw new HttpClientAdapterException("There was no entity."); + } + InputStream is = entity.getContent(); + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int c; + while ((c = is.read()) != -1) { + os.write(c); + } + byte[] bytes = os.toByteArray(); + String json = new String(bytes); + return JsonSerialization.readValue(json, clazz); + } finally { + try { + is.close(); + } catch (IOException ignored) { + + } + } + } catch (IOException e) { + throw new HttpClientAdapterException("IO error", e); + } + } + + + private static void close(HttpResponse response) { + if (response.getEntity() != null) { + try { + response.getEntity().getContent().close(); + } catch (IOException e) { + + } + } + } +} diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java new file mode 100644 index 0000000000..7d303e290d --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientAdapterException.java @@ -0,0 +1,32 @@ +/* + * 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.adapters; + +/** + * @author Marek Posolda + */ +public class HttpClientAdapterException extends Exception { + + public HttpClientAdapterException(String message) { + super(message); + } + + public HttpClientAdapterException(String message, Throwable t) { + super(message, t); + } +} diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java index 901b3ea99c..30c40c37ca 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java @@ -21,6 +21,7 @@ import org.apache.http.client.HttpClient; import org.jboss.logging.Logger; import org.keycloak.adapters.authentication.ClientCredentialsProvider; import org.keycloak.adapters.authorization.PolicyEnforcer; +import org.keycloak.adapters.rotation.PublicKeyLocator; import org.keycloak.constants.ServiceUrlConstants; import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.SslRequired; @@ -29,7 +30,6 @@ import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.common.util.KeycloakUriBuilder; import java.net.URI; -import java.security.PublicKey; import java.util.HashMap; import java.util.Map; @@ -43,7 +43,7 @@ public class KeycloakDeployment { protected RelativeUrlsUsed relativeUrls; protected String realm; - protected volatile PublicKey realmKey; + protected PublicKeyLocator publicKeyLocator; protected String authServerBaseUrl; protected String realmInfoUrl; protected KeycloakUriBuilder authUrl; @@ -52,6 +52,7 @@ public class KeycloakDeployment { protected String accountUrl; protected String registerNodeUrl; protected String unregisterNodeUrl; + protected String jwksUrl; protected String principalAttribute = "sub"; protected String resourceName; @@ -79,13 +80,14 @@ public class KeycloakDeployment { protected volatile int notBefore; protected int tokenMinimumTimeToLive; + protected int minTimeBetweenJwksRequests; private PolicyEnforcer policyEnforcer; public KeycloakDeployment() { } public boolean isConfigured() { - return getRealm() != null && getRealmKey() != null && (isBearerOnly() || getAuthServerBaseUrl() != null); + return getRealm() != null && getPublicKeyLocator() != null && (isBearerOnly() || getAuthServerBaseUrl() != null); } public String getResourceName() { @@ -100,12 +102,12 @@ public class KeycloakDeployment { this.realm = realm; } - public PublicKey getRealmKey() { - return realmKey; + public PublicKeyLocator getPublicKeyLocator() { + return publicKeyLocator; } - public void setRealmKey(PublicKey realmKey) { - this.realmKey = realmKey; + public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) { + this.publicKeyLocator = publicKeyLocator; } public String getAuthServerBaseUrl() { @@ -147,6 +149,7 @@ public class KeycloakDeployment { accountUrl = authUrlBuilder.clone().path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH).build(getRealm()).toString(); registerNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_REGISTER_NODE_PATH).build(getRealm()).toString(); unregisterNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH).build(getRealm()).toString(); + jwksUrl = authUrlBuilder.clone().path(ServiceUrlConstants.JWKS_URL).build(getRealm()).toString(); } public RelativeUrlsUsed getRelativeUrls() { @@ -181,6 +184,10 @@ public class KeycloakDeployment { return unregisterNodeUrl; } + public String getJwksUrl() { + return jwksUrl; + } + public void setResourceName(String resourceName) { this.resourceName = resourceName; } @@ -369,6 +376,14 @@ public class KeycloakDeployment { this.tokenMinimumTimeToLive = tokenMinimumTimeToLive; } + public int getMinTimeBetweenJwksRequests() { + return minTimeBetweenJwksRequests; + } + + public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) { + this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests; + } + public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) { this.policyEnforcer = policyEnforcer; } diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java index 5d54df9bab..04e16f895a 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java @@ -22,6 +22,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.jboss.logging.Logger; import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils; import org.keycloak.adapters.authorization.PolicyEnforcer; +import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator; +import org.keycloak.adapters.rotation.JWKPublicKeyLocator; import org.keycloak.common.enums.SslRequired; import org.keycloak.common.util.PemUtils; import org.keycloak.enums.TokenStore; @@ -59,11 +61,16 @@ public class KeycloakDeploymentBuilder { PublicKey realmKey; try { realmKey = PemUtils.decodePublicKey(realmKeyPem); + HardcodedPublicKeyLocator pkLocator = new HardcodedPublicKeyLocator(realmKey); + deployment.setPublicKeyLocator(pkLocator); } catch (Exception e) { throw new RuntimeException(e); } - deployment.setRealmKey(realmKey); + } else { + JWKPublicKeyLocator pkLocator = new JWKPublicKeyLocator(); + deployment.setPublicKeyLocator(pkLocator); } + if (adapterConfig.getSslRequired() != null) { deployment.setSslRequired(SslRequired.valueOf(adapterConfig.getSslRequired().toUpperCase())); } else { @@ -97,6 +104,7 @@ public class KeycloakDeploymentBuilder { deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup()); deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod()); deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive()); + deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests()); if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) { throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url"); diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java index 73aa0f52e7..02637c0810 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java @@ -20,6 +20,7 @@ package org.keycloak.adapters; import org.jboss.logging.Logger; import org.keycloak.OAuth2Constants; import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.spi.AdapterSessionStore; import org.keycloak.adapters.spi.AuthChallenge; import org.keycloak.adapters.spi.AuthOutcome; @@ -342,7 +343,7 @@ public class OAuthRequestAuthenticator { refreshToken = tokenResponse.getRefreshToken(); idTokenString = tokenResponse.getIdToken(); try { - token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); + token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); if (idTokenString != null) { try { JWSInput input = new JWSInput(idTokenString); diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java index 1a8c735afd..5a1df8c1b6 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java @@ -17,7 +17,10 @@ package org.keycloak.adapters; +import java.security.PublicKey; + import org.jboss.logging.Logger; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.UserSessionManagement; import org.keycloak.jose.jws.JWSInputException; @@ -198,7 +201,8 @@ public class PreAuthActionsHandler { try { JWSInput input = new JWSInput(token); - if (RSAProvider.verify(input, deployment.getRealmKey())) { + PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input, deployment); + if (RSAProvider.verify(input, publicKey)) { return input; } } catch (JWSInputException ignore) { diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java index 7cfc7a698b..75f0cb8f2a 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java @@ -21,6 +21,7 @@ import org.jboss.logging.Logger; import org.keycloak.AuthorizationContext; import org.keycloak.KeycloakSecurityContext; import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.common.VerificationException; import org.keycloak.common.util.Time; import org.keycloak.representations.AccessToken; @@ -130,7 +131,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext String tokenString = response.getToken(); AccessToken token = null; try { - token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); + token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); log.debug("Token Verification succeeded!"); } catch (VerificationException e) { log.error("failed verification of token"); 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 f1be944353..1c900b8657 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 @@ -21,6 +21,7 @@ import org.jboss.logging.Logger; import org.keycloak.RSATokenVerifier; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.OIDCHttpFacade; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.authorization.client.AuthorizationDeniedException; import org.keycloak.authorization.client.AuthzClient; @@ -120,7 +121,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer { AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest); if (authzResponse != null) { - return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl()); + return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment); } return null; @@ -130,7 +131,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer { if (token.getAuthorization() == null) { EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId()); - return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl()); + return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment); } else { EntitlementRequest request = new EntitlementRequest(); PermissionRequest permissionRequest = new PermissionRequest(); @@ -139,7 +140,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer { permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes())); request.addPermission(permissionRequest); EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getClientId(), request); - return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl()); + return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment); } } } catch (AuthorizationDeniedException e) { diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java index 51be55137f..75086e7804 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java @@ -43,6 +43,7 @@ import org.keycloak.adapters.AdapterUtils; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.RefreshableKeycloakSecurityContext; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.common.VerificationException; import org.keycloak.common.util.FindFile; import org.keycloak.representations.AccessToken; @@ -88,9 +89,6 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule { try { InputStream is = FindFile.findFile(keycloakConfigFile); KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is); - if (kd.getRealmKey() == null) { - new AdapterDeploymentContext().resolveRealmKey(kd); - } return kd; } catch (RuntimeException e) { getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e); @@ -190,7 +188,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule { protected Auth bearerAuth(String tokenString) throws VerificationException { - AccessToken token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); + AccessToken token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); boolean verifyCaller; if (deployment.isUseResourceRoleMappings()) { diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java new file mode 100644 index 0000000000..c69ee38d65 --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/AdapterRSATokenVerifier.java @@ -0,0 +1,64 @@ +/* + * 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.adapters.rotation; + +import java.security.PublicKey; + +import org.jboss.logging.Logger; +import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.common.VerificationException; +import org.keycloak.jose.jws.JWSInput; +import org.keycloak.representations.AccessToken; + +/** + * @author Marek Posolda + */ +public class AdapterRSATokenVerifier { + + private static final Logger log = Logger.getLogger(AdapterRSATokenVerifier.class); + + public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException { + return verifyToken(tokenString, deployment, true, true); + } + + + public static PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) throws VerificationException { + PublicKeyLocator pkLocator = deployment.getPublicKeyLocator(); + + PublicKey publicKey = pkLocator.getPublicKey(input, deployment); + if (publicKey == null) { + log.errorf("Didn't find publicKey for kid: %s", input.getHeader().getKeyId()); + throw new VerificationException("Didn't find publicKey for specified kid"); + } + + return publicKey; + } + + public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException { + JWSInput input; + try { + input = new JWSInput(tokenString); + } catch (Exception e) { + throw new VerificationException("Couldn't parse token", e); + } + + PublicKey publicKey = getPublicKey(input, deployment); + return RSATokenVerifier.verifyToken(input, publicKey, deployment.getRealmInfoUrl(), checkActive, checkTokenType); + } +} diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.java new file mode 100644 index 0000000000..40fb71ad77 --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/HardcodedPublicKeyLocator.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.adapters.rotation; + +import java.security.PublicKey; + +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.jose.jws.JWSInput; + +/** + * @author Marek Posolda + */ +public class HardcodedPublicKeyLocator implements PublicKeyLocator { + + private PublicKey publicKey; + + public HardcodedPublicKeyLocator(PublicKey publicKey) { + this.publicKey = publicKey; + } + + @Override + public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) { + return publicKey; + } +} diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java new file mode 100644 index 0000000000..500392338c --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/JWKPublicKeyLocator.java @@ -0,0 +1,107 @@ +/* + * 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.adapters.rotation; + +import java.security.PublicKey; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.http.client.methods.HttpGet; +import org.jboss.logging.Logger; +import org.keycloak.adapters.HttpAdapterUtils; +import org.keycloak.adapters.HttpClientAdapterException; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.common.util.Time; +import org.keycloak.jose.jwk.JSONWebKeySet; +import org.keycloak.jose.jwk.JWK; +import org.keycloak.jose.jws.JWSInput; +import org.keycloak.util.JWKSUtils; + +/** + * When needed, publicKeys are downloaded by sending request to realm's jwks_url + * + * @author Marek Posolda + */ +public class JWKPublicKeyLocator implements PublicKeyLocator { + + private static final Logger log = Logger.getLogger(JWKPublicKeyLocator.class); + + private Map currentKeys = new ConcurrentHashMap<>(); + + private volatile int lastRequestTime = 0; + + @Override + public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) { + String kid = input.getHeader().getKeyId(); + return getPublicKey(kid, deployment); + } + + + private PublicKey getPublicKey(String kid, KeycloakDeployment deployment) { + int minTimeBetweenRequests = deployment.getMinTimeBetweenJwksRequests(); + + // Check if key is in cache. + PublicKey publicKey = currentKeys.get(kid); + if (publicKey != null) { + return publicKey; + } + + int currentTime = Time.currentTime(); + + // Check if we are allowed to send request + if (currentTime > lastRequestTime + minTimeBetweenRequests) { + synchronized (this) { + currentTime = Time.currentTime(); + if (currentTime > lastRequestTime + minTimeBetweenRequests) { + sendRequest(deployment); + lastRequestTime = currentTime; + } else { + // TODO: debug + log.infof("Won't send request to realm jwks url. Last request time was %d", lastRequestTime); + } + } + } + + return currentKeys.get(kid); + + } + + + private void sendRequest(KeycloakDeployment deployment) { + // Send the request + // TODO: trace or remove? + log.infof("Going to send request to retrieve new set of realm public keys for client %s", deployment.getResourceName()); + + HttpGet getMethod = new HttpGet(deployment.getJwksUrl()); + try { + JSONWebKeySet jwks = HttpAdapterUtils.sendJsonHttpRequest(deployment, getMethod, JSONWebKeySet.class); + + Map publicKeys = JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG); + + // TODO: Debug with condition + log.infof("Realm public keys successfully retrieved for client %s. New kids: %s", deployment.getResourceName(), publicKeys.keySet().toString()); + + // Update current keys + currentKeys.clear(); + currentKeys.putAll(publicKeys); + + } catch (HttpClientAdapterException e) { + log.error("Error when sending request to retrieve realm keys", e); + } + } +} diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java new file mode 100644 index 0000000000..bda80dc3a3 --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/rotation/PublicKeyLocator.java @@ -0,0 +1,37 @@ +/* + * 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.adapters.rotation; + +import java.security.PublicKey; + +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.jose.jws.JWSInput; + +/** + * @author Marek Posolda + */ +public interface PublicKeyLocator { + + /** + * @param input + * @param deployment + * @return publicKey, which should be used for verify signature on given "input" + */ + PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment); + +} diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java index 4be511023c..d1ce748f63 100644 --- a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java +++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java @@ -21,6 +21,8 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.junit.Test; import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider; import org.keycloak.adapters.authentication.JWTClientCredentialsProvider; +import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator; +import org.keycloak.adapters.rotation.JWKPublicKeyLocator; import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.SslRequired; import org.keycloak.enums.TokenStore; @@ -39,7 +41,11 @@ public class KeycloakDeploymentBuilderTest { KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak.json")); assertEquals("demo", deployment.getRealm()); assertEquals("customer-portal", deployment.getResourceName()); - assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"), deployment.getRealmKey()); + + assertTrue(deployment.getPublicKeyLocator() instanceof HardcodedPublicKeyLocator); + assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"), + deployment.getPublicKeyLocator().getPublicKey(null, deployment)); + assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/auth", deployment.getAuthUrl().build().toString()); assertEquals(SslRequired.EXTERNAL, deployment.getSslRequired()); assertTrue(deployment.isUseResourceRoleMappings()); @@ -62,12 +68,16 @@ public class KeycloakDeploymentBuilderTest { assertEquals(TokenStore.COOKIE, deployment.getTokenStore()); assertEquals("email", deployment.getPrincipalAttribute()); assertEquals(10, deployment.getTokenMinimumTimeToLive()); + assertEquals(20, deployment.getMinTimeBetweenJwksRequests()); } @Test public void loadNoClientCredentials() throws Exception { KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-no-credentials.json")); assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId()); + + assertTrue(deployment.getPublicKeyLocator() instanceof JWKPublicKeyLocator); + assertEquals(10, deployment.getMinTimeBetweenJwksRequests()); } @Test diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json b/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json index 5f223ac75f..a3c4026c34 100644 --- a/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json +++ b/adapters/oidc/adapter-core/src/test/resources/keycloak-no-credentials.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "customer-portal", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "https://localhost:8443/auth", "public-client": true, "expose-token": true diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak.json b/adapters/oidc/adapter-core/src/test/resources/keycloak.json index 7bf269f2c1..a8afd22cf2 100644 --- a/adapters/oidc/adapter-core/src/test/resources/keycloak.json +++ b/adapters/oidc/adapter-core/src/test/resources/keycloak.json @@ -29,5 +29,6 @@ "register-node-period": 1000, "token-store": "cookie", "principal-attribute": "email", - "token-minimum-time-to-live": 10 + "token-minimum-time-to-live": 10, + "min-time-between-jwks-requests": 20 } \ No newline at end of file diff --git a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java index fc0e47fa50..c52a44af65 100644 --- a/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java +++ b/adapters/oidc/installed/src/main/java/org/keycloak/adapters/installed/KeycloakInstalled.java @@ -20,6 +20,7 @@ package org.keycloak.adapters.installed; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.common.VerificationException; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeploymentBuilder; @@ -213,7 +214,7 @@ public class KeycloakInstalled { refreshToken = tokenResponse.getRefreshToken(); idTokenString = tokenResponse.getIdToken(); - token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); + token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment); if (idTokenString != null) { try { JWSInput input = new JWSInput(idTokenString); diff --git a/core/src/main/java/org/keycloak/RSATokenVerifier.java b/core/src/main/java/org/keycloak/RSATokenVerifier.java index 27a430102b..562261de5e 100755 --- a/core/src/main/java/org/keycloak/RSATokenVerifier.java +++ b/core/src/main/java/org/keycloak/RSATokenVerifier.java @@ -38,6 +38,12 @@ public class RSATokenVerifier { public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException { AccessToken token = toAccessToken(tokenString, realmKey); + tokenVerifications(token, realmUrl, checkActive, checkTokenType); + + return token; + } + + private static void tokenVerifications(AccessToken token, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException { String user = token.getSubject(); if (user == null) { throw new VerificationException("Token user was null."); @@ -60,9 +66,9 @@ public class RSATokenVerifier { throw new VerificationException("Token is not active."); } - return token; } + public static AccessToken toAccessToken(String tokenString, PublicKey realmKey) throws VerificationException { JWSInput input; try { @@ -81,6 +87,23 @@ public class RSATokenVerifier { return token; } + + public static AccessToken verifyToken(JWSInput input, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException { + if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature."); + + AccessToken token; + try { + token = input.readJsonContent(AccessToken.class); + } catch (JWSInputException e) { + throw new VerificationException("Couldn't parse token signature", e); + } + + tokenVerifications(token, realmUrl, checkActive, checkTokenType); + + return token; + } + + private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException { try { return RSAProvider.verify(input, realmKey); diff --git a/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java b/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java index a21f26284b..36b4f1d2d6 100755 --- a/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java +++ b/core/src/main/java/org/keycloak/constants/ServiceUrlConstants.java @@ -30,5 +30,6 @@ public interface ServiceUrlConstants { public static final String REALM_INFO_PATH = "/realms/{realm-name}"; public static final String CLIENTS_MANAGEMENT_REGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/register-node"; public static final String CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/unregister-node"; + public static final String JWKS_URL = "/realms/{realm-name}/protocol/openid-connect/certs"; } diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java index 7a31f72b6a..a20d253241 100755 --- a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java +++ b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import org.keycloak.common.util.Base64Url; import org.keycloak.util.JsonSerialization; -import java.io.InputStream; import java.math.BigInteger; import java.security.KeyFactory; import java.security.PublicKey; @@ -66,8 +65,8 @@ public class JWKParser { } public PublicKey toPublicKey() { - String algorithm = jwk.getKeyType(); - if (isAlgorithmSupported(algorithm)) { + String keyType = jwk.getKeyType(); + if (isKeyTypeSupported(keyType)) { BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString())); BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString())); @@ -77,12 +76,12 @@ public class JWKParser { throw new RuntimeException(e); } } else { - throw new RuntimeException("Unsupported algorithm " + algorithm); + throw new RuntimeException("Unsupported keyType " + keyType); } } - public boolean isAlgorithmSupported(String algorithm) { - return RSAPublicJWK.RSA.equals(algorithm); + public boolean isKeyTypeSupported(String keyType) { + return RSAPublicJWK.RSA.equals(keyType); } } diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java index 91fb5f0e9c..c4818b45c7 100755 --- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java +++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java @@ -36,7 +36,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; "client-keystore", "client-keystore-password", "client-key-password", "always-refresh-token", "register-node-at-startup", "register-node-period", "token-store", "principal-attribute", - "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", + "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests", "policy-enforcer" }) public class AdapterConfig extends BaseAdapterConfig { @@ -71,6 +71,8 @@ public class AdapterConfig extends BaseAdapterConfig { protected Boolean turnOffChangeSessionIdOnLogin; @JsonProperty("token-minimum-time-to-live") protected int tokenMinimumTimeToLive = 0; + @JsonProperty("min-time-between-jwks-requests") + protected int minTimeBetweenJwksRequests = 10; @JsonProperty("policy-enforcer") protected PolicyEnforcerConfig policyEnforcerConfig; @@ -216,4 +218,11 @@ public class AdapterConfig extends BaseAdapterConfig { this.tokenMinimumTimeToLive = tokenMinimumTimeToLive; } + public int getMinTimeBetweenJwksRequests() { + return minTimeBetweenJwksRequests; + } + + public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) { + this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests; + } } diff --git a/core/src/main/java/org/keycloak/util/JWKSUtils.java b/core/src/main/java/org/keycloak/util/JWKSUtils.java new file mode 100644 index 0000000000..72ffe91c86 --- /dev/null +++ b/core/src/main/java/org/keycloak/util/JWKSUtils.java @@ -0,0 +1,45 @@ +/* + * 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.util; + +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; + +import org.keycloak.jose.jwk.JSONWebKeySet; +import org.keycloak.jose.jwk.JWK; +import org.keycloak.jose.jwk.JWKParser; + +/** + * @author Marek Posolda + */ +public class JWKSUtils { + + public static Map getKeysForUse(JSONWebKeySet keySet, JWK.Use requestedUse) { + Map result = new HashMap<>(); + + for (JWK jwk : keySet.getKeys()) { + JWKParser parser = JWKParser.create(jwk); + if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) { + result.put(jwk.getKeyId(), parser.toPublicKey()); + } + } + + return result; + } +} diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json index a492837299..b5a1bbf797 100644 --- a/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/hello-world-authz-service/src/main/webapp/WEB-INF/keycloak.json @@ -1,6 +1,5 @@ { "realm": "hello-world-authz", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required": "external", "resource": "hello-world-authz-service", diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json index c1dee24574..affafdd82c 100644 --- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json +++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json @@ -1,6 +1,5 @@ { "realm": "photoz", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "resource" : "photoz-html5-client", diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json index 6849d0771b..9e06730fe3 100644 --- a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json @@ -1,6 +1,5 @@ { "realm": "photoz", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required": "external", "resource": "photoz-restful-api", diff --git a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json index eaffea8d19..f6b9c90927 100644 --- a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json @@ -1,6 +1,5 @@ { "realm": "servlet-authz", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "resource" : "servlet-authz-app", diff --git a/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json b/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json index 4502199d70..9da7ed42e5 100644 --- a/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/basic-auth/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "basic-auth", "resource" : "basic-auth-service", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "enable-basic-auth" : "true", diff --git a/examples/broker/facebook-authentication/src/main/webapp/keycloak.json b/examples/broker/facebook-authentication/src/main/webapp/keycloak.json index 55446affdb..17743f663e 100644 --- a/examples/broker/facebook-authentication/src/main/webapp/keycloak.json +++ b/examples/broker/facebook-authentication/src/main/webapp/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "facebook-identity-provider-realm", "resource" : "facebook-authentication", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "public-client" : true diff --git a/examples/broker/google-authentication/src/main/webapp/keycloak.json b/examples/broker/google-authentication/src/main/webapp/keycloak.json index 93c25b4497..f05da2ff5e 100644 --- a/examples/broker/google-authentication/src/main/webapp/keycloak.json +++ b/examples/broker/google-authentication/src/main/webapp/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "google-identity-provider-realm", "resource" : "google-authentication", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "public-client" : true diff --git a/examples/broker/twitter-authentication/src/main/webapp/keycloak.json b/examples/broker/twitter-authentication/src/main/webapp/keycloak.json index 7243636390..2f745149d9 100644 --- a/examples/broker/twitter-authentication/src/main/webapp/keycloak.json +++ b/examples/broker/twitter-authentication/src/main/webapp/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "twitter-identity-provider-realm", "resource" : "twitter-authentication", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "public-client" : true diff --git a/examples/cors/angular-product-app/src/main/webapp/keycloak.json b/examples/cors/angular-product-app/src/main/webapp/keycloak.json index 40b35a1953..d68545945d 100755 --- a/examples/cors/angular-product-app/src/main/webapp/keycloak.json +++ b/examples/cors/angular-product-app/src/main/webapp/keycloak.json @@ -1,6 +1,5 @@ { "realm" : "cors", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost-auth:8080/auth", "ssl-required" : "external", "resource" : "angular-cors-product", diff --git a/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json b/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json index 265049f22c..61da4087a3 100755 --- a/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/cors/database-service/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "cors", "resource" : "cors-database-service", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost-auth:8080/auth", "bearer-only" : true, "ssl-required": "external", diff --git a/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json b/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json index 72ecb5be56..174053e4a1 100755 --- a/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json +++ b/examples/demo-template/angular-product-app/src/main/webapp/keycloak.json @@ -1,6 +1,5 @@ { "realm" : "demo", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "/auth", "ssl-required" : "external", "resource" : "angular-product", diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json b/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json index ddd1f2ec81..87a2ad64ef 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json +++ b/examples/demo-template/angular2-product-app/src/main/webapp/keycloak.json @@ -1,6 +1,5 @@ { "realm": "demo", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required": "external", "resource": "angular2-product", diff --git a/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json b/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json index 51c8775c99..1c61433813 100644 --- a/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json +++ b/examples/demo-template/customer-app-cli/src/main/resources/META-INF/keycloak.json @@ -1,6 +1,5 @@ { "realm" : "demo", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "resource" : "customer-portal-cli", diff --git a/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json index 14e56f543b..3766330ece 100755 --- a/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/demo-template/customer-app-filter/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "customer-portal-filter", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "expose-token": true, diff --git a/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json b/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json index 224c70b76f..4a60ffacd0 100644 --- a/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json +++ b/examples/demo-template/customer-app-js/src/main/webapp/keycloak.json @@ -1,6 +1,5 @@ { "realm" : "demo", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "/auth", "ssl-required" : "external", "resource" : "customer-portal-js", diff --git a/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json index c2241b3e91..20619097b5 100755 --- a/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/demo-template/customer-app/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "customer-portal", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "expose-token": true, diff --git a/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json index cb938548dc..72e4903bc6 100755 --- a/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/demo-template/database-service/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "demo", "resource" : "database-service", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "bearer-only" : true, "ssl-required" : "external" diff --git a/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json index dff976cf0e..600dcfa8a8 100644 --- a/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/demo-template/offline-access-app/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "offline-access-portal", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json b/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json index 0a86c041c7..109270101f 100755 --- a/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/demo-template/product-app/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "demo", "resource" : "product-portal", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "/auth", "ssl-required" : "external", "credentials" : { diff --git a/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java b/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java index e4d6a4033a..72ffd4959b 100644 --- a/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java +++ b/examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java @@ -41,6 +41,7 @@ import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.keycloak.OAuth2Constants; import org.keycloak.RSATokenVerifier; +import org.keycloak.adapters.rotation.AdapterRSATokenVerifier; import org.keycloak.common.VerificationException; import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeploymentBuilder; @@ -163,7 +164,7 @@ public abstract class ProductServiceAccountServlet extends HttpServlet { private void setTokens(HttpServletRequest req, KeycloakDeployment deployment, AccessTokenResponse tokenResponse) throws IOException, VerificationException { String token = tokenResponse.getToken(); String refreshToken = tokenResponse.getRefreshToken(); - AccessToken tokenParsed = RSATokenVerifier.verifyToken(token, deployment.getRealmKey(), deployment.getRealmInfoUrl()); + AccessToken tokenParsed = AdapterRSATokenVerifier.verifyToken(token, deployment); req.getSession().setAttribute(TOKEN, token); req.getSession().setAttribute(REFRESH_TOKEN, refreshToken); req.getSession().setAttribute(TOKEN_PARSED, tokenParsed); diff --git a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json index 7eec22a6c3..1a9322d96a 100644 --- a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json +++ b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json @@ -1,6 +1,5 @@ { "realm" : "demo", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "resource" : "product-sa-client", diff --git a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json index 3e90c34a30..3c99da7b42 100644 --- a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json +++ b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json @@ -1,6 +1,5 @@ { "realm" : "demo", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "resource" : "product-sa-client-jwt-auth", diff --git a/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml index a4796cc152..56550d681a 100644 --- a/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/examples/fuse/camel/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -26,7 +26,6 @@ - diff --git a/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json b/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json index b5d6b30d24..c7f61f68e3 100755 --- a/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/fuse/customer-app-fuse/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "customer-portal", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json b/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json index f5d7e1a989..a8c4b752e1 100644 --- a/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json +++ b/examples/fuse/cxf-jaxrs/src/main/resources/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "builtin-cxf-app", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml b/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml index 2d15ae017b..8a808c623c 100644 --- a/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml +++ b/examples/fuse/cxf-jaxws/src/main/resources/META-INF/spring/beans.xml @@ -32,7 +32,6 @@ - diff --git a/examples/fuse/external-config/external-config-keycloak.json b/examples/fuse/external-config/external-config-keycloak.json index 920e99a677..469da82a17 100644 --- a/examples/fuse/external-config/external-config-keycloak.json +++ b/examples/fuse/external-config/external-config-keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "external-config", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/fuse/fuse-admin/keycloak-direct-access.json b/examples/fuse/fuse-admin/keycloak-direct-access.json index 8e5ac0ff24..2441134231 100644 --- a/examples/fuse/fuse-admin/keycloak-direct-access.json +++ b/examples/fuse/fuse-admin/keycloak-direct-access.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "ssh-jmx-admin-client", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "ssl-required" : "external", "auth-server-url" : "http://localhost:8080/auth", "credentials": { diff --git a/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json b/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json index 2a52d247f7..e90433ac51 100644 --- a/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json +++ b/examples/fuse/product-app-fuse/src/main/resources/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm": "demo", "resource": "product-portal", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/js-console/src/main/webapp/keycloak.json b/examples/js-console/src/main/webapp/keycloak.json index c0c04d5aee..cc4bab3394 100644 --- a/examples/js-console/src/main/webapp/keycloak.json +++ b/examples/js-console/src/main/webapp/keycloak.json @@ -1,6 +1,5 @@ { "realm" : "example", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "/auth", "ssl-required" : "external", "resource" : "js-console", diff --git a/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json b/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json index db1223c6b2..7e9d91a7da 100644 --- a/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/kerberos/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "kerberos-demo", "resource" : "kerberos-app", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/ldap/src/main/webapp/WEB-INF/keycloak.json b/examples/ldap/src/main/webapp/WEB-INF/keycloak.json index 84e1129a88..f43107b054 100644 --- a/examples/ldap/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/ldap/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,6 @@ { "realm" : "ldap-demo", "resource" : "ldap-app", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "/auth", "ssl-required" : "external", "credentials": { diff --git a/examples/multi-tenant/src/main/resources/tenant1-keycloak.json b/examples/multi-tenant/src/main/resources/tenant1-keycloak.json index 57be2774e7..34c17737b3 100644 --- a/examples/multi-tenant/src/main/resources/tenant1-keycloak.json +++ b/examples/multi-tenant/src/main/resources/tenant1-keycloak.json @@ -1,7 +1,6 @@ { "realm" : "tenant1", "resource" : "multi-tenant", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "credentials" : { diff --git a/examples/multi-tenant/src/main/resources/tenant2-keycloak.json b/examples/multi-tenant/src/main/resources/tenant2-keycloak.json index 4f221dc66d..5877082e35 100644 --- a/examples/multi-tenant/src/main/resources/tenant2-keycloak.json +++ b/examples/multi-tenant/src/main/resources/tenant2-keycloak.json @@ -1,7 +1,6 @@ { "realm" : "tenant2", "resource" : "multi-tenant", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQAB", "auth-server-url" : "http://localhost:8080/auth", "ssl-required" : "external", "credentials" : { diff --git a/server-spi/src/main/java/org/keycloak/models/Constants.java b/server-spi/src/main/java/org/keycloak/models/Constants.java index 42982f6ace..edc9567e37 100755 --- a/server-spi/src/main/java/org/keycloak/models/Constants.java +++ b/server-spi/src/main/java/org/keycloak/models/Constants.java @@ -51,4 +51,7 @@ public interface Constants { // Prefix for user attributes used in various "context"data maps String USER_ATTRIBUTES_PREFIX = "user.attributes."; + + // Indication to admin-rest-endpoint that realm keys should be re-generated + String GENERATE = "GENERATE"; } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index ac285a8fe1..51b1f4e40f 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -820,7 +820,7 @@ public class RepresentationToModel { realm.setUserFederationProviders(providerModels); } - if ("GENERATE".equals(rep.getPublicKey())) { + if (Constants.GENERATE.equals(rep.getPublicKey())) { KeycloakModelUtils.generateRealmKeys(realm); } else { if (rep.getPrivateKey() != null && rep.getPublicKey() != null) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java index 6fda3f0394..79a3919be5 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java @@ -51,7 +51,6 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide ClientManager.InstallationAdapterConfig rep = new ClientManager.InstallationAdapterConfig(); rep.setAuthServerUrl(baseUri.toString()); rep.setRealm(realm.getName()); - rep.setRealmKey(realm.getPublicKeyPem()); rep.setSslRequired(realm.getSslRequired().name().toLowerCase()); if (client.isPublicClient() && !client.isBearerOnly()) rep.setPublicClient(true); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java index 33ab67787d..d0bc939eb2 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java @@ -40,7 +40,6 @@ public class KeycloakOIDCJbossSubsystemClientInstallation implements ClientInsta StringBuffer buffer = new StringBuffer(); buffer.append("\n"); buffer.append(" ").append(realm.getName()).append("\n"); - buffer.append(" ").append(realm.getPublicKeyPem()).append("\n"); buffer.append(" ").append(baseUri.toString()).append("\n"); if (client.isBearerOnly()){ buffer.append(" true\n"); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java index d8f7fe72de..62d2c58039 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java @@ -30,6 +30,8 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.util.JsonSerialization; /** + * TODO: Merge with JWKSUtils from keycloak-core? + * * @author Marek Posolda */ public class JWKSUtils { @@ -44,7 +46,7 @@ public class JWKSUtils { public static PublicKey getKeyForUse(JSONWebKeySet keySet, JWK.Use requestedUse) { for (JWK jwk : keySet.getKeys()) { JWKParser parser = JWKParser.create(jwk); - if (parser.getJwk().getPublicKeyUse().equals(requestedUse.asString()) && parser.isAlgorithmSupported(jwk.getKeyType())) { + if (parser.getJwk().getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) { return parser.toPublicKey(); } } diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java index 7bdf5b1889..3de62db240 100644 --- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java @@ -265,7 +265,6 @@ public class ClientManager { InstallationAdapterConfig rep = new InstallationAdapterConfig(); rep.setAuthServerUrl(baseUri.toString()); rep.setRealm(realmModel.getName()); - rep.setRealmKey(realmModel.getPublicKeyPem()); rep.setSslRequired(realmModel.getSslRequired().name().toLowerCase()); if (clientModel.isPublicClient() && !clientModel.isBearerOnly()) rep.setPublicClient(true); @@ -286,7 +285,6 @@ public class ClientManager { StringBuffer buffer = new StringBuffer(); buffer.append("\n"); buffer.append(" ").append(realmModel.getName()).append("\n"); - buffer.append(" ").append(realmModel.getPublicKeyPem()).append("\n"); buffer.append(" ").append(baseUri.toString()).append("\n"); if (clientModel.isBearerOnly()){ buffer.append(" true\n"); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 7d4922340d..1caa5fb207 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -35,6 +35,7 @@ import org.keycloak.events.admin.ResourceType; import org.keycloak.exportimport.ClientDescriptionConverter; import org.keycloak.exportimport.ClientDescriptionConverterFactory; import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; @@ -281,7 +282,7 @@ public class RealmAdminResource { logger.debug("updating realm: " + realm.getName()); try { - if (!"GENERATE".equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) { + if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) { try { KeyPairVerifier.verify(rep.getPrivateKey(), rep.getPublicKey()); } catch (VerificationException e) { @@ -289,7 +290,7 @@ public class RealmAdminResource { } } - if (!"GENERATE".equals(rep.getPublicKey()) && (rep.getCertificate() != null)) { + if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getCertificate() != null)) { try { X509Certificate cert = PemUtils.decodeCertificate(rep.getCertificate()); if (cert == null) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java index c282dff966..aad5bb889d 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/filter/AdapterActionsFilter.java @@ -27,8 +27,13 @@ import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.jboss.logging.Logger; +import org.keycloak.adapters.AdapterDeploymentContext; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.rotation.JWKPublicKeyLocator; import org.keycloak.common.util.Time; /** @@ -38,6 +43,11 @@ import org.keycloak.common.util.Time; */ public class AdapterActionsFilter implements Filter { + public static final String TIME_OFFSET_PARAM = "timeOffset"; + public static final String RESET_PUBLIC_KEY_PARAM = "resetPublicKey"; + + private static final Logger log = Logger.getLogger(AdapterActionsFilter.class); + @Override public void init(FilterConfig filterConfig) throws ServletException { @@ -45,16 +55,28 @@ public class AdapterActionsFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest servletReq = (HttpServletRequest) request; HttpServletResponse servletResp = (HttpServletResponse) response; //Accept timeOffset as argument to enforce timeouts - String timeOffsetParam = request.getParameter("timeOffset"); - if (timeOffsetParam != null && !timeOffsetParam.isEmpty()) { - Time.setOffset(Integer.parseInt(timeOffsetParam)); - } + String timeOffsetParam = request.getParameter(TIME_OFFSET_PARAM); + String resetPublicKey = request.getParameter(RESET_PUBLIC_KEY_PARAM); - // Continue request - chain.doFilter(request, response); + if (timeOffsetParam != null && !timeOffsetParam.isEmpty()) { + int timeOffset = Integer.parseInt(timeOffsetParam); + log.infof("Time offset updated to %d for application %s", timeOffset, servletReq.getRequestURI()); + Time.setOffset(timeOffset); + writeResponse(servletResp, "Offset set successfully"); + } else if (resetPublicKey != null && !resetPublicKey.isEmpty()) { + AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) request.getServletContext().getAttribute(AdapterDeploymentContext.class.getName()); + KeycloakDeployment deployment = deploymentContext.resolveDeployment(null); + deployment.setPublicKeyLocator(new JWKPublicKeyLocator()); + log.infof("Restarted publicKey locator for application %s", servletReq.getRequestURI()); + writeResponse(servletResp, "PublicKeyLocator restarted successfully"); + } else { + // Continue request + chain.doFilter(request, response); + } } @@ -64,8 +86,9 @@ public class AdapterActionsFilter implements Filter { } private void writeResponse(HttpServletResponse response, String responseText) throws IOException { + response.setContentType("text/html"); PrintWriter writer = response.getWriter(); - writer.println(responseText); + writer.println("" + responseText + ""); writer.flush(); } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java index d370dd049b..6cb1f37d5b 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AbstractShowTokensPage.java @@ -23,6 +23,7 @@ import org.keycloak.representations.AccessToken; import org.keycloak.representations.RefreshToken; import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl; import org.keycloak.util.JsonSerialization; +import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -43,6 +44,8 @@ public abstract class AbstractShowTokensPage extends AbstractPageWithInjectedUrl return JsonSerialization.readValue(accessToken.getText(), AccessToken.class); } catch (IOException e) { e.printStackTrace(); + } catch (NoSuchElementException nsee) { + log.warn("No accessToken element found on the page"); } return null; @@ -53,7 +56,10 @@ public abstract class AbstractShowTokensPage extends AbstractPageWithInjectedUrl return JsonSerialization.readValue(refreshToken.getText(), RefreshToken.class); } catch (IOException e) { e.printStackTrace(); + } catch (NoSuchElementException nsee) { + log.warn("No idToken element found on the page"); } + return null; } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java index 9cd7625cc1..4af2988d1b 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java @@ -49,8 +49,6 @@ import static org.keycloak.testsuite.util.IOUtil.*; */ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { - public static final String REALM_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; - protected final Logger log = org.jboss.logging.Logger.getLogger(this.getClass()); private final boolean authServerSslRequired = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required")); @@ -129,7 +127,6 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor { // ac.setRealmKey(null); // TODO verify if realm key is required for relative scneario } else { adapterConfig.setAuthServerUrl(getAuthServerContextRoot() + "/auth"); - adapterConfig.setRealmKey(REALM_KEY); } if ("true".equals(System.getProperty("app.server.ssl.required"))) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java index 127c863fa8..a40275f727 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java @@ -22,6 +22,7 @@ import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter; import org.keycloak.testsuite.util.WaitUtils; import org.openqa.selenium.By; @@ -113,7 +114,7 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest { protected void setAdapterAndServerTimeOffset(int timeOffset, String servletUri) { setTimeOffset(timeOffset); String timeOffsetUri = UriBuilder.fromUri(servletUri) - .queryParam("timeOffset", timeOffset) + .queryParam(AdapterActionsFilter.TIME_OFFSET_PARAM, timeOffset) .build().toString(); driver.navigate().to(timeOffsetUri); 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 083867f514..02adb84429 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 @@ -28,6 +28,7 @@ import org.keycloak.OAuth2Constants; import org.keycloak.common.Version; import org.keycloak.common.util.Time; import org.keycloak.constants.AdapterConstants; +import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.representations.AccessToken; @@ -36,8 +37,11 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest; import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter; import org.keycloak.testsuite.adapter.page.*; +import org.keycloak.testsuite.util.URLAssert; import org.keycloak.testsuite.util.URLUtils; +import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.util.BasicAuthHelper; +import org.openqa.selenium.By; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; @@ -46,6 +50,8 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Form; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; + import java.net.URI; import java.util.List; import java.util.Map; @@ -166,6 +172,59 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd client.close(); } + @Test + public void testRealmKeyRotationWithNewKeyDownload() throws Exception { + // Login success first + tokenMinTTLPage.navigateTo(); + testRealmLoginPage.form().waitForUsernameInputPresent(); + assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + testRealmLoginPage.form().login("bburke@redhat.com", "password"); + assertCurrentUrlEquals(tokenMinTTLPage); + + AccessToken token = tokenMinTTLPage.getAccessToken(); + Assert.assertEquals("bburke@redhat.com", token.getPreferredUsername()); + + // Logout + String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()) + .queryParam(OAuth2Constants.REDIRECT_URI, tokenMinTTLPage.toString()) + .build("demo").toString(); + driver.navigate().to(logoutUri); + + // Generate new realm key + RealmRepresentation realmRep = testRealmResource().toRepresentation(); + String oldPublicKey = realmRep.getPublicKey(); + String oldPrivateKey = realmRep.getPrivateKey(); + realmRep.setPublicKey(Constants.GENERATE); + testRealmResource().update(realmRep); + + // Try to login again. It should fail now + tokenMinTTLPage.navigateTo(); + testRealmLoginPage.form().waitForUsernameInputPresent(); + assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + testRealmLoginPage.form().login("bburke@redhat.com", "password"); + URLAssert.assertCurrentUrlStartsWith(driver, tokenMinTTLPage.getInjectedUrl().toString()); + assertNull(tokenMinTTLPage.getAccessToken()); + + String adapterActionsUrl = tokenMinTTLPage.toString() + "/unsecured/foo"; + setAdapterAndServerTimeOffset(300, adapterActionsUrl); + + // Try to login. Should work now due to realm key change + tokenMinTTLPage.navigateTo(); + assertCurrentUrlEquals(tokenMinTTLPage); + token = tokenMinTTLPage.getAccessToken(); + Assert.assertEquals("bburke@redhat.com", token.getPreferredUsername()); + driver.navigate().to(logoutUri); + + // Revert public keys change + String timeOffsetUri = UriBuilder.fromUri(adapterActionsUrl) + .queryParam(AdapterActionsFilter.RESET_PUBLIC_KEY_PARAM, "true") + .build().toString(); + driver.navigate().to(timeOffsetUri); + WaitUtils.waitUntilElement(By.tagName("body")).is().visible(); + + setAdapterAndServerTimeOffset(0, adapterActionsUrl); + } + @Test public void testLoginSSOAndLogout() { // test login to customer-portal which does a bearer request to customer-db @@ -444,6 +503,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd // Sets 5 minutes offset and assert access token will be still the same setAdapterAndServerTimeOffset(300, tokenMinTTLPage.toString()); + tokenMinTTLPage.navigateTo(); token = tokenMinTTLPage.getAccessToken(); int tokenIssued2 = token.getIssuedAt(); Assert.assertEquals(tokenIssued1, tokenIssued2); @@ -451,6 +511,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd // Sets 9 minutes offset and assert access token will be refreshed (accessTokenTimeout is 10 minutes, token-min-ttl is 2 minutes. Hence 8 minutes or more should be sufficient) setAdapterAndServerTimeOffset(540, tokenMinTTLPage.toString()); + tokenMinTTLPage.navigateTo(); token = tokenMinTTLPage.getAccessToken(); int tokenIssued3 = token.getIssuedAt(); Assert.assertTrue(tokenIssued3 > tokenIssued1); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java index fa6d0a8aed..b8c10b6dac 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java @@ -85,7 +85,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet String refreshTokenId = offlineTokenPage.getRefreshToken().getId(); setAdapterAndServerTimeOffset(9999); - + offlineTokenPage.navigateTo(); assertCurrentUrlStartsWith(offlineTokenPage); Assert.assertNotEquals(offlineTokenPage.getRefreshToken().getId(), refreshTokenId); Assert.assertNotEquals(offlineTokenPage.getAccessToken().getId(), accessTokenId); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java index fb03891e15..c7339ed12d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java @@ -21,11 +21,11 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; -import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; /** * Test getting the installation/configuration files for OIDC and SAML. @@ -81,7 +81,7 @@ public class InstallationTest extends AbstractClientTest { private void assertOidcInstallationConfig(String config) { RealmRepresentation realmRep = realmRep(); assertTrue(config.contains(realmRep.getId())); - assertTrue(config.contains(realmRep.getPublicKey())); + assertFalse(config.contains(realmRep.getPublicKey())); assertTrue(config.contains(authServerUrl())); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java index 0a4fe4bea7..818a84a992 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java @@ -35,14 +35,11 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes private ClientRepresentation client; private ClientRepresentation client2; private ClientRepresentation clientPublic; - private String publicKey; @Before public void before() throws Exception { super.before(); - publicKey = adminClient.realm(REALM_NAME).toRepresentation().getPublicKey(); - client = new ClientRepresentation(); client.setEnabled(true); client.setClientId("RegistrationAccessTokenTest"); @@ -92,7 +89,6 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes assertEquals(1, config.getCredentials().size()); assertEquals(client.getSecret(), config.getCredentials().get("secret")); - assertEquals(publicKey, config.getRealmKey()); assertEquals(client.getClientId(), config.getResource()); assertEquals(SslRequired.EXTERNAL.name().toLowerCase(), config.getSslRequired()); } @@ -132,7 +128,6 @@ public class AdapterInstallationConfigTest extends AbstractClientRegistrationTes assertEquals(0, config.getCredentials().size()); - assertEquals(publicKey, config.getRealmKey()); assertEquals(clientPublic.getClientId(), config.getResource()); assertEquals(SslRequired.EXTERNAL.name().toLowerCase(), config.getSslRequired()); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json index b75c1d5e65..32703f9a12 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json @@ -1,10 +1,10 @@ { "realm": "demo", "resource": "customer-portal", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", - "auth-server-url": "http://localhost:8180/auth", + "auth-server-url": "http://localhostt:8180/auth", "ssl-required" : "external", "expose-token": true, + "min-time-between-jwks-requests": 120, "credentials": { "secret": "password" } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json index a934e97ec2..eecc24bac9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json @@ -1,9 +1,9 @@ { "realm" : "demo", "resource" : "input-portal", - "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url" : "http://${my.host.name}:8180/auth", "ssl-required" : "external", + "min-time-between-jwks-requests": 120, "credentials" : { "secret": "password" } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json index 85eb9a72ab..b86e018279 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/keycloak.json @@ -1,11 +1,11 @@ { "realm": "demo", "resource": "token-min-ttl", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8180/auth", "ssl-required" : "external", "credentials": { "secret": "password" }, - "token-minimum-time-to-live": 120 + "token-minimum-time-to-live": 120, + "min-time-between-jwks-requests": 120 } \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml index f8110bc3cc..86d07f5ee6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/token-min-ttl/WEB-INF/web.xml @@ -68,6 +68,12 @@ /error.html + + + Unsecured + /unsecured/* + + KEYCLOAK From 04d03452bd29fe1fd976769df30238f2da22266e Mon Sep 17 00:00:00 2001 From: Martin Hardselius Date: Tue, 13 Sep 2016 09:18:45 +0200 Subject: [PATCH 32/53] KEYCLOAK-3422 support pairwise subject identifier in oidc --- .../java/org/keycloak/JsonParserTest.java | 30 ++-- .../models/utils/RepresentationToModel.java | 5 + .../ProtocolMapperConfigException.java | 21 +++ .../oidc/OIDCAdvancedConfigWrapper.java | 31 +++- .../oidc/OIDCLoginProtocolFactory.java | 25 +-- .../protocol/oidc/OIDCWellKnownProvider.java | 2 +- .../oidc/endpoints/UserInfoEndpoint.java | 15 +- .../mappers/AbstractPairwiseSubMapper.java | 125 ++++++++++++++ .../oidc/mappers/PairwiseSubMapperHelper.java | 47 ++++++ .../oidc/mappers/SHA265PairwiseSubMapper.java | 119 +++++++++++++ .../oidc/utils/PairwiseSubMapperUtils.java | 159 ++++++++++++++++++ .../utils/PairwiseSubMapperValidator.java | 113 +++++++++++++ .../protocol/oidc/utils/SubjectType.java | 13 ++ .../org/keycloak/services/ServicesLogger.java | 4 + .../AbstractClientRegistrationProvider.java | 60 ++++++- .../oidc/DescriptionConverter.java | 13 ++ .../resources/admin/ClientResource.java | 3 +- .../resources/admin/ClientsResource.java | 3 +- .../admin/ProtocolMappersResource.java | 22 +-- .../validation/PairwiseClientValidator.java | 41 +++++ .../validation/ValidationMessages.java | 5 +- .../org.keycloak.protocol.ProtocolMapper | 2 +- ...estApplicationResourceProviderFactory.java | 12 +- ...stingOIDCEndpointsApplicationResource.java | 41 +++-- .../TestApplicationResourceUrls.java | 10 +- .../TestOIDCEndpointsApplicationResource.java | 17 +- .../client/OIDCClientRegistrationTest.java | 137 ++++++++++++++- .../oidc/OIDCWellKnownProviderTest.java | 2 +- .../messages/admin-messages_en.properties | 6 + .../admin/messages/messages_en.properties | 9 +- .../admin/resources/js/controllers/clients.js | 6 + 31 files changed, 995 insertions(+), 103 deletions(-) create mode 100644 services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java create mode 100644 services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java create mode 100644 services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA265PairwiseSubMapper.java create mode 100644 services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java create mode 100644 services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperValidator.java create mode 100644 services/src/main/java/org/keycloak/protocol/oidc/utils/SubjectType.java create mode 100644 services/src/main/java/org/keycloak/services/validation/PairwiseClientValidator.java diff --git a/core/src/test/java/org/keycloak/JsonParserTest.java b/core/src/test/java/org/keycloak/JsonParserTest.java index e346fe6c05..965a13c863 100755 --- a/core/src/test/java/org/keycloak/JsonParserTest.java +++ b/core/src/test/java/org/keycloak/JsonParserTest.java @@ -17,17 +17,6 @@ package org.keycloak; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; import org.junit.Assert; import org.junit.Test; import org.keycloak.representations.IDToken; @@ -36,6 +25,13 @@ import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.util.JsonSerialization; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * @author Marek Posolda */ @@ -138,6 +134,18 @@ public class JsonParserTest { Assert.assertNull(clientRep.getJwks()); } + @Test + public void testReadOIDCClientRepWithPairwise() throws IOException { + String stringRep = "{\"subject_type\": \"pairwise\", \"jwks_uri\": \"https://op.certification.openid.net:60720/export/jwk_60720.json\", \"contacts\": [\"roland.hedberg@umu.se\"], \"application_type\": \"web\", \"grant_types\": [\"authorization_code\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60720/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60720/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"default_max_age\": 3600}"; + OIDCClientRepresentation clientRep = JsonSerialization.readValue(stringRep, OIDCClientRepresentation.class); + Assert.assertEquals("pairwise", clientRep.getSubjectType()); + Assert.assertTrue(clientRep.getRequireAuthTime()); + Assert.assertEquals(3600, clientRep.getDefaultMaxAge().intValue()); + Assert.assertEquals(1, clientRep.getRedirectUris().size()); + Assert.assertEquals("https://op.certification.openid.net:60720/authz_cb", clientRep.getRedirectUris().get(0)); + Assert.assertNull(clientRep.getJwks()); + } + @Test public void testReadOIDCClientRepWithJWKS() throws IOException { String stringRep = "{\"token_endpoint_auth_method\": \"private_key_jwt\", \"subject_type\": \"public\", \"jwks_uri\": null, \"jwks\": {\"keys\": [{\"use\": \"enc\", \"e\": \"AQAB\", \"d\": \"lZQv0_81euRLeUYU84Aodh0ar7ymDlzWP5NMra4Jklkb-lTBWkI-u4RMsPqGYyW3KHRoL_pgzZXSzQx8RLQfER6timRWb--NxMMKllZubByU3RqH2ooNuocJurspYiXkznPW1Mg9DaNXL0C2hwWPQHTeUVISpjgi5TCOV1ccWVyksFruya_VNL1CIByB-L0GL1rqbKv32cDwi2A3_jJa61cpzfLSIBe-lvCO6tuiDsR4qgJnUwnndQFwEI_4mLmD3iNWXrc8N-poleV8mBfMqBB5fWwy_ZTFCpmQ5AywGmctaik_wNhMoWuA4tUfY6_1LdKld-5Cjq55eLtuJjtvuQ\", \"n\": \"tx3Hjdbc19lkTiohbJrNj4jf2_90MEE122CRrwtFu6saDywKcG7Bi7w2FMAK2oTkuWfqhWRb5BEGmnSXdiCEPO5d-ytqP3nwlZXHaCDYscpP8bB4YLhvCn7R8Efw6gwQle24QPRP3lYoFeuUbDUq7GKA5SfaZUvWoeWjqyLIaBspKQsC26_Umx1E4IXLrMSL6nkRnrYcVZBAXrYCeTP1XtsV38_lZVJfHSaJaUy4PKaj3yvgm93EV2CXybPti7CCMXZ34VqqWiF64pQjZsPu3ZTr7ha_TTQq499-zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q\", \"q\": \"1q-r-bmMFbIzrLK2U3elksZq8CqUqZxlSfkGMZuVkxgYMS-e4FPzEp2iirG-eO11aa0cpMMoBdTnVdGJ_ZUR93w0lGf9XnQAJqxP7eOsrUoiW4VWlWH4WfOiLgpO-pFtyTz_JksYYaotc_Z3Zy-Szw6a39IDbuYGy1qL-15oQuc\", \"p\": \"2lrYPppRbcQWu4LtWN6tOVUrtCOPv1eLTKTc7q8vCMcem1Ox5QFB7KnUtNZ5Ni7wnZUeVDfimNebtjNsGvDSrpgIlo9dEnFBQsQIkzZ2SkoYfgmF8hNdi6P-BfRjdgYouy4c6xAnGDgSMTip1YnPRyvbMaoYT9E_tEcBW5wOeoc\", \"kid\": \"a0\", \"kty\": \"RSA\"}, {\"use\": \"sig\", \"e\": \"AQAB\", \"d\": \"DodXDEtkovWWGsMEXYy_nEEMCWyROMOebCnCv0ey3i4M4bh2dmwqgz0e-IKQAFlGiMkidGL1lNbq0uFS04FbuRAR06dYw1cbrNbDdhrWFxKTd1L5D9p-x-gW-YDWhpI8rUGRa76JXkOSxZUbg09_QyUd99CXAHh-FXi_ZkIKD8hK6FrAs68qhLf8MNkUv63DTduw7QgeFfQivdopePxyGuMk5n8veqwsUZsklQkhNlTYQqeM1xb2698ZQcNYkl0OssEsSJKRjXt-LRPowKrdvTuTo2p--HMI0pIEeFs7H_u5OW3jihjvoFClGPynHQhgWmQzlQRvWRXh6FhDVqFeGQ\", \"n\": \"zfZzttF7HmnTYwSMPdxKs5AoczbNS2mOPz-tN1g4ljqI_F1DG8cgQDcN_VDufxoFGRERo2FK6WEN41LhbGEyP6uL6wW6Cy29qE9QZcvY5mXrncndRSOkNcMizvuEJes_fMYrmP_lPiC6kWiqItTk9QBWqJfiYKhCx9cSDXsBmJXn3KWQCVHvj1ANFWW0CWLMKlWN-_NMNLIWJN_pEAocTZMzxSFBK1b5_5J8ZS7hfWRF6MQmjsJcz2jzA21SQZNpre3kwnTGRSwo05sAS-TyeadDqQPWgbqX69UzcGq5irhzN8cpZ_JaTk3Y_uV6owanTZLVvCgdjaAnMYeZhb0KFw\", \"q\": \"5E5XKK5njT-zzRqqTeY2tgP9PJBACeaH_xQRHZ_1ydE7tVd7HdgdaEHfQ1jvKIHFkknWWOBAY1mlBc4YDirLShB_voShD8C-Hx3nF5sne5fleVfU-sZy6Za4B2U75PcE62oZgCPauOTAEm9Xuvrt5aMMovyzR8ecJZhm9bw7naU\", \"p\": \"5vJHCSM3H3q4RltYzENC9RyZZV8EUmpkv9moyguT5t-BUGA-T4W_FGIxzOPXRWOckIplKkoDKhavUeNmTZMCUcue0nkICSJpvNE4Nb2p5PZk_QqSdQNvCasQtdojEG0AmfVD85SU551CYxJdLdDFOqyK2entpMr8lhokem189As\", \"kid\": \"a1\", \"kty\": \"RSA\"}, {\"d\": \"S4_OufhLBgXFMgIDMI1zlVe2uCExpcEAQ80J_lXfS8I\", \"use\": \"sig\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"DBdNyq30mXmUs_BIvKMqaTTNO7HDhCi0YiC8GciwNYk\", \"x\": \"cYwzBoyjRjxj334bRTqanONf7DUYK-6TgiuN0DixJAk\", \"kid\": \"a2\"}, {\"d\": \"33TnYgdJtWAiVosKqUnz0zSmvWTbsx5-6pceynW6Xck\", \"use\": \"enc\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"Cula95Eix1Ia77St3OULe6-UKWs5I06nmdfUzhXUQTs\", \"x\": \"wk8HBVxNNzj1gJBxPmmx9XYW1L61ObBGzxpRa6_OqWU\", \"kid\": \"a3\"}]}, \"application_type\": \"web\", \"contacts\": [\"roland.hedberg@umu.se\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60784/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60784/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"grant_types\": [\"authorization_code\"], \"default_max_age\": 3600}"; diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 51b1f4e40f..8ea1e970d4 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -1041,6 +1041,8 @@ public class RepresentationToModel { client.updateDefaultRoles(resourceRep.getDefaultRoles()); } + + if (resourceRep.getProtocolMappers() != null) { // first, remove all default/built in mappers Set mappers = client.getProtocolMappers(); @@ -1049,6 +1051,9 @@ public class RepresentationToModel { for (ProtocolMapperRepresentation mapper : resourceRep.getProtocolMappers()) { client.addProtocolMapper(toModel(mapper)); } + + + } if (resourceRep.getClientTemplate() != null) { diff --git a/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java b/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java index f50a73de8e..3f1f676619 100644 --- a/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java +++ b/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java @@ -22,21 +22,42 @@ package org.keycloak.protocol; */ public class ProtocolMapperConfigException extends Exception { + private String messageKey; private Object[] parameters; public ProtocolMapperConfigException(String message) { super(message); } + public ProtocolMapperConfigException(String message, String messageKey) { + super(message); + this.messageKey = messageKey; + } + public ProtocolMapperConfigException(String message, Throwable cause) { super(message, cause); } + public ProtocolMapperConfigException(String message, String messageKey, Throwable cause) { + super(message, cause); + this.messageKey = messageKey; + } + public ProtocolMapperConfigException(String message, Object ... parameters) { super(message); this.parameters = parameters; } + public ProtocolMapperConfigException(String messageKey, String message, Object ... parameters) { + super(message); + this.messageKey = messageKey; + this.parameters = parameters; + } + + public String getMessageKey() { + return messageKey; + } + public Object[] getParameters() { return parameters; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java index 3233690493..e16d7b8b6d 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java @@ -17,12 +17,13 @@ package org.keycloak.protocol.oidc; -import java.util.HashMap; - import org.keycloak.jose.jws.Algorithm; import org.keycloak.models.ClientModel; +import org.keycloak.protocol.oidc.utils.SubjectType; import org.keycloak.representations.idm.ClientRepresentation; +import java.util.HashMap; + /** * @author Marek Posolda */ @@ -32,6 +33,11 @@ public class OIDCAdvancedConfigWrapper { private static final String REQUEST_OBJECT_SIGNATURE_ALG = "request.object.signature.alg"; + private static final String SUBJECT_TYPE = "oidc.subject_type"; + private static final String SECTOR_IDENTIFIER_URI = "oidc.sector_identifier_uri"; + private static final String PUBLIC = "public"; + private static final String PAIRWISE = "pairwise"; + private final ClientModel clientModel; private final ClientRepresentation clientRep; @@ -74,6 +80,27 @@ public class OIDCAdvancedConfigWrapper { setAttribute(REQUEST_OBJECT_SIGNATURE_ALG, algStr); } + public void setSubjectType(SubjectType subjectType) { + if (subjectType == null) { + setAttribute(SUBJECT_TYPE, SubjectType.PUBLIC.toString()); + return; + } + setAttribute(SUBJECT_TYPE, subjectType.toString()); + } + + public SubjectType getSubjectType() { + String subjectType = getAttribute(SUBJECT_TYPE); + return subjectType == null ? SubjectType.PUBLIC : Enum.valueOf(SubjectType.class, subjectType); + } + + public void setSectorIdentifierUri(String sectorIdentifierUri) { + setAttribute(SECTOR_IDENTIFIER_URI, sectorIdentifierUri); + } + + public String getSectorIdentifierUri() { + return getAttribute(SECTOR_IDENTIFIER_URI); + } + private String getAttribute(String attrKey) { if (clientModel != null) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java index 087b1f6009..aa56dc7963 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java @@ -19,31 +19,15 @@ package org.keycloak.protocol.oidc; import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.util.UriUtils; import org.keycloak.events.EventBuilder; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ClientTemplateModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.RealmModel; +import org.keycloak.models.*; import org.keycloak.protocol.AbstractLoginProtocolFactory; import org.keycloak.protocol.LoginProtocol; -import org.keycloak.protocol.oidc.mappers.AddressMapper; -import org.keycloak.protocol.oidc.mappers.FullNameMapper; -import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; -import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; -import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; +import org.keycloak.protocol.oidc.mappers.*; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.ServicesLogger; -import org.keycloak.services.managers.AuthenticationManager; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; +import java.util.*; /** * @author Bill Burke @@ -146,6 +130,9 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { ProtocolMapperModel address = AddressMapper.createAddressMapper(); builtins.add(address); + ProtocolMapperModel pairwise = SHA265PairwiseSubMapper.createPairwiseMapper(); + builtins.add(pairwise); + model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java index 800630c2c2..2653b9b247 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java @@ -56,7 +56,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider { public static final List DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, OIDCResponseType.TOKEN, "id_token token", "code id_token", "code token", "code id_token token"); - public static final List DEFAULT_SUBJECT_TYPES_SUPPORTED = list("public"); + public static final List DEFAULT_SUBJECT_TYPES_SUPPORTED = list("public", "pairwise"); public static final List DEFAULT_RESPONSE_MODES_SUPPORTED = list("query", "fragment", "form_post"); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java index f5e4caab17..28d951438a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java @@ -19,10 +19,9 @@ package org.keycloak.protocol.oidc.endpoints; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; -import org.keycloak.OAuth2Constants; -import org.keycloak.common.ClientConnection; import org.keycloak.OAuthErrorException; import org.keycloak.RSATokenVerifier; +import org.keycloak.common.ClientConnection; import org.keycloak.common.VerificationException; import org.keycloak.events.Details; import org.keycloak.events.Errors; @@ -30,20 +29,15 @@ import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; import org.keycloak.jose.jws.Algorithm; import org.keycloak.jose.jws.JWSBuilder; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.AccessToken; import org.keycloak.services.ErrorResponseException; +import org.keycloak.services.Urls; import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.resources.Cors; -import org.keycloak.services.Urls; import org.keycloak.utils.MediaType; import javax.ws.rs.GET; @@ -54,7 +48,6 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; - import java.security.PrivateKey; import java.util.HashMap; import java.util.Map; @@ -179,8 +172,8 @@ public class UserInfoEndpoint { tokenManager.transformUserInfoAccessToken(session, userInfo, realm, clientModel, userModel, userSession, clientSession); Map claims = new HashMap(); - claims.putAll(userInfo.getOtherClaims()); claims.put("sub", userModel.getId()); + claims.putAll(userInfo.getOtherClaims()); Response.ResponseBuilder responseBuilder; OIDCAdvancedConfigWrapper cfg = OIDCAdvancedConfigWrapper.fromClientModel(clientModel); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java new file mode 100644 index 0000000000..b153fe48a1 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java @@ -0,0 +1,125 @@ +package org.keycloak.protocol.oidc.mappers; + +import org.keycloak.models.*; +import org.keycloak.protocol.ProtocolMapperConfigException; +import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; +import org.keycloak.protocol.oidc.utils.PairwiseSubMapperValidator; +import org.keycloak.protocol.oidc.utils.PairwiseSubMapperUtils; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.AccessToken; +import org.keycloak.representations.IDToken; + +import java.util.LinkedList; +import java.util.List; + +/** + * Set the 'sub' claim to pairwise . + * + * @author Martin Hardselius + */ +public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { + public static final String PROVIDER_ID_SUFFIX = "-pairwise-sub-mapper"; + + public static String getId(String prefix) { + return prefix + PROVIDER_ID_SUFFIX; + } + + public abstract String getIdPrefix(); + + /** + * Generates a pairwise subject identifier. + * + * @param mappingModel + * @param sectorIdentifier client sector identifier + * @param localSub local subject identifier (user id) + * @return A pairwise subject identifier + */ + public abstract String generateSub(ProtocolMapperModel mappingModel, String sectorIdentifier, String localSub); + + /** + * Override to add additional provider configuration properties. By default, a pairwise sub mapper will only contain configuration for a sector identifier URI. + * + * @return A list of provider configuration properties. + */ + public List getAdditionalConfigProperties() { + return new LinkedList<>(); + } + + /** + * Override to add additional configuration validation. Called when instance of mapperModel is created/updated for this protocolMapper through admin endpoint. + * + * @param session + * @param realm + * @param mapperContainer client or clientTemplate + * @param mapperModel + * @throws ProtocolMapperConfigException if configuration provided in mapperModel is not valid + */ + public void validateAdditionalConfig(KeycloakSession session, RealmModel realm, ProtocolMapperContainerModel mapperContainer, ProtocolMapperModel mapperModel) throws ProtocolMapperConfigException { + } + + @Override + public final String getDisplayCategory() { + return AbstractOIDCProtocolMapper.TOKEN_MAPPER_CATEGORY; + } + + @Override + public final IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId())); + return token; + } + + @Override + public final AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId())); + return token; + } + + @Override + public final AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { + setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId())); + return token; + } + + private void setSubject(IDToken token, String pairwiseSub) { + token.getOtherClaims().put("sub", pairwiseSub); + } + + @Override + public final List getConfigProperties() { + List configProperties = new LinkedList<>(); + configProperties.add(PairwiseSubMapperHelper.createSectorIdentifierConfig()); + configProperties.addAll(getAdditionalConfigProperties()); + return configProperties; + } + + private String getSectorIdentifier(ClientModel client, ProtocolMapperModel mappingModel) { + String sectorIdentifierUri = PairwiseSubMapperHelper.getSectorIdentifierUri(mappingModel); + if (sectorIdentifierUri != null && !sectorIdentifierUri.isEmpty()) { + return PairwiseSubMapperUtils.resolveValidSectorIdentifier(sectorIdentifierUri); + } + return PairwiseSubMapperUtils.resolveValidSectorIdentifier(client.getRootUrl(), client.getRedirectUris()); + } + + @Override + public final void validateConfig(KeycloakSession session, RealmModel realm, ProtocolMapperContainerModel mapperContainer, ProtocolMapperModel mapperModel) throws ProtocolMapperConfigException { + ClientModel client = null; + if (mapperContainer instanceof ClientModel) { + client = (ClientModel) mapperContainer; + PairwiseSubMapperValidator.validate(session, client, mapperModel); + } + validateAdditionalConfig(session, realm, mapperContainer, mapperModel); + + if (client != null) { + // Propagate changes to the sector identifier uri + OIDCAdvancedConfigWrapper configWrapper = OIDCAdvancedConfigWrapper.fromClientModel(client); + configWrapper.setSectorIdentifierUri(PairwiseSubMapperHelper.getSectorIdentifierUri(mapperModel)); + } + } + + @Override + public final String getId() { + return getIdPrefix() + PROVIDER_ID_SUFFIX; + } +} + + diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java new file mode 100644 index 0000000000..a2aa4b3970 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/PairwiseSubMapperHelper.java @@ -0,0 +1,47 @@ +package org.keycloak.protocol.oidc.mappers; + +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.services.ServicesLogger; + +public class PairwiseSubMapperHelper { + private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + + public static final String SECTOR_IDENTIFIER_URI = "sectorIdentifierUri"; + public static final String SECTOR_IDENTIFIER_URI_LABEL = "sectorIdentifierUri.label"; + public static final String SECTOR_IDENTIFIER_URI_HELP_TEXT = "sectorIdentifierUri.tooltip"; + + public static final String PAIRWISE_SUB_ALGORITHM_SALT = "pairwiseSubAlgorithmSalt"; + public static final String PAIRWISE_SUB_ALGORITHM_SALT_LABEL = "pairwiseSubAlgorithmSalt.label"; + public static final String PAIRWISE_SUB_ALGORITHM_SALT_HELP_TEXT = "pairwiseSubAlgorithmSalt.tooltip"; + + public static String getSectorIdentifierUri(ProtocolMapperModel mappingModel) { + return mappingModel.getConfig().get(SECTOR_IDENTIFIER_URI); + } + + public static String getSalt(ProtocolMapperModel mappingModel) { + return mappingModel.getConfig().get(PAIRWISE_SUB_ALGORITHM_SALT); + } + + public static void setSalt(ProtocolMapperModel mappingModel, String salt) { + mappingModel.getConfig().put(PAIRWISE_SUB_ALGORITHM_SALT, salt); + } + + public static ProviderConfigProperty createSectorIdentifierConfig() { + ProviderConfigProperty property = new ProviderConfigProperty(); + property.setName(SECTOR_IDENTIFIER_URI); + property.setType(ProviderConfigProperty.STRING_TYPE); + property.setLabel(SECTOR_IDENTIFIER_URI_LABEL); + property.setHelpText(SECTOR_IDENTIFIER_URI_HELP_TEXT); + return property; + } + + public static ProviderConfigProperty createSaltConfig() { + ProviderConfigProperty property = new ProviderConfigProperty(); + property.setName(PAIRWISE_SUB_ALGORITHM_SALT); + property.setType(ProviderConfigProperty.STRING_TYPE); + property.setLabel(PAIRWISE_SUB_ALGORITHM_SALT_LABEL); + property.setHelpText(PAIRWISE_SUB_ALGORITHM_SALT_HELP_TEXT); + return property; + } +} diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA265PairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA265PairwiseSubMapper.java new file mode 100644 index 0000000000..bc1caaad85 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA265PairwiseSubMapper.java @@ -0,0 +1,119 @@ +package org.keycloak.protocol.oidc.mappers; + +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.services.ServicesLogger; + +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.*; + +public class SHA265PairwiseSubMapper extends AbstractPairwiseSubMapper { + public static final String PROVIDER_ID = "sha256"; + private static final String HASH_ALGORITHM = "SHA-256"; + private static final String ALPHA_NUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + private final Charset charset; + + public SHA265PairwiseSubMapper() throws NoSuchAlgorithmException { + charset = Charset.forName("UTF-8"); + MessageDigest.getInstance(HASH_ALGORITHM); + } + + public static ProtocolMapperModel createPairwiseMapper() { + return createPairwiseMapper(null); + } + + public static ProtocolMapperModel createPairwiseMapper(String sectorIdentifierUri) { + Map config; + ProtocolMapperModel pairwise = new ProtocolMapperModel(); + pairwise.setName("pairwise subject identifier"); + pairwise.setProtocolMapper(AbstractPairwiseSubMapper.getId(PROVIDER_ID)); + pairwise.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + pairwise.setConsentRequired(false); + config = new HashMap<>(); + config.put(PairwiseSubMapperHelper.SECTOR_IDENTIFIER_URI, sectorIdentifierUri); + pairwise.setConfig(config); + return pairwise; + } + + public static ProtocolMapperModel createPairwiseMapper(String sectorIdentifierUri, String salt) { + Map config; + ProtocolMapperModel pairwise = new ProtocolMapperModel(); + pairwise.setName("pairwise subject identifier"); + pairwise.setProtocolMapper(AbstractPairwiseSubMapper.getId(PROVIDER_ID)); + pairwise.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + pairwise.setConsentRequired(false); + config = new HashMap<>(); + config.put(PairwiseSubMapperHelper.SECTOR_IDENTIFIER_URI, sectorIdentifierUri); + config.put(PairwiseSubMapperHelper.PAIRWISE_SUB_ALGORITHM_SALT, salt); + pairwise.setConfig(config); + return pairwise; + } + + @Override + public String getHelpText() { + return "Calculates a pairwise subject identifier using a salted sha-256 hash."; + } + + @Override + public List getAdditionalConfigProperties() { + List configProperties = new LinkedList<>(); + configProperties.add(PairwiseSubMapperHelper.createSaltConfig()); + return configProperties; + } + + @Override + public String generateSub(ProtocolMapperModel mappingModel, String sectorIdentifier, String localSub) { + String saltStr = getSalt(mappingModel); + + Charset charset = Charset.forName("UTF-8"); + byte[] salt = saltStr.getBytes(charset); + String pairwiseSub = generateSub(sectorIdentifier, localSub, salt); + logger.infof("local sub = '%s', pairwise sub = '%s'", localSub, pairwiseSub); + return pairwiseSub; + } + + private String generateSub(String sectorIdentifier, String localSub, byte[] salt) { + MessageDigest sha256; + try { + sha256 = MessageDigest.getInstance(HASH_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } + sha256.update(sectorIdentifier.getBytes(charset)); + sha256.update(localSub.getBytes(charset)); + byte[] hash = sha256.digest(salt); + return UUID.nameUUIDFromBytes(hash).toString(); + } + + private String getSalt(ProtocolMapperModel mappingModel) { + String salt = PairwiseSubMapperHelper.getSalt(mappingModel); + if (salt == null || salt.trim().isEmpty()) { + salt = createSalt(32); + PairwiseSubMapperHelper.setSalt(mappingModel, salt); + } + return salt; + } + + private String createSalt(int len) { + Random rnd = new SecureRandom(); + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) + sb.append(ALPHA_NUMERIC.charAt(rnd.nextInt(ALPHA_NUMERIC.length()))); + return sb.toString(); + } + + @Override + public String getDisplayType() { + return "Pairwise subject identifier"; + } + + @Override + public String getIdPrefix() { + return PROVIDER_ID; + } +} diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java new file mode 100644 index 0000000000..5e2dadcdc8 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperUtils.java @@ -0,0 +1,159 @@ +package org.keycloak.protocol.oidc.utils; + +import org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper; +import org.keycloak.protocol.oidc.mappers.PairwiseSubMapperHelper; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.services.ServicesLogger; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class PairwiseSubMapperUtils { + private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; + + /** + * Returns a set of valid redirect URIs from the root url and redirect URIs registered on a client. + * + * @param clientRootUrl + * @param clientRedirectUris + * @return + */ + public static Set resolveValidRedirectUris(String clientRootUrl, Set clientRedirectUris) { + Set validRedirects = new HashSet(); + for (String redirectUri : clientRedirectUris) { + if (redirectUri.startsWith("/")) { + redirectUri = relativeToAbsoluteURI(clientRootUrl, redirectUri); + logger.debugv("replacing relative valid redirect with: {0}", redirectUri); + } + if (redirectUri != null) { + validRedirects.add(redirectUri); + } + } + return validRedirects.stream() + .filter(r -> r != null && !r.trim().isEmpty()) + .collect(Collectors.toSet()); + } + + /** + * Tries to resolve a valid sector identifier from a sector identifier URI. + * + * @param sectorIdentifierUri + * @return a sector identifier iff. the sector identifier URI is a valid URI, contains a valid scheme and contains a valid host component. + */ + public static String resolveValidSectorIdentifier(String sectorIdentifierUri) { + URI uri; + try { + uri = new URI(sectorIdentifierUri); + } catch (URISyntaxException e) { + logger.debug("Invalid sector identifier URI", e); + return null; + } + + if (uri.getScheme() == null) { + logger.debugv("Invalid sector identifier URI: {0}", sectorIdentifierUri); + return null; + } + + /*if (!uri.getScheme().equalsIgnoreCase("https")) { + logger.debugv("The sector identifier URI scheme must be HTTPS. Was '{0}'", uri.getScheme()); + return null; + }*/ + + if (uri.getHost() == null) { + logger.debug("The sector identifier URI must specify a host"); + return null; + } + + return uri.getHost(); + } + + /** + * Tries to resolve a valid sector identifier from the redirect URIs registered on a client. + * + * @param clientRootUrl Root url registered on the client. + * @param clientRedirectUris Redirect URIs registered on the client. + * @return a sector identifier iff. all the registered redirect URIs are located at the same host, otherwise {@code null}. + */ + public static String resolveValidSectorIdentifier(String clientRootUrl, Set clientRedirectUris) { + Set hosts = new HashSet<>(); + for (String redirectUri : resolveValidRedirectUris(clientRootUrl, clientRedirectUris)) { + try { + URI uri = new URI(redirectUri); + hosts.add(uri.getHost()); + } catch (URISyntaxException e) { + logger.debugv("client redirect uris contained an invalid uri: {0}", redirectUri); + } + } + if (hosts.isEmpty()) { + logger.debug("could not infer any valid sector_identifiers from client redirect uris"); + return null; + } + if (hosts.size() > 1) { + logger.debug("the client redirect uris contained multiple hosts"); + return null; + } + return hosts.iterator().next(); + } + + /** + * Checks if the the registered client redirect URIs matches the set of redirect URIs from the sector identifier URI. + * + * @param clientRootUrl root url registered on the client. + * @param clientRedirectUris redirect URIs registered on the client. + * @param sectorRedirects value of the sector identifier URI. + * @return {@code true} iff. the all the redirect URIs can be described by the {@code sectorRedirects}, i.e if the registered redirect URIs is a subset of the {@code sectorRedirects}, otherwise {@code false}. + */ + public static boolean matchesRedirects(String clientRootUrl, Set clientRedirectUris, Set sectorRedirects) { + Set validRedirects = resolveValidRedirectUris(clientRootUrl, clientRedirectUris); + for (String redirect : validRedirects) { + if (!matchesRedirect(sectorRedirects, redirect)) return false; + } + return true; + } + + private static boolean matchesRedirect(Set validRedirects, String redirect) { + for (String validRedirect : validRedirects) { + if (validRedirect.endsWith("*") && !validRedirect.contains("?")) { + // strip off the query component - we don't check them when wildcards are effective + String r = redirect.contains("?") ? redirect.substring(0, redirect.indexOf("?")) : redirect; + // strip off * + int length = validRedirect.length() - 1; + validRedirect = validRedirect.substring(0, length); + if (r.startsWith(validRedirect)) return true; + // strip off trailing '/' + if (length - 1 > 0 && validRedirect.charAt(length - 1) == '/') length--; + validRedirect = validRedirect.substring(0, length); + if (validRedirect.equals(r)) return true; + } else if (validRedirect.equals(redirect)) return true; + } + return false; + } + + private static String relativeToAbsoluteURI(String rootUrl, String relative) { + if (rootUrl == null || rootUrl.isEmpty()) { + return null; + } + relative = rootUrl + relative; + return relative; + } + + public static ProtocolMapperRepresentation getPairwiseSubMapperRepresentation(ClientRepresentation client) { + List mappers = client.getProtocolMappers(); + if (mappers == null) { + return null; + } + for (ProtocolMapperRepresentation mapper : mappers) { + if (mapper.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX)) return mapper; + } + return null; + } + + public static String getSubjectIdentifierUri(ProtocolMapperRepresentation pairwiseMapper) { + return pairwiseMapper.getConfig().get(PairwiseSubMapperHelper.SECTOR_IDENTIFIER_URI); + } +} diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperValidator.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperValidator.java new file mode 100644 index 0000000000..e6d8a6eeb7 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/PairwiseSubMapperValidator.java @@ -0,0 +1,113 @@ +package org.keycloak.protocol.oidc.utils; + +import org.keycloak.connections.httpclient.HttpClientProvider; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.protocol.ProtocolMapperConfigException; +import org.keycloak.protocol.oidc.mappers.PairwiseSubMapperHelper; +import org.keycloak.util.JsonSerialization; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Martin Hardselius + */ +public class PairwiseSubMapperValidator { + + public static final String PAIRWISE_MALFORMED_CLIENT_REDIRECT_URI = "pairwiseMalformedClientRedirectURI"; + public static final String PAIRWISE_CLIENT_REDIRECT_URIS_MISSING_HOST = "pairwiseClientRedirectURIsMissingHost"; + public static final String PAIRWISE_CLIENT_REDIRECT_URIS_MULTIPLE_HOSTS = "pairwiseClientRedirectURIsMultipleHosts"; + public static final String PAIRWISE_MALFORMED_SECTOR_IDENTIFIER_URI = "pairwiseMalformedSectorIdentifierURI"; + public static final String PAIRWISE_FAILED_TO_GET_REDIRECT_URIS = "pairwiseFailedToGetRedirectURIs"; + public static final String PAIRWISE_REDIRECT_URIS_MISMATCH = "pairwiseRedirectURIsMismatch"; + + public static void validate(KeycloakSession session, ClientModel client, ProtocolMapperModel mapperModel) throws ProtocolMapperConfigException { + String sectorIdentifierUri = PairwiseSubMapperHelper.getSectorIdentifierUri(mapperModel); + String rootUrl = client.getRootUrl(); + Set redirectUris = client.getRedirectUris(); + validate(session, rootUrl, redirectUris, sectorIdentifierUri); + } + + public static void validate(KeycloakSession session, String rootUrl, Set redirectUris, String sectorIdentifierUri) throws ProtocolMapperConfigException { + if (sectorIdentifierUri == null || sectorIdentifierUri.isEmpty()) { + validateClientRedirectUris(rootUrl, redirectUris); + return; + } + validateSectorIdentifierUri(sectorIdentifierUri); + validateSectorIdentifierUri(session, rootUrl, redirectUris, sectorIdentifierUri); + } + + private static void validateClientRedirectUris(String rootUrl, Set redirectUris) throws ProtocolMapperConfigException { + Set hosts = new HashSet<>(); + for (String redirectUri : PairwiseSubMapperUtils.resolveValidRedirectUris(rootUrl, redirectUris)) { + try { + URI uri = new URI(redirectUri); + hosts.add(uri.getHost()); + } catch (URISyntaxException e) { + throw new ProtocolMapperConfigException("Client contained an invalid redirect URI.", + PAIRWISE_MALFORMED_CLIENT_REDIRECT_URI, e); + } + } + + if (hosts.isEmpty()) { + throw new ProtocolMapperConfigException("Client redirect URIs must contain a valid host component.", + PAIRWISE_CLIENT_REDIRECT_URIS_MISSING_HOST); + } + + if (hosts.size() > 1) { + throw new ProtocolMapperConfigException("Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.", PAIRWISE_CLIENT_REDIRECT_URIS_MULTIPLE_HOSTS); + } + } + + private static void validateSectorIdentifierUri(String sectorIdentifierUri) throws ProtocolMapperConfigException { + URI uri; + try { + uri = new URI(sectorIdentifierUri); + } catch (URISyntaxException e) { + throw new ProtocolMapperConfigException("Invalid Sector Identifier URI.", + PAIRWISE_MALFORMED_SECTOR_IDENTIFIER_URI, e); + } + if (uri.getScheme() == null || uri.getHost() == null) { + throw new ProtocolMapperConfigException("Invalid Sector Identifier URI.", + PAIRWISE_MALFORMED_SECTOR_IDENTIFIER_URI); + } + } + + private static void validateSectorIdentifierUri(KeycloakSession session, String rootUrl, Set redirectUris, String sectorIdentifierUri) throws ProtocolMapperConfigException { + Set sectorRedirects = getSectorRedirects(session, sectorIdentifierUri); + if (!PairwiseSubMapperUtils.matchesRedirects(rootUrl, redirectUris, sectorRedirects)) { + throw new ProtocolMapperConfigException("Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI.", + PAIRWISE_REDIRECT_URIS_MISMATCH); + } + } + + private static Set getSectorRedirects(KeycloakSession session, String sectorIdentifierUri) throws ProtocolMapperConfigException { + InputStream is = null; + try { + is = session.getProvider(HttpClientProvider.class).get(sectorIdentifierUri); + List sectorRedirects = JsonSerialization.readValue(is, TypedList.class); + return new HashSet<>(sectorRedirects); + } catch (IOException e) { + throw new ProtocolMapperConfigException("Failed to get redirect URIs from the Sector Identifier URI.", + PAIRWISE_FAILED_TO_GET_REDIRECT_URIS, e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ignored) { + } + } + } + } + + public static class TypedList extends ArrayList {} + +} diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/SubjectType.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/SubjectType.java new file mode 100644 index 0000000000..ec1ba97a90 --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/SubjectType.java @@ -0,0 +1,13 @@ +package org.keycloak.protocol.oidc.utils; + +public enum SubjectType { + PUBLIC, + PAIRWISE; + + public static SubjectType parse(String subjectTypeStr) { + if (subjectTypeStr == null) { + return PUBLIC; + } + return Enum.valueOf(SubjectType.class, subjectTypeStr.toUpperCase()); + } +} diff --git a/services/src/main/java/org/keycloak/services/ServicesLogger.java b/services/src/main/java/org/keycloak/services/ServicesLogger.java index af6c4c13e5..0595abbcce 100644 --- a/services/src/main/java/org/keycloak/services/ServicesLogger.java +++ b/services/src/main/java/org/keycloak/services/ServicesLogger.java @@ -434,4 +434,8 @@ public interface ServicesLogger extends BasicLogger { @Message(id=97, value="Invalid request") void invalidRequest(@Cause Throwable t); + @LogMessage(level = ERROR) + @Message(id=98, value="Failed to get redirect uris from sector identifier URI: %s") + void failedToGetRedirectUrisFromSectorIdentifierUri(@Cause Throwable t, String sectorIdentifierUri); + } diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java index d634b006b7..f5ad8b073a 100755 --- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java @@ -19,22 +19,22 @@ package org.keycloak.services.clientregistration; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; -import org.keycloak.models.ClientInitialAccessModel; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ClientRegistrationTrustedHostModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.*; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; +import org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper; +import org.keycloak.protocol.oidc.mappers.PairwiseSubMapperHelper; +import org.keycloak.protocol.oidc.mappers.SHA265PairwiseSubMapper; +import org.keycloak.protocol.oidc.utils.SubjectType; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.services.validation.ClientValidator; +import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; import javax.ws.rs.core.Response; -import java.util.Properties; /** * @author Stian Thorgersen @@ -55,7 +55,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist auth.requireCreate(); ValidationMessages validationMessages = new ValidationMessages(); - if (!ClientValidator.validate(client, validationMessages)) { + if (!ClientValidator.validate(client, validationMessages) || !PairwiseClientValidator.validate(session, client, validationMessages)) { String errorCode = validationMessages.fieldHasError("redirectUris") ? ErrorCodes.INVALID_REDIRECT_URI : ErrorCodes.INVALID_CLIENT_METADATA; throw new ErrorResponseException( errorCode, @@ -66,6 +66,10 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist try { ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true); + OIDCAdvancedConfigWrapper configWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(client); + if (configWrapper.getSubjectType().equals(SubjectType.PAIRWISE)) { + addPairwiseSubMapper(clientModel, configWrapper.getSectorIdentifierUri()); + } client = ModelToRepresentation.toRepresentation(clientModel); @@ -119,7 +123,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist } ValidationMessages validationMessages = new ValidationMessages(); - if (!ClientValidator.validate(rep, validationMessages)) { + if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) { String errorCode = validationMessages.fieldHasError("redirectUris") ? ErrorCodes.INVALID_REDIRECT_URI : ErrorCodes.INVALID_CLIENT_METADATA; throw new ErrorResponseException( errorCode, @@ -128,6 +132,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist ); } + updateSubjectType(rep, client); RepresentationToModel.updateClient(rep, client); rep = ModelToRepresentation.toRepresentation(client); @@ -140,6 +145,43 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist return rep; } + private void updateSubjectType(ClientRepresentation rep, ClientModel client) { + OIDCAdvancedConfigWrapper repConfigWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(rep); + SubjectType repSubjectType = repConfigWrapper.getSubjectType(); + OIDCAdvancedConfigWrapper clientConfigWrapper = OIDCAdvancedConfigWrapper.fromClientModel(client); + SubjectType clientSubjectType = clientConfigWrapper.getSubjectType(); + + if (repSubjectType.equals(SubjectType.PAIRWISE) && clientSubjectType.equals(SubjectType.PAIRWISE)) { + updateSectorIdentifier(client, repConfigWrapper.getSectorIdentifierUri()); + } + + if (repSubjectType.equals(SubjectType.PAIRWISE) && clientSubjectType.equals(SubjectType.PUBLIC)) { + addPairwiseSubMapper(client, repConfigWrapper.getSectorIdentifierUri()); + } + + if (repSubjectType.equals(SubjectType.PUBLIC) && clientSubjectType.equals(SubjectType.PAIRWISE)) { + removePairwiseSubMapper(client); + } + } + + private void updateSectorIdentifier(ClientModel client, String sectorIdentifierUri) { + client.getProtocolMappers().stream().filter(mapping -> mapping.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX)).forEach(mapping -> { + mapping.getConfig().put(PairwiseSubMapperHelper.SECTOR_IDENTIFIER_URI, sectorIdentifierUri); + }); + } + + private void addPairwiseSubMapper(ClientModel client, String sectorIdentifierUri) { + client.addProtocolMapper(SHA265PairwiseSubMapper.createPairwiseMapper(sectorIdentifierUri)); + } + + private void removePairwiseSubMapper(ClientModel client) { + for (ProtocolMapperModel mapping : client.getProtocolMappers()) { + if (mapping.getProtocolMapper().endsWith(AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX)) { + client.removeProtocolMapper(mapping); + } + } + } + public void delete(String clientId) { event.event(EventType.CLIENT_DELETE).client(clientId); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java index e1939ef0c8..c377313ddc 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java @@ -32,6 +32,7 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil; import org.keycloak.protocol.oidc.utils.JWKSUtils; import org.keycloak.protocol.oidc.utils.OIDCResponseType; +import org.keycloak.protocol.oidc.utils.SubjectType; import org.keycloak.representations.idm.CertificateRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; @@ -53,6 +54,7 @@ public class DescriptionConverter { public static ClientRepresentation toInternal(KeycloakSession session, OIDCClientRepresentation clientOIDC) throws ClientRegistrationException { ClientRepresentation client = new ClientRepresentation(); + client.setClientId(clientOIDC.getClientId()); client.setName(clientOIDC.getClientName()); client.setRedirectUris(clientOIDC.getRedirectUris()); @@ -113,6 +115,12 @@ public class DescriptionConverter { configWrapper.setRequestObjectSignatureAlg(algorithm); } + SubjectType subjectType = SubjectType.parse(clientOIDC.getSubjectType()); + configWrapper.setSubjectType(subjectType); + if (subjectType.equals(SubjectType.PAIRWISE)) { + configWrapper.setSectorIdentifierUri(clientOIDC.getSectorIdentifierUri()); + } + return client; } @@ -172,6 +180,11 @@ public class DescriptionConverter { response.setRequestObjectSigningAlg(config.getRequestObjectSignatureAlg().toString()); } + response.setSubjectType(config.getSubjectType().toString().toLowerCase()); + if (config.getSubjectType().equals(SubjectType.PAIRWISE)) { + response.setSectorIdentifierUri(config.getSectorIdentifierUri()); + } + return response; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index 9b6593f548..d208b50711 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -50,6 +50,7 @@ import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.ErrorResponse; import org.keycloak.common.util.Time; import org.keycloak.services.validation.ClientValidator; +import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; import javax.ws.rs.Consumes; @@ -127,7 +128,7 @@ public class ClientResource { } ValidationMessages validationMessages = new ValidationMessages(); - if (!ClientValidator.validate(rep, validationMessages)) { + if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) { Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); throw new ErrorResponseException( validationMessages.getStringMessages(), diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index dacea26b68..1be0cac65d 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -31,6 +31,7 @@ import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.validation.ClientValidator; +import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; import javax.ws.rs.*; @@ -120,7 +121,7 @@ public class ClientsResource { auth.requireManage(); ValidationMessages validationMessages = new ValidationMessages(); - if (!ClientValidator.validate(rep, validationMessages)) { + if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) { Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); throw new ErrorResponseException( validationMessages.getStringMessages(), diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java index 86dcefcfe5..804082051d 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java @@ -20,15 +20,7 @@ import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.NotFoundException; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.mappers.UserFederationMapperFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ProtocolMapperContainerModel; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.*; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.protocol.ProtocolMapper; @@ -39,19 +31,11 @@ import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.resources.admin.RealmAuth.Resource; -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.*; 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.text.MessageFormat; import java.util.LinkedList; import java.util.List; @@ -269,7 +253,7 @@ public class ProtocolMappersResource { } catch (ProtocolMapperConfigException ex) { logger.error(ex.getMessage()); Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); - throw new ErrorResponseException(ex.getMessage(), MessageFormat.format(messages.getProperty(ex.getMessage(), ex.getMessage()), ex.getParameters()), + throw new ErrorResponseException(ex.getMessage(), MessageFormat.format(messages.getProperty(ex.getMessageKey(), ex.getMessage()), ex.getParameters()), Response.Status.BAD_REQUEST); } } diff --git a/services/src/main/java/org/keycloak/services/validation/PairwiseClientValidator.java b/services/src/main/java/org/keycloak/services/validation/PairwiseClientValidator.java new file mode 100644 index 0000000000..6cc20334e1 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/validation/PairwiseClientValidator.java @@ -0,0 +1,41 @@ +package org.keycloak.services.validation; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.protocol.ProtocolMapperConfigException; +import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; +import org.keycloak.protocol.oidc.utils.PairwiseSubMapperValidator; +import org.keycloak.protocol.oidc.utils.SubjectType; +import org.keycloak.representations.idm.ClientRepresentation; + +import java.util.HashSet; +import java.util.Set; + + +/** + * @author Martin Hardselius + */ +public class PairwiseClientValidator { + + public static boolean validate(KeycloakSession session, ClientRepresentation client, ValidationMessages messages) { + OIDCAdvancedConfigWrapper configWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(client); + if (configWrapper.getSubjectType().equals(SubjectType.PAIRWISE)) { + String sectorIdentifierUri = configWrapper.getSectorIdentifierUri(); + String rootUrl = client.getRootUrl(); + Set redirectUris = new HashSet<>(); + if (client.getRedirectUris() != null) redirectUris.addAll(client.getRedirectUris()); + return validate(session, rootUrl, redirectUris, sectorIdentifierUri, messages); + } + return true; + } + + public static boolean validate(KeycloakSession session, String rootUrl, Set redirectUris, String sectorIdentifierUri, ValidationMessages messages) { + try { + PairwiseSubMapperValidator.validate(session, rootUrl, redirectUris, sectorIdentifierUri); + } catch (ProtocolMapperConfigException e) { + messages.add(e.getMessage(), e.getMessageKey()); + return false; + } + return true; + } + +} diff --git a/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java b/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java index e26ebff397..a7e82c8601 100644 --- a/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java +++ b/services/src/main/java/org/keycloak/services/validation/ValidationMessages.java @@ -57,8 +57,11 @@ public class ValidationMessages { } public boolean fieldHasError(String fieldId) { + if (fieldId == null) { + return false; + } for (ValidationMessage message : messages) { - if (message.getFieldId().equals(fieldId)) { + if (fieldId.equals(message.getFieldId())) { return true; } } diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper index 9d1e10e927..77830dc995 100755 --- a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper +++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper @@ -34,5 +34,5 @@ org.keycloak.protocol.saml.mappers.UserSessionNoteStatementMapper org.keycloak.protocol.saml.mappers.GroupMembershipMapper org.keycloak.protocol.oidc.mappers.UserClientRoleMappingMapper org.keycloak.protocol.oidc.mappers.UserRealmRoleMappingMapper - +org.keycloak.protocol.oidc.mappers.SHA265PairwiseSubMapper diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java index 6bd7dc262d..d8d2a8d163 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java @@ -18,10 +18,8 @@ package org.keycloak.testsuite.rest; import org.keycloak.Config.Scope; -import org.keycloak.events.admin.AdminEvent; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.representations.adapters.action.AdminAction; import org.keycloak.representations.adapters.action.LogoutAction; import org.keycloak.representations.adapters.action.PushNotBeforeAction; import org.keycloak.representations.adapters.action.TestAvailabilityAction; @@ -29,6 +27,7 @@ import org.keycloak.services.resource.RealmResourceProvider; import org.keycloak.services.resource.RealmResourceProviderFactory; import java.security.KeyPair; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; @@ -70,6 +69,7 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProv private KeyPair signingKeyPair; private String oidcRequest; + private List sectorIdentifierRedirectUris; public KeyPair getSigningKeyPair() { return signingKeyPair; @@ -86,5 +86,13 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProv public void setOidcRequest(String oidcRequest) { this.oidcRequest = oidcRequest; } + + public List getSectorIdentifierRedirectUris() { + return sectorIdentifierRedirectUris; + } + + public void setSectorIdentifierRedirectUris(List sectorIdentifierRedirectUris) { + this.sectorIdentifierRedirectUris = sectorIdentifierRedirectUris; + } } } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java index 6ea488f5c8..8b5df6ceb4 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java @@ -17,19 +17,6 @@ package org.keycloak.testsuite.rest.resource; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.util.HashMap; -import java.util.Map; - -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.MediaType; - import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; import org.keycloak.OAuth2Constants; @@ -42,6 +29,19 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.testsuite.rest.TestApplicationResourceProviderFactory; +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.MediaType; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author Marek Posolda */ @@ -134,4 +134,19 @@ public class TestingOIDCEndpointsApplicationResource { public String getOIDCRequest() { return clientData.getOidcRequest(); } + + @GET + @Path("/set-sector-identifier-redirect-uris") + @Produces(MediaType.APPLICATION_JSON) + public void setSectorIdentifierRedirectUris(@QueryParam("redirectUris") List redirectUris) { + clientData.setSectorIdentifierRedirectUris(new ArrayList<>()); + clientData.getSectorIdentifierRedirectUris().addAll(redirectUris); + } + + @GET + @Path("/get-sector-identifier-redirect-uris") + @Produces(MediaType.APPLICATION_JSON) + public List getSectorIdentifierRedirectUris() { + return clientData.getSectorIdentifierRedirectUris(); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java index 8c5f98b6d6..88b7b38ea3 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResourceUrls.java @@ -17,10 +17,10 @@ package org.keycloak.testsuite.client.resources; -import javax.ws.rs.core.UriBuilder; - import org.keycloak.testsuite.util.OAuthClient; +import javax.ws.rs.core.UriBuilder; + /** * @author Marek Posolda */ @@ -45,4 +45,10 @@ public class TestApplicationResourceUrls { return builder.build().toString(); } + + public static String pairwiseSectorIdentifierUri() { + UriBuilder builder = oidcClientEndpoints() + .path(TestOIDCEndpointsApplicationResource.class, "getSectorIdentifierRedirectUris"); + return builder.build().toString(); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java index 54d6c3573d..9c4f324967 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java @@ -17,15 +17,15 @@ package org.keycloak.testsuite.client.resources; -import java.util.Map; +import org.keycloak.jose.jwk.JSONWebKeySet; 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.MediaType; - -import org.keycloak.jose.jwk.JSONWebKeySet; +import java.util.List; +import java.util.Map; /** * @author Marek Posolda @@ -55,4 +55,15 @@ public interface TestOIDCEndpointsApplicationResource { @Path("/get-oidc-request") @Produces(org.keycloak.utils.MediaType.APPLICATION_JWT) String getOIDCRequest(); + + @GET + @Path("/set-sector-identifier-redirect-uris") + @Produces(MediaType.APPLICATION_JSON) + void setSectorIdentifierRedirectUris(@QueryParam("redirectUris") List redirectUris); + + @GET + @Path("/get-sector-identifier-redirect-uris") + @Produces(MediaType.APPLICATION_JSON) + List getSectorIdentifierRedirectUris(); + } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java index 19d64135a3..d13bfa1fb7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java @@ -55,11 +55,7 @@ import org.keycloak.testsuite.client.resources.TestOIDCEndpointsApplicationResou import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource; import org.keycloak.testsuite.util.OAuthClient; import java.security.PrivateKey; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; @@ -286,6 +282,137 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest { Assert.assertEquals(response.getClientId(), accessToken.getAudience()[0]); } + @Test + public void createPairwiseClient() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + clientRep.setSubjectType("pairwise"); + + OIDCClientRepresentation response = reg.oidc().create(clientRep); + Assert.assertEquals("pairwise", response.getSubjectType()); + } + + @Test + public void updateClientToPairwise() throws Exception { + OIDCClientRepresentation response = create(); + Assert.assertEquals("public", response.getSubjectType()); + + reg.auth(Auth.token(response)); + response.setSubjectType("pairwise"); + OIDCClientRepresentation updated = reg.oidc().update(response); + + Assert.assertEquals("pairwise", updated.getSubjectType()); + } + + @Test + public void updateSectorIdentifierUri() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + clientRep.setSubjectType("pairwise"); + OIDCClientRepresentation response = reg.oidc().create(clientRep); + Assert.assertEquals("pairwise", response.getSubjectType()); + Assert.assertNull(response.getSectorIdentifierUri()); + + reg.auth(Auth.token(response)); + + // Push redirect uris to the sector identifier URI + List sectorRedirects = new ArrayList<>(); + sectorRedirects.addAll(response.getRedirectUris()); + TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints(); + oidcClientEndpointsResource.setSectorIdentifierRedirectUris(sectorRedirects); + + response.setSectorIdentifierUri(TestApplicationResourceUrls.pairwiseSectorIdentifierUri()); + + OIDCClientRepresentation updated = reg.oidc().update(response); + + Assert.assertEquals("pairwise", updated.getSubjectType()); + Assert.assertEquals(TestApplicationResourceUrls.pairwiseSectorIdentifierUri(), updated.getSectorIdentifierUri()); + + } + + @Test + public void createPairwiseClientWithSectorIdentifierURI() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + + // Push redirect uris to the sector identifier URI + List sectorRedirects = new ArrayList<>(); + sectorRedirects.addAll(clientRep.getRedirectUris()); + TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints(); + oidcClientEndpointsResource.setSectorIdentifierRedirectUris(sectorRedirects); + + clientRep.setSubjectType("pairwise"); + clientRep.setSectorIdentifierUri(TestApplicationResourceUrls.pairwiseSectorIdentifierUri()); + + OIDCClientRepresentation response = reg.oidc().create(clientRep); + Assert.assertEquals("pairwise", response.getSubjectType()); + Assert.assertEquals(TestApplicationResourceUrls.pairwiseSectorIdentifierUri(), response.getSectorIdentifierUri()); + } + + @Test + public void createPairwiseClientWithRedirectsToMultipleHostsWithoutSectorIdentifierURI() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + + List redirects = new ArrayList<>(); + redirects.add("http://redirect1"); + redirects.add("http://redirect2"); + + clientRep.setSubjectType("pairwise"); + clientRep.setRedirectUris(redirects); + + assertCreateFail(clientRep, 400, "Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components."); + } + + @Test + public void createPairwiseClientWithRedirectsToMultipleHosts() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + + // Push redirect URIs to the sector identifier URI + List redirects = new ArrayList<>(); + redirects.add("http://redirect1"); + redirects.add("http://redirect2"); + TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints(); + oidcClientEndpointsResource.setSectorIdentifierRedirectUris(redirects); + + clientRep.setSubjectType("pairwise"); + clientRep.setSectorIdentifierUri(TestApplicationResourceUrls.pairwiseSectorIdentifierUri()); + clientRep.setRedirectUris(redirects); + + OIDCClientRepresentation response = reg.oidc().create(clientRep); + Assert.assertEquals("pairwise", response.getSubjectType()); + Assert.assertEquals(TestApplicationResourceUrls.pairwiseSectorIdentifierUri(), response.getSectorIdentifierUri()); + Assert.assertNames(response.getRedirectUris(), "http://redirect1", "http://redirect2"); + } + + @Test + public void createPairwiseClientWithSectorIdentifierURIContainingMismatchedRedirects() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + + // Push redirect uris to the sector identifier URI + List sectorRedirects = new ArrayList<>(); + sectorRedirects.add("http://someotherredirect"); + TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints(); + oidcClientEndpointsResource.setSectorIdentifierRedirectUris(sectorRedirects); + + clientRep.setSubjectType("pairwise"); + clientRep.setSectorIdentifierUri(TestApplicationResourceUrls.pairwiseSectorIdentifierUri()); + + assertCreateFail(clientRep, 400, "Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI."); + } + + @Test + public void createPairwiseClientWithInvalidSectorIdentifierURI() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + clientRep.setSubjectType("pairwise"); + clientRep.setSectorIdentifierUri("malformed"); + assertCreateFail(clientRep, 400, "Invalid Sector Identifier URI."); + } + + @Test + public void createPairwiseClientWithUnreachableSectorIdentifierURI() throws Exception { + OIDCClientRepresentation clientRep = createRep(); + clientRep.setSubjectType("pairwise"); + clientRep.setSectorIdentifierUri("http://localhost/dummy"); + assertCreateFail(clientRep, 400, "Failed to get redirect URIs from the Sector Identifier URI."); + } + @Test public void testSignaturesRequired() throws Exception { OIDCClientRepresentation clientRep = createRep(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java index 7fea65748b..314047d634 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java @@ -84,7 +84,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest { assertContains(oidcConfig.getGrantTypesSupported(), OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT); assertContains(oidcConfig.getResponseModesSupported(), "query", "fragment"); - Assert.assertNames(oidcConfig.getSubjectTypesSupported(), "public"); + Assert.assertNames(oidcConfig.getSubjectTypesSupported(), "pairwise", "public"); Assert.assertNames(oidcConfig.getIdTokenSigningAlgValuesSupported(), Algorithm.RS256.toString()); Assert.assertNames(oidcConfig.getUserInfoSigningAlgValuesSupported(), Algorithm.RS256.toString()); Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), Algorithm.none.toString(), Algorithm.RS256.toString()); diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 998eff8a94..ea3426890f 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -164,6 +164,12 @@ usermodel.clientRoleMapping.rolePrefix.label=Client Role prefix usermodel.clientRoleMapping.rolePrefix.tooltip=A prefix for each client role (optional). usermodel.realmRoleMapping.rolePrefix.label=Realm Role prefix usermodel.realmRoleMapping.rolePrefix.tooltip=A prefix for each Realm Role (optional). +sectorIdentifierUri.label=Sector Identifier URI +sectorIdentifierUri.tooltip=Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all of their users. +pairwiseSubAlgorithmSalt.label=Salt +pairwiseSubAlgorithmSalt.tooltip=Salt used when calculating the pairwise subject identifier. If left blank, a salt will be generated. + + # client details clients.tooltip=Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles. diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties index 345cb2503c..f4044ab7df 100644 --- a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties @@ -14,4 +14,11 @@ ldapErrorCantWriteOnlyForReadOnlyLdap=Can't set write only when LDAP provider mo ldapErrorCantWriteOnlyAndReadOnly=Can't set write-only and read-only together clientRedirectURIsFragmentError=Redirect URIs must not contain an URI fragment -clientRootURLFragmentError=Root URL must not contain an URL fragment \ No newline at end of file +clientRootURLFragmentError=Root URL must not contain an URL fragment + +pairwiseMalformedClientRedirectURI=Client contained an invalid redirect URI. +pairwiseClientRedirectURIsMissingHost=Client redirect URIs must contain a valid host component. +pairwiseClientRedirectURIsMultipleHosts=Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components. +pairwiseMalformedSectorIdentifierURI=Malformed Sector Identifier URI. +pairwiseFailedToGetRedirectURIs=Failed to get redirect URIs from the Sector Identifier URI. +pairwiseRedirectURIsMismatch=Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI. diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index c015dfa448..71f21658c2 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -1703,6 +1703,12 @@ module.controller('ClientProtocolMapperCtrl', function($scope, realm, serverInfo mapper = angular.copy($scope.mapper); $location.url("/realms/" + realm.realm + '/clients/' + client.id + "/mappers/" + $scope.model.mapper.id); Notifications.success("Your changes have been saved."); + }, function(error) { + if (error.status == 400 && error.data.error_description) { + Notifications.error(error.data.error_description); + } else { + Notifications.error('Unexpected error when updating protocol mapper'); + } }); }; From eb80d59cd8b26c30c7e100f583174f895f45c02c Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Tue, 13 Sep 2016 16:16:01 -0300 Subject: [PATCH 33/53] [KEYCLOAK-3534] - Fixing the logic to display the authz tab. --- .../base/admin/resources/js/authz/authz-controller.js | 9 --------- .../theme/base/admin/resources/js/controllers/clients.js | 2 +- .../base/admin/resources/templates/kc-tabs-client.html | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) 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 f4f87aa340..72b6c43c06 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 @@ -9,7 +9,6 @@ module.controller('ResourceServerCtrl', function($scope, realm, ResourceServer) module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $location, $upload, $modal, realm, ResourceServer, client, AuthzDialog, Notifications) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; ResourceServer.get({ realm : $route.current.params.realm, @@ -83,7 +82,6 @@ module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $l module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerResource, client) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.query = { realm: realm.realm, @@ -136,7 +134,6 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerResource, ResourceServerScope, AuthzDialog, Notifications) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) { $scope.scopes = data; @@ -268,7 +265,6 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope, client) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.query = { realm: realm.realm, @@ -321,7 +317,6 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerScope, AuthzDialog, Notifications) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; var $instance = this; @@ -431,7 +426,6 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.policyProviders = []; $scope.query = { @@ -504,7 +498,6 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.policyProviders = []; $scope.query = { @@ -1207,7 +1200,6 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.decisionStrategies = ['AFFIRMATIVE', 'UNANIMOUS', 'CONSENSUS']; $scope.logics = ['POSITIVE', 'NEGATIVE']; @@ -1373,7 +1365,6 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $location, realm, clients, roles, ResourceServer, client, ResourceServerResource, ResourceServerScope, User, Notifications) { $scope.realm = realm; $scope.client = client; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; $scope.clients = clients; $scope.roles = roles; $scope.authzRequest = {}; diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index c015dfa448..cdb8b480cb 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -812,7 +812,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates, $scope.samlEncrypt = false; $scope.samlForcePostBinding = false; $scope.samlForceNameIdFormat = false; - $scope.showAuthorizationTab = client.authorizationServicesEnabled; + $scope.disableAuthorizationTab = !client.authorizationServicesEnabled; function updateProperties() { if (!$scope.client.attributes) { diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html index 96d9adaa52..f39bc2c6cb 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html @@ -19,7 +19,7 @@ {{:: 'scope' | translate}} {{:: 'scope.tooltip' | translate}}
  • -
  • {{:: 'authz-authorization' | translate}}
  • +
  • {{:: 'authz-authorization' | translate}}
  • {{:: 'revocation' | translate}}
  • From cb1b34eee5b4d1409caa72e6f74ac825b4d9682f Mon Sep 17 00:00:00 2001 From: Gilberto Vieira da Silva Date: Thu, 1 Sep 2016 09:21:24 -0300 Subject: [PATCH 34/53] When keycloak is set to login email and Username is different from email, to check the "Remember Me" username is not displayed on the login screen with that email because the KEYCLOAK_REMEMBER_ME cookie is always recorded the username field. Conflicts: services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java --- .../org/keycloak/services/managers/AuthenticationManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index e53ac00b9f..aa536f5fca 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -408,7 +408,7 @@ public class AuthenticationManager { // refresh the cookies! createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection); if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN); - if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection); + if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection); // Update userSession note with authTime. But just if flag SSO_AUTH is not set if (!isSSOAuthentication(clientSession)) { From 4b3d3bf55b7e56731710fd1a99bc4e744678e254 Mon Sep 17 00:00:00 2001 From: Gilberto Vieira da Silva Date: Tue, 13 Sep 2016 17:56:00 -0300 Subject: [PATCH 35/53] Include test case for PULL-REQ-3181 --- .../keycloak/testsuite/forms/LoginTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index accbd37e33..5bef3d2472 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -480,6 +480,40 @@ public class LoginTest extends TestRealmKeycloakTest { setRememberMe(false); } } + + @Test + // KEYCLOAK-3181 + public void loginWithEmailUserAndRememberMe() { + setRememberMe(true); + + try { + loginPage.open(); + loginPage.setRememberMe(true); + assertTrue(loginPage.isRememberMeChecked()); + loginPage.login("login@test.com", "password"); + + Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); + EventRepresentation loginEvent = events.expectLogin().user(userId) + .detail(Details.USERNAME, "login@test.com") + .detail(Details.REMEMBER_ME, "true") + .assertEvent(); + String sessionId = loginEvent.getSessionId(); + + // Expire session + testingClient.testing().removeUserSession("test", sessionId); + + // Assert rememberMe checked and username/email prefilled + loginPage.open(); + assertTrue(loginPage.isRememberMeChecked()); + + Assert.assertEquals("login@test.com", loginPage.getUsername()); + + loginPage.setRememberMe(false); + } finally { + setRememberMe(false); + } + } // KEYCLOAK-1037 @Test From 868f8b166fafa24201d1ef5c4a070e9d280ebdcb Mon Sep 17 00:00:00 2001 From: Gilberto Vieira da Silva Date: Tue, 13 Sep 2016 18:52:05 -0300 Subject: [PATCH 36/53] Reverted to appli to branch master-KEYCLOAK-LoginUsername --- .../keycloak/testsuite/forms/LoginTest.java | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index 5bef3d2472..accbd37e33 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -480,40 +480,6 @@ public class LoginTest extends TestRealmKeycloakTest { setRememberMe(false); } } - - @Test - // KEYCLOAK-3181 - public void loginWithEmailUserAndRememberMe() { - setRememberMe(true); - - try { - loginPage.open(); - loginPage.setRememberMe(true); - assertTrue(loginPage.isRememberMeChecked()); - loginPage.login("login@test.com", "password"); - - Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); - Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); - EventRepresentation loginEvent = events.expectLogin().user(userId) - .detail(Details.USERNAME, "login@test.com") - .detail(Details.REMEMBER_ME, "true") - .assertEvent(); - String sessionId = loginEvent.getSessionId(); - - // Expire session - testingClient.testing().removeUserSession("test", sessionId); - - // Assert rememberMe checked and username/email prefilled - loginPage.open(); - assertTrue(loginPage.isRememberMeChecked()); - - Assert.assertEquals("login@test.com", loginPage.getUsername()); - - loginPage.setRememberMe(false); - } finally { - setRememberMe(false); - } - } // KEYCLOAK-1037 @Test From 55e07bcde2bb66dd3ab3bf562b5d573b428ed730 Mon Sep 17 00:00:00 2001 From: Gilberto Vieira da Silva Date: Tue, 13 Sep 2016 18:52:16 -0300 Subject: [PATCH 37/53] Reverted to appli to branch master-KEYCLOAK-LoginUsername --- .../org/keycloak/services/managers/AuthenticationManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index aa536f5fca..e53ac00b9f 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -408,7 +408,7 @@ public class AuthenticationManager { // refresh the cookies! createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection); if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN); - if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection); + if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection); // Update userSession note with authTime. But just if flag SSO_AUTH is not set if (!isSSOAuthentication(clientSession)) { From 6d5dc673d4aafb7059b515310679736677d46ffb Mon Sep 17 00:00:00 2001 From: Gilberto Vieira da Silva Date: Thu, 1 Sep 2016 09:21:24 -0300 Subject: [PATCH 38/53] When keycloak is set to login email and Username is different from email, to check the "Remember Me" username is not displayed on the login screen with that email because the KEYCLOAK_REMEMBER_ME cookie is always recorded the username field. Conflicts: services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java [PULL-REQUEST-3181] --- .../org/keycloak/services/managers/AuthenticationManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index e53ac00b9f..aa536f5fca 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -408,7 +408,7 @@ public class AuthenticationManager { // refresh the cookies! createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection); if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN); - if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection); + if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection); // Update userSession note with authTime. But just if flag SSO_AUTH is not set if (!isSSOAuthentication(clientSession)) { From 8c518a8d38b66271f326eee0369f3ade55ff8983 Mon Sep 17 00:00:00 2001 From: Gilberto Vieira da Silva Date: Tue, 13 Sep 2016 17:56:00 -0300 Subject: [PATCH 39/53] Include test case for PULL-REQ-3181 --- .../keycloak/testsuite/forms/LoginTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java index accbd37e33..5bef3d2472 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java @@ -480,6 +480,40 @@ public class LoginTest extends TestRealmKeycloakTest { setRememberMe(false); } } + + @Test + // KEYCLOAK-3181 + public void loginWithEmailUserAndRememberMe() { + setRememberMe(true); + + try { + loginPage.open(); + loginPage.setRememberMe(true); + assertTrue(loginPage.isRememberMeChecked()); + loginPage.login("login@test.com", "password"); + + Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); + EventRepresentation loginEvent = events.expectLogin().user(userId) + .detail(Details.USERNAME, "login@test.com") + .detail(Details.REMEMBER_ME, "true") + .assertEvent(); + String sessionId = loginEvent.getSessionId(); + + // Expire session + testingClient.testing().removeUserSession("test", sessionId); + + // Assert rememberMe checked and username/email prefilled + loginPage.open(); + assertTrue(loginPage.isRememberMeChecked()); + + Assert.assertEquals("login@test.com", loginPage.getUsername()); + + loginPage.setRememberMe(false); + } finally { + setRememberMe(false); + } + } // KEYCLOAK-1037 @Test From bde45eaa07768c4248b9b8552cea5ee535fa35be Mon Sep 17 00:00:00 2001 From: Vlasta Ramik Date: Fri, 9 Sep 2016 10:45:44 +0200 Subject: [PATCH 40/53] fix xsl locator to work with ibmjdk --- .../auth-server/jboss/common/keycloak-server-subsystem.xsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl index 664eecb1ae..d104e37cc8 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl @@ -42,8 +42,8 @@ - - + + From 05792516f64e274658b1ab0d1f064511b61ad32f Mon Sep 17 00:00:00 2001 From: Gian Carlo Pace Date: Thu, 15 Sep 2016 09:47:49 +0200 Subject: [PATCH 41/53] The documentation link was referring the old documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14addf7442..570576c59b 100755 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ To stop the server press `Ctrl + C`. Help and Documentation ---------------------- -* [Documentation](http://keycloak.jboss.org/docs) - User Guide, Admin REST API and Javadocs +* [Documentation](http://www.keycloak.org/documentation.html) - User Guide, Admin REST API and Javadocs * [User Mailing List](https://lists.jboss.org/mailman/listinfo/keycloak-user) - Mailing list to ask for help and general questions about Keycloak * [JIRA](https://issues.jboss.org/projects/KEYCLOAK) - Issue tracker for bugs and feature requests @@ -72,4 +72,4 @@ Contributing License ------- -* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) \ No newline at end of file +* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) From bd2f220736dacef6ca0347fd996caba271aaf5bf Mon Sep 17 00:00:00 2001 From: mwcz Date: Thu, 15 Sep 2016 17:25:29 -0400 Subject: [PATCH 42/53] always resolve keycloak.init's promise --- adapters/oidc/js/src/main/resources/keycloak.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js index 44a7e75729..6f56643f87 100755 --- a/adapters/oidc/js/src/main/resources/keycloak.js +++ b/adapters/oidc/js/src/main/resources/keycloak.js @@ -166,6 +166,8 @@ kc.onAuthError && kc.onAuthError(); if (initOptions.onLoad) { onLoad(); + } else { + initPromise.setError(); } }); }); @@ -177,6 +179,8 @@ kc.onAuthError && kc.onAuthError(); if (initOptions.onLoad) { onLoad(); + } else { + initPromise.setError(); } }); } From 12919223cf826bbd4a51ae4295003da373a9f77a Mon Sep 17 00:00:00 2001 From: mhajas Date: Mon, 12 Sep 2016 12:09:58 +0200 Subject: [PATCH 43/53] KEYCLOAK-3553 All functionality tests to servlets --- .../testsuite/adapter/page/BasicAuth.java | 61 +++++ .../adapter/servlet/BasicAuthServlet.java | 27 ++ .../AbstractDemoServletsAdapterTest.java | 239 +++++++++++++++++- .../basic-auth/META-INF/context.xml | 20 ++ .../basic-auth/WEB-INF/jetty-web.xml | 46 ++++ .../basic-auth/WEB-INF/keycloak-relative.json | 11 + .../basic-auth/WEB-INF/keycloak.json | 11 + .../adapter-test/basic-auth/WEB-INF/web.xml | 46 ++++ .../resources/adapter-test/demorealm.json | 9 + 9 files changed, 466 insertions(+), 4 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuth.java create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/BasicAuthServlet.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/META-INF/context.xml create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/jetty-web.xml create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak-relative.json create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak.json create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/web.xml diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuth.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuth.java new file mode 100644 index 0000000000..8880d8313a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuth.java @@ -0,0 +1,61 @@ +/* + * 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.page; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl; + +import javax.ws.rs.core.UriBuilder; +import java.net.URL; + +/** + * + * @author tkyjovsk + */ +public class BasicAuth extends AbstractPageWithInjectedUrl { + + public static final String DEPLOYMENT_NAME = "basic-auth"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + //EAP6 URL fix + URL fixedUrl = createInjectedURL("basic-auth"); + return fixedUrl != null ? fixedUrl : url; + } + + @Override + public UriBuilder createUriBuilder() { + return super.createUriBuilder() + .userInfo("{user}:{password}") + .path("basic-auth") + .queryParam("value", "{value}"); + } + + public BasicAuth setTemplateValues(String user, String password, String value) { + setUriParameter("user", user); + setUriParameter("password", password); + setUriParameter("value", value); + return this; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/BasicAuthServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/BasicAuthServlet.java new file mode 100644 index 0000000000..3c51343758 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/BasicAuthServlet.java @@ -0,0 +1,27 @@ +package org.keycloak.testsuite.adapter.servlet; + +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @author mhajas + */ +@WebServlet("/basic-auth") +public class BasicAuthServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String value = req.getParameter("value"); + System.out.println("In BasicAuthServlet with value: " + value); + + resp.setContentType("text/plain"); + PrintWriter pw = resp.getWriter(); + pw.printf(value); + pw.flush(); + } + +} 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 02adb84429..065de7768d 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 @@ -17,6 +17,7 @@ package org.keycloak.testsuite.adapter.servlet; +import org.apache.commons.io.FileUtils; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.graphene.page.Page; import org.jboss.shrinkwrap.api.spec.WebArchive; @@ -25,6 +26,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.keycloak.OAuth2Constants; +import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.common.Version; import org.keycloak.common.util.Time; import org.keycloak.constants.AdapterConstants; @@ -33,15 +35,22 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.representations.AccessToken; import org.keycloak.representations.VersionRepresentation; +import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest; import org.keycloak.testsuite.adapter.filter.AdapterActionsFilter; import org.keycloak.testsuite.adapter.page.*; +import org.keycloak.testsuite.admin.ApiUtil; +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.URLAssert; import org.keycloak.testsuite.util.URLUtils; -import org.keycloak.testsuite.util.WaitUtils; import org.keycloak.util.BasicAuthHelper; import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; @@ -51,16 +60,22 @@ import javax.ws.rs.core.Form; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; - +import java.io.File; +import java.io.IOException; import java.net.URI; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.junit.Assert.*; +import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf; import static org.keycloak.testsuite.util.WaitUtils.pause; +import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement; /** * @@ -84,6 +99,16 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd private InputPortal inputPortal; @Page private TokenMinTTLPage tokenMinTTLPage; + @Page + private OAuthGrant oAuthGrantPage; + @Page + private Applications applicationsPage; + @Page + private LoginEvents loginEventsPage; + @Page + private BasicAuth basicAuthPage; + @Page + private Config configPage; @Deployment(name = CustomerPortal.DEPLOYMENT_NAME) protected static WebArchive customerPortal() { @@ -125,6 +150,20 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd return servletDeployment(TokenMinTTLPage.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, TokenMinTTLServlet.class, ErrorServlet.class); } + @Deployment(name = BasicAuth.DEPLOYMENT_NAME) + protected static WebArchive basicAuth() { + return servletDeployment(BasicAuth.DEPLOYMENT_NAME, BasicAuthServlet.class); + } + + @Override + public void setDefaultPageUriParameters() { + super.setDefaultPageUriParameters(); + configPage.setConsoleRealm(DEMO); + loginEventsPage.setConsoleRealm(DEMO); + applicationsPage.setAuthRealm(DEMO); + loginEventsPage.setConsoleRealm(DEMO); + } + @Before public void beforeDemoServletsAdapterTest() { // Delete all cookies from token-min-ttl page to be sure we are logged out @@ -220,7 +259,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd .queryParam(AdapterActionsFilter.RESET_PUBLIC_KEY_PARAM, "true") .build().toString(); driver.navigate().to(timeOffsetUri); - WaitUtils.waitUntilElement(By.tagName("body")).is().visible(); + waitUntilElement(By.tagName("body")).is().visible(); setAdapterAndServerTimeOffset(0, adapterActionsUrl); } @@ -320,7 +359,7 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd demoRealmRep.setSsoSessionIdleTimeout(1); testRealmResource().update(demoRealmRep); - pause(2000); + pause(2000); productPortal.navigateTo(); assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); @@ -382,6 +421,10 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd demoRealmRep.setSsoSessionIdleTimeout(originalIdle); testRealmResource().update(demoRealmRep); + + String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()) + .queryParam(OAuth2Constants.REDIRECT_URI, securePortal.toString()).build("demo").toString(); + driver.navigate().to(logoutUri); } @Test @@ -547,5 +590,193 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd setAdapterAndServerTimeOffset(0, tokenMinTTLPage.toString()); } + @Test + public void testBasicAuth() { + String value = "hello"; + Client client = ClientBuilder.newClient(); + + Response response = client.target(basicAuthPage + .setTemplateValues("mposolda", "password", value).buildUri()).request().get(); + + assertEquals(200, response.getStatus()); + assertEquals(value, response.readEntity(String.class)); + response.close(); + + response = client.target(basicAuthPage + .setTemplateValues("invalid-user", "password", value).buildUri()).request().get(); + assertEquals(401, response.getStatus()); + String readResponse = response.readEntity(String.class); + assertTrue(readResponse.contains("Unauthorized") || readResponse.contains("Status 401")); + response.close(); + + response = client.target(basicAuthPage + .setTemplateValues("admin", "invalid-password", value).buildUri()).request().get(); + assertEquals(401, response.getStatus()); + readResponse = response.readEntity(String.class); + assertTrue(readResponse.contains("Unauthorized") || readResponse.contains("Status 401")); + response.close(); + + client.close(); + } + + @Test + public void grantServerBasedApp() { + ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "customer-portal"); + ClientRepresentation client = clientResource.toRepresentation(); + client.setConsentRequired(true); + clientResource.update(client); + + RealmRepresentation realm = testRealmResource().toRepresentation(); + realm.setEventsEnabled(true); + realm.setEnabledEventTypes(Arrays.asList("REVOKE_GRANT", "LOGIN")); + testRealmResource().update(realm); + + customerPortal.navigateTo(); + + loginPage.form().login("bburke@redhat.com", "password"); + + assertTrue(oAuthGrantPage.isCurrent()); + + oAuthGrantPage.accept(); + + waitUntilElement(By.xpath("//body")).text().contains("Bill Burke"); + waitUntilElement(By.xpath("//body")).text().contains("Stian Thorgersen"); + + applicationsPage.navigateTo(); + applicationsPage.revokeGrantForApplication("customer-portal"); + + customerPortal.navigateTo(); + + assertTrue(oAuthGrantPage.isCurrent()); + + loginEventsPage.navigateTo(); + + if (!testContext.isAdminLoggedIn()) { + loginPage.form().login(adminUser); + testContext.setAdminLoggedIn(true); + } + + loginEventsPage.table().filter(); + loginEventsPage.table().filterForm().addEventType("REVOKE_GRANT"); + loginEventsPage.table().update(); + + List resultList = loginEventsPage.table().rows(); + + assertEquals(1, resultList.size()); + + resultList.get(0).findElement(By.xpath(".//td[text()='REVOKE_GRANT']")); + resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='account']")); + resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']")); + resultList.get(0).findElement(By.xpath(".//td[text()='revoked_client']/../td[text()='customer-portal']")); + + loginEventsPage.table().reset(); + loginEventsPage.table().filterForm().addEventType("LOGIN"); + loginEventsPage.table().update(); + resultList = loginEventsPage.table().rows(); + + assertEquals(1, resultList.size()); + + resultList.get(0).findElement(By.xpath(".//td[text()='LOGIN']")); + resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='customer-portal']")); + resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']")); + resultList.get(0).findElement(By.xpath(".//td[text()='username']/../td[text()='bburke@redhat.com']")); + resultList.get(0).findElement(By.xpath(".//td[text()='consent']/../td[text()='consent_granted']")); + + configPage.navigateTo(); + configPage.form().clearLoginEvents(); + driver.findElement(By.xpath("//div[@class='modal-dialog']//button[text()='Delete']")).click(); + } + + @Test + public void historyOfAccessResourceTest() throws IOException { + RealmRepresentation realm = testRealmResource().toRepresentation(); + realm.setEventsEnabled(true); + realm.setEnabledEventTypes(Arrays.asList("LOGIN", "LOGIN_ERROR", "LOGOUT", "CODE_TO_TOKEN")); + testRealmResource().update(realm); + + customerPortal.navigateTo(); + + testRealmLoginPage.form().login("bburke@redhat.com", "password"); + + waitUntilElement(By.xpath("//body")).text().contains("Bill Burke"); + waitUntilElement(By.xpath("//body")).text().contains("Stian Thorgersen"); + + driver.navigate().to(testRealmPage.getOIDCLogoutUrl() + "?redirect_uri=" + customerPortal); + + loginEventsPage.navigateTo(); + + if (!testContext.isAdminLoggedIn()) { + loginPage.form().login(adminUser); + testContext.setAdminLoggedIn(true); + } + + loginEventsPage.table().filter(); + loginEventsPage.table().filterForm().addEventType("LOGOUT"); + loginEventsPage.table().update(); + + List resultList = loginEventsPage.table().rows(); + + assertEquals(1, resultList.size()); + + resultList.get(0).findElement(By.xpath(".//td[text()='LOGOUT']")); + resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='']")); + resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']")); + + loginEventsPage.table().reset(); + loginEventsPage.table().filterForm().addEventType("LOGIN"); + loginEventsPage.table().update(); + resultList = loginEventsPage.table().rows(); + + assertEquals(1, resultList.size()); + + resultList.get(0).findElement(By.xpath(".//td[text()='LOGIN']")); + resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='customer-portal']")); + resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']")); + resultList.get(0).findElement(By.xpath(".//td[text()='username']/../td[text()='bburke@redhat.com']")); + + loginEventsPage.table().reset(); + loginEventsPage.table().filterForm().addEventType("CODE_TO_TOKEN"); + loginEventsPage.table().update(); + resultList = loginEventsPage.table().rows(); + + assertEquals(1, resultList.size()); + resultList.get(0).findElement(By.xpath(".//td[text()='CODE_TO_TOKEN']")); + resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='customer-portal']")); + resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1' or text()='0:0:0:0:0:0:0:1']")); + resultList.get(0).findElement(By.xpath(".//td[text()='refresh_token_type']/../td[text()='Refresh']")); + + configPage.navigateTo(); + configPage.form().clearLoginEvents(); + driver.findElement(By.xpath("//div[@class='modal-dialog']//button[text()='Delete']")).click(); + + String serverLogPath = null; + + if (System.getProperty("app.server").equals("wildfly") || System.getProperty("app.server").equals("eap6") || System.getProperty("app.server").equals("eap")) { + serverLogPath = System.getProperty("app.server.home") + "/standalone/log/server.log"; + } + + String appServerUrl; + if (Boolean.parseBoolean(System.getProperty("app.server.ssl.required"))) { + appServerUrl = "https://localhost:" + System.getProperty("app.server.https.port", "8543") + "/"; + } else { + appServerUrl = "http://localhost:" + System.getProperty("app.server.http.port", "8280") + "/"; + } + + if (serverLogPath != null) { + log.info("Checking app server log at: " + serverLogPath); + File serverLog = new File(serverLogPath); + String serverLogContent = FileUtils.readFileToString(serverLog); + UserRepresentation bburke = ApiUtil.findUserByUsername(testRealmResource(), "bburke@redhat.com"); + + Pattern pattern = Pattern.compile("User '" + bburke.getId() + "' invoking '" + appServerUrl + "customer-portal[^\\s]+' on client 'customer-portal'"); + Matcher matcher = pattern.matcher(serverLogContent); + + assertTrue(matcher.find()); + assertTrue(serverLogContent.contains("User '" + bburke.getId() + "' invoking '" + appServerUrl + "customer-db/' on client 'customer-db'")); + } else { + log.info("Checking app server log on app-server: \"" + System.getProperty("app.server") + "\" is not supported."); + } + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/META-INF/context.xml new file mode 100644 index 0000000000..e626986964 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/META-INF/context.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/jetty-web.xml new file mode 100644 index 0000000000..8c59313878 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/jetty-web.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak-relative.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak-relative.json new file mode 100644 index 0000000000..e00b8fcea7 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak-relative.json @@ -0,0 +1,11 @@ +{ + "realm" : "demo", + "resource" : "basic-auth-service", + "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url": "/auth", + "ssl-required" : "external", + "enable-basic-auth" : "true", + "credentials": { + "secret": "password" + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak.json new file mode 100644 index 0000000000..e00b8fcea7 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/keycloak.json @@ -0,0 +1,11 @@ +{ + "realm" : "demo", + "resource" : "basic-auth-service", + "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url": "/auth", + "ssl-required" : "external", + "enable-basic-auth" : "true", + "credentials": { + "secret": "password" + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/web.xml new file mode 100644 index 0000000000..0ea56f483f --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/basic-auth/WEB-INF/web.xml @@ -0,0 +1,46 @@ + + + + + + basic-auth + + + + /* + + + + user + + + + + KEYCLOAK + demo + + + + user + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json index 9a4a7f6d55..027258439d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json @@ -226,6 +226,15 @@ "/oauth-client-cdi/*" ], "secret": "password" + }, + { + "clientId": "basic-auth-service", + "standardFlowEnabled": false, + "directAccessGrantsEnabled": true, + "enabled": true, + "adminUrl": "/basic-auth", + "baseUrl": "/basic-auth", + "secret": "password" } ] } From 6bdc9dc1332c6e4cb01e53c475c56474e3cd2a72 Mon Sep 17 00:00:00 2001 From: wyvie Date: Tue, 23 Aug 2016 23:22:25 +0200 Subject: [PATCH 44/53] [KEYCLOAK-3036] Added sssd integration test --- .../integration-arquillian/tests/base/pom.xml | 2 +- .../keycloak/testsuite/pages/LoginPage.java | 7 + .../tests/other/pom.xml | 5 +- .../tests/other/sssd/README.md | 27 ++++ .../tests/other/sssd/pom.xml | 56 ++++++++ .../org/keycloak/testsuite/sssd/SSSDTest.java | 121 ++++++++++++++++++ 6 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/other/sssd/README.md create mode 100644 testsuite/integration-arquillian/tests/other/sssd/pom.xml create mode 100644 testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 15594dae28..df3166ff16 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -153,5 +153,5 @@ - + diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java index 78913d6942..94a8fb6b97 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPage.java @@ -71,6 +71,9 @@ public class LoginPage extends AbstractPage { @FindBy(className = "alert-info") private WebElement loginInfoMessage; + @FindBy(className = "instruction") + private WebElement instruction; + @FindBy(id = "kc-current-locale-link") private WebElement languageText; @@ -128,6 +131,10 @@ public class LoginPage extends AbstractPage { return loginErrorMessage != null ? loginErrorMessage.getText() : null; } + public String getInstruction() { + return instruction != null ? instruction.getText() : null; + } + public String getSuccessMessage() { return loginSuccessMessage != null ? loginSuccessMessage.getText() : null; } diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index 6b0f83c7f1..b2805252e6 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -38,8 +38,9 @@ adapters + sssd - + @@ -65,7 +66,7 @@ - + maven-resources-plugin diff --git a/testsuite/integration-arquillian/tests/other/sssd/README.md b/testsuite/integration-arquillian/tests/other/sssd/README.md new file mode 100644 index 0000000000..03d73a0e82 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/sssd/README.md @@ -0,0 +1,27 @@ +What is this module about? +------------------------- + +This module containes integration tests for testing the SSSD features of Keycloak. + +Prerequisites +------------- + +To run tests inside this module, one needs to have a linux machine configured as an `IPA` client having sssd + service started with infopipe support. + +How does one run the tests? +-------------------------- + +*All the commands are intended to be run from the root `keycloak` project directory.* + +First build the distribution of keycloak: +`mvn clean install -B -DskipTests -Pdistribution` + +It may fail in the end, but it's not a problem as far as it creates a zip distribution of Keycloak inside +distribution/server-dist/target. + +Then build the integration-arquillian-servers-auth-server-wildfly artifact: +`mvn clean install -B -Pauth-server-wildfly -f testsuite/integration-arquillian/servers/pom.xml` + +And then, finally, it's possible to run the tests: +`mvn test -f testsuite/integration-arquillian/tests/other/sssd/ -Pauth-server-wildfly -Psssd-testing` \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/other/sssd/pom.xml b/testsuite/integration-arquillian/tests/other/sssd/pom.xml new file mode 100644 index 0000000000..3388822449 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/sssd/pom.xml @@ -0,0 +1,56 @@ + + + + integration-arquillian-tests-other + org.keycloak.testsuite + 2.2.0-SNAPSHOT + + 4.0.0 + + integration-arquillian-tests-sssd + + SSSD tests + + + **/sssd/**/*Test.java + + + + + + maven-jar-plugin + 2.2 + + + + test-jar + + + + + + + maven-surefire-plugin + + + ${exclude.sssd} + + + + + + + + + + + sssd-testing + + - + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java b/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java new file mode 100644 index 0000000000..b26a8a1963 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java @@ -0,0 +1,121 @@ +package org.keycloak.testsuite.sssd; + +import org.jboss.arquillian.graphene.page.Page; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserFederationProviderRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.pages.LoginPage; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SSSDTest extends AbstractKeycloakTest { + + private static final String DISPLAY_NAME = "Test user federation"; + private static final String PROVIDER_NAME = "sssd"; + private static final String REALM_NAME = "test"; + + private static final String USERNAME = "emily"; + private static final String PASSWORD = "emily123"; + private static final String DEFINITELY_NOT_PASSWORD = "not" + PASSWORD; + + private static final String ADMIN_USERNAME = "admin"; + private static final String ADMIN_PASSWORD = "password"; + + @Page + private LoginPage accountLoginPage; + + @Override + public void addTestRealms(List testRealms) { + RealmRepresentation realm = new RealmRepresentation(); + + realm.setRealm(REALM_NAME); + realm.setEnabled(true); + + testRealms.add(realm); + } + + @Before + public void createUserFederation() { + UserFederationProviderRepresentation userFederation = new UserFederationProviderRepresentation(); + + Map config = new HashMap<>(); + userFederation.setConfig(config); + + userFederation.setDisplayName(DISPLAY_NAME); + userFederation.setPriority(0); + userFederation.setProviderName(PROVIDER_NAME); + + adminClient.realm(REALM_NAME).userFederation().create(userFederation); + } + + @Test + public void testWrongUser() { + log.debug("Testing wrong password for user " + USERNAME); + + driver.navigate().to(getAccountUrl()); + Assert.assertEquals("Browser should be on login page now", "Log in to " + REALM_NAME, driver.getTitle()); + accountLoginPage.login(USERNAME, DEFINITELY_NOT_PASSWORD); + + Assert.assertEquals("Invalid username or password.", accountLoginPage.getError()); + } + + @Test + public void testAdmin() { + log.debug("Testing wrong password for user " + ADMIN_USERNAME); + + driver.navigate().to(getAccountUrl()); + Assert.assertEquals("Browser should be on login page now", "Log in to " + REALM_NAME, driver.getTitle()); + accountLoginPage.login(ADMIN_USERNAME, ADMIN_PASSWORD); + + Assert.assertEquals("Unexpected error when handling authentication request to identity provider.", accountLoginPage.getInstruction()); + } + + @Test + public void testExistingUserLogIn() { + log.debug("Testing correct password"); + + driver.navigate().to(getAccountUrl()); + Assert.assertEquals("Browser should be on login page now", "Log in to " + REALM_NAME, driver.getTitle()); + accountLoginPage.login(USERNAME, PASSWORD); + Assert.assertEquals("Browser should be on account page now, logged in", "Keycloak Account Management", driver.getTitle()); + + testUserGroups(); + } + + private void testUserGroups() { + log.debug("Testing user groups"); + + List users = adminClient.realm(REALM_NAME).users().search(USERNAME, 0, 1); + + Assert.assertTrue("There must be at least one user", users.size() > 0); + Assert.assertEquals("Exactly our test user", USERNAME, users.get(0).getUsername()); + + List groups = adminClient.realm(REALM_NAME).users().get(users.get(0).getId()).groups(); + + Assert.assertEquals("User must have exactly two groups", 2, groups.size()); + boolean wrongGroup = false; + for (GroupRepresentation group : groups) { + if (!group.getName().equalsIgnoreCase("ipausers") && !group.getName().equalsIgnoreCase("testgroup")) { + wrongGroup = true; + break; + } + } + + Assert.assertFalse("There exists some wrong group", wrongGroup); + } + + private String getAccountUrl() { + return getAuthRoot() + "/auth/realms/" + REALM_NAME + "/account"; + } + + private String getAuthRoot() { + return suiteContext.getAuthServerInfo().getContextRoot().toString(); + } +} From 4b1b3a0dda66394b3c32c86ac243a8b26e40999c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 14 Sep 2016 07:42:56 -0300 Subject: [PATCH 45/53] Add Maven Shade for JNA --- federation/sssd/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml index 7d109e82a5..d3e3afbb65 100644 --- a/federation/sssd/pom.xml +++ b/federation/sssd/pom.xml @@ -23,6 +23,20 @@ ${maven.compiler.target} + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + + package + + shade + + + + From c26471faa8d92b6daaae10259d1b04db2ae6d888 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 16 Sep 2016 18:18:18 -0300 Subject: [PATCH 46/53] Tests cases for: user disabled, password and profile changes --- .../integration-arquillian/tests/base/pom.xml | 2 +- .../org/keycloak/testsuite/sssd/SSSDTest.java | 55 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index df3166ff16..15594dae28 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -153,5 +153,5 @@ - + diff --git a/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java b/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java index b26a8a1963..81ffa988d3 100644 --- a/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java +++ b/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDTest.java @@ -2,6 +2,7 @@ package org.keycloak.testsuite.sssd; import org.jboss.arquillian.graphene.page.Page; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.RealmRepresentation; @@ -9,6 +10,9 @@ import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.pages.AccountPasswordPage; +import org.keycloak.testsuite.pages.AccountUpdateProfilePage; import org.keycloak.testsuite.pages.LoginPage; import java.util.HashMap; @@ -23,13 +27,25 @@ public class SSSDTest extends AbstractKeycloakTest { private static final String USERNAME = "emily"; private static final String PASSWORD = "emily123"; + private static final String DISABLED_USER = "david"; + private static final String DISABLED_USER_PASSWORD = "emily123"; + private static final String DEFINITELY_NOT_PASSWORD = "not" + PASSWORD; private static final String ADMIN_USERNAME = "admin"; private static final String ADMIN_PASSWORD = "password"; @Page - private LoginPage accountLoginPage; + protected LoginPage accountLoginPage; + + @Page + protected AccountPasswordPage changePasswordPage; + + @Page + protected AccountUpdateProfilePage profilePage; + + @Rule + public AssertEvents events = new AssertEvents(this); @Override public void addTestRealms(List testRealms) { @@ -66,6 +82,17 @@ public class SSSDTest extends AbstractKeycloakTest { Assert.assertEquals("Invalid username or password.", accountLoginPage.getError()); } + @Test + public void testDisabledUser() { + log.debug("Testing disabled user " + USERNAME); + + driver.navigate().to(getAccountUrl()); + Assert.assertEquals("Browser should be on login page now", "Log in to " + REALM_NAME, driver.getTitle()); + accountLoginPage.login(DISABLED_USER, DISABLED_USER_PASSWORD); + + Assert.assertEquals("Invalid username or password.", accountLoginPage.getError()); + } + @Test public void testAdmin() { log.debug("Testing wrong password for user " + ADMIN_USERNAME); @@ -89,6 +116,32 @@ public class SSSDTest extends AbstractKeycloakTest { testUserGroups(); } + @Test + public void changeReadOnlyProfile() throws Exception { + + profilePage.open(); + accountLoginPage.login(USERNAME, PASSWORD); + + Assert.assertEquals("emily", profilePage.getUsername()); + Assert.assertEquals("Emily", profilePage.getFirstName()); + Assert.assertEquals("Jones", profilePage.getLastName()); + Assert.assertEquals("emily@jones.com", profilePage.getEmail()); + + profilePage.updateProfile("New first", "New last", "new@email.com"); + + Assert.assertEquals("You can't update your account as it is read only.", profilePage.getError()); + } + + @Test + public void changeReadOnlyPassword() { + changePasswordPage.open(); + accountLoginPage.login(USERNAME, PASSWORD); + + changePasswordPage.changePassword(PASSWORD, "new-password", "new-password"); + Assert.assertEquals("You can't update your password as your account is read only.", profilePage.getError()); + } + + private void testUserGroups() { log.debug("Testing user groups"); From 47b742a16e101deff2f9f453b71764092b58224a Mon Sep 17 00:00:00 2001 From: Brett Epps Date: Sun, 18 Sep 2016 13:41:42 -0500 Subject: [PATCH 47/53] Update Angular 2 example to Angular 2.0.0 final release --- .../src/main/webapp/.gitignore | 4 + .../src/main/webapp/app/app.component.ts | 67 ++++++++++++++++ .../src/main/webapp/app/app.module.ts | 20 +++++ .../src/main/webapp/app/app.ts | 78 ------------------- .../src/main/webapp/app/keycloak.service.ts | 48 ++++++++++++ .../src/main/webapp/app/keycloak.ts | 49 ------------ .../src/main/webapp/app/main.ts | 23 +++--- .../src/main/webapp/index.html | 51 ++++-------- .../src/main/webapp/package.json | 36 ++++++--- .../src/main/webapp/systemjs.config.js | 43 ++++++++++ .../src/main/webapp/tsconfig.json | 11 +-- .../src/main/webapp/typings.json | 7 ++ 12 files changed, 240 insertions(+), 197 deletions(-) create mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/.gitignore create mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts create mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts delete mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/app/app.ts create mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.service.ts delete mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.ts create mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/systemjs.config.js create mode 100644 examples/demo-template/angular2-product-app/src/main/webapp/typings.json diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/.gitignore b/examples/demo-template/angular2-product-app/src/main/webapp/.gitignore new file mode 100644 index 0000000000..1c790facbc --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/.gitignore @@ -0,0 +1,4 @@ +app/*.js +app/*.js.map +node_modules +typings diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts new file mode 100644 index 0000000000..d43e25740d --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.component.ts @@ -0,0 +1,67 @@ +import { Component } from '@angular/core'; +import { Http, Headers, RequestOptions, Response } from '@angular/http'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/map'; + +import { KeycloakService } from './keycloak.service'; + +@Component({ + selector: 'my-app', + template: ` +
    +
    +

    Angular2 Product (Beta)

    +

    Products

    + + + + + + + + + + + + + +
    Product Listing
    {{p}}
    +
    +
    +` +}) +export class AppComponent { + products: string[] = []; + + constructor(private http: Http, private kc: KeycloakService) {} + + logout() { + this.kc.logout(); + } + + reloadData() { + //angular dont have http interceptor yet + + this.kc.getToken() + .then(token => { + let headers = new Headers({ + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + token + }); + + let options = new RequestOptions({ headers }); + + this.http.get('/database/products', options) + .map(res => res.json()) + .subscribe(prods => this.products = prods, + error => console.log(error)); + }) + .catch(error => console.log(error)); + } + + private handleError(error: Response) { + console.error(error); + return Observable.throw(error.json().error || 'Server error'); + } +} diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts new file mode 100644 index 0000000000..f345fa3ea6 --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { HttpModule } from '@angular/http'; +import { KeycloakService } from './keycloak.service'; +import { AppComponent } from './app.component'; + +@NgModule({ + imports: [ + BrowserModule, + HttpModule + ], + declarations: [ + AppComponent + ], + providers: [ + KeycloakService, + ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/app.ts deleted file mode 100644 index 8630f006ea..0000000000 --- a/examples/demo-template/angular2-product-app/src/main/webapp/app/app.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {Http, Headers, - RequestOptions, Response} from 'angular2/http'; -import {Component} from 'angular2/core'; -import {Observable} from 'rxjs/Observable'; -import {KeycloakService} from './keycloak'; - - - - -@Component({ - selector: 'my-app', - template: -` -
    -
    -

    Angular2 Product (Beta)

    -

    Products

    - - - - - - - - - - - - - - -
    Product Listing
    {{p}}
    -
    -
    -` -}) -export class AppComponent { - - constructor(private _kc:KeycloakService, private http:Http){ } - - products : string[] = []; - - logout(){ - this._kc.logout(); - } - - reloadData() { - //angular dont have http interceptor yet - - this._kc.getToken().then( - token=>{ - let headers = new Headers({ - 'Accept': 'application/json', - 'Authorization': 'Bearer ' + token - }); - - let options = new RequestOptions({ headers: headers }); - - this.http.get('/database/products', options) - .map(res => res.json()) - .subscribe( - prods => this.products = prods, - error => console.log(error)); - - }, - error=>{ - console.log(error); - } - ); - - } - - private handleError (error: Response) { - console.error(error); - return Observable.throw(error.json().error || 'Server error'); - } - -} diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.service.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.service.ts new file mode 100644 index 0000000000..33fc28357e --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; + +declare var Keycloak: any; + +@Injectable() +export class KeycloakService { + static auth: any = {}; + + static init(): Promise { + let keycloakAuth: any = new Keycloak('keycloak.json'); + KeycloakService.auth.loggedIn = false; + + return new Promise((resolve, reject) => { + keycloakAuth.init({ onLoad: 'login-required' }) + .success(() => { + KeycloakService.auth.loggedIn = true; + KeycloakService.auth.authz = keycloakAuth; + KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/protocol/openid-connect/logout?redirect_uri=/angular2-product/index.html"; + resolve(); + }) + .error(() => { + reject(); + }); + }); + } + + logout() { + console.log('*** LOGOUT'); + KeycloakService.auth.loggedIn = false; + KeycloakService.auth.authz = null; + + window.location.href = KeycloakService.auth.logoutUrl; + } + + getToken(): Promise { + return new Promise((resolve, reject) => { + if (KeycloakService.auth.authz.token) { + KeycloakService.auth.authz.updateToken(5) + .success(() => { + resolve(KeycloakService.auth.authz.token); + }) + .error(() => { + reject('Failed to refresh token'); + }); + } + }); + } +} diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.ts deleted file mode 100644 index 64242ae932..0000000000 --- a/examples/demo-template/angular2-product-app/src/main/webapp/app/keycloak.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {Injectable} from 'angular2/core'; - - -declare var Keycloak: any; - -@Injectable() -export class KeycloakService { - - static auth : any = {}; - - static init() : Promise{ - let keycloakAuth : any = new Keycloak('keycloak.json'); - KeycloakService.auth.loggedIn = false; - - return new Promise((resolve,reject)=>{ - keycloakAuth.init({ onLoad: 'login-required' }) - .success( () => { - KeycloakService.auth.loggedIn = true; - KeycloakService.auth.authz = keycloakAuth; - KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/tokens/logout?redirect_uri=/angular2-product/index.html"; - resolve(null); - }) - .error(()=> { - reject(null); - }); - }); - } - - logout(){ - console.log('*** LOGOUT'); - KeycloakService.auth.loggedIn = false; - KeycloakService.auth.authz = null; - - window.location.href = KeycloakService.auth.logoutUrl; - } - - getToken(): Promise{ - return new Promise((resolve,reject)=>{ - if (KeycloakService.auth.authz.token) { - KeycloakService.auth.authz.updateToken(5).success(function() { - resolve(KeycloakService.auth.authz.token); - }) - .error(function() { - reject('Failed to refresh token'); - }); - } - }); - } -} diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/app/main.ts b/examples/demo-template/angular2-product-app/src/main/webapp/app/main.ts index 73613b2b92..6bf99bfc91 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/app/main.ts +++ b/examples/demo-template/angular2-product-app/src/main/webapp/app/main.ts @@ -1,14 +1,11 @@ -import 'rxjs/Rx'; -import {bootstrap} from 'angular2/platform/browser'; -import {HTTP_BINDINGS} from 'angular2/http'; -import {KeycloakService} from './keycloak'; -import {AppComponent} from './app'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -KeycloakService.init().then( - o=>{ - bootstrap(AppComponent,[HTTP_BINDINGS, KeycloakService]); - }, - x=>{ - window.location.reload(); - } -); \ No newline at end of file +import {KeycloakService} from './keycloak.service'; + +KeycloakService.init() + .then(() => { + const platform = platformBrowserDynamic(); + platform.bootstrapModule(AppModule); + }) + .catch(() => window.location.reload()); diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/index.html b/examples/demo-template/angular2-product-app/src/main/webapp/index.html index 2da600c335..1edeb56477 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/index.html +++ b/examples/demo-template/angular2-product-app/src/main/webapp/index.html @@ -2,48 +2,23 @@ Angular 2 QuickStart + - - - - - - + + + + + + + + + + - Loading... - - - - - - - - - - - - - - - - - - - - diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/package.json b/examples/demo-template/angular2-product-app/src/main/webapp/package.json index f66b44c110..5bd783b03e 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/package.json +++ b/examples/demo-template/angular2-product-app/src/main/webapp/package.json @@ -2,24 +2,36 @@ "name": "angular2-product-app", "version": "1.0.0", "scripts": { + "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ", + "lite": "lite-server", + "postinstall": "typings install", "tsc": "tsc", "tsc:w": "tsc -w", - "lite": "lite-server", - "start": "concurrent \"npm run tsc:w\" \"npm run lite\" " + "typings": "typings" }, "license": "ISC", "dependencies": { - "angular2": "2.0.0-beta.3", - "systemjs": "0.19.6", - "es6-promise": "^3.0.2", - "es6-shim": "^0.33.3", - "reflect-metadata": "0.1.2", - "rxjs": "5.0.0-beta.0", - "zone.js": "0.5.11" + "@angular/common": "2.0.0", + "@angular/compiler": "2.0.0", + "@angular/core": "2.0.0", + "@angular/forms": "2.0.0", + "@angular/http": "2.0.0", + "@angular/platform-browser": "2.0.0", + "@angular/platform-browser-dynamic": "2.0.0", + "@angular/router": "3.0.0", + "@angular/upgrade": "2.0.0", + "angular2-in-memory-web-api": "0.0.20", + "bootstrap": "^3.3.6", + "core-js": "^2.4.1", + "reflect-metadata": "^0.1.3", + "rxjs": "5.0.0-beta.12", + "systemjs": "0.19.27", + "zone.js": "^0.6.21" }, "devDependencies": { - "concurrently": "^1.0.0", - "lite-server": "^2.0.1", - "typescript": "^1.7.5" + "concurrently": "^2.2.0", + "lite-server": "^2.2.2", + "typescript": "^2.0.2", + "typings": "^1.3.2" } } diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/systemjs.config.js b/examples/demo-template/angular2-product-app/src/main/webapp/systemjs.config.js new file mode 100644 index 0000000000..de199e68ce --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/systemjs.config.js @@ -0,0 +1,43 @@ +/** + * System configuration for Angular 2 samples + * Adjust as necessary for your application needs. + */ +(function (global) { + System.config({ + paths: { + // paths serve as alias + 'npm:': 'node_modules/' + }, + // map tells the System loader where to look for things + map: { + // our app is within the app folder + app: 'app', + // angular bundles + '@angular/core': 'npm:@angular/core/bundles/core.umd.js', + '@angular/common': 'npm:@angular/common/bundles/common.umd.js', + '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', + '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', + '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', + '@angular/http': 'npm:@angular/http/bundles/http.umd.js', + '@angular/router': 'npm:@angular/router/bundles/router.umd.js', + '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', + // other libraries + 'rxjs': 'npm:rxjs', + 'angular2-in-memory-web-api': 'npm:angular2-in-memory-web-api', + }, + // packages tells the System loader how to load when no filename and/or no extension + packages: { + app: { + main: './main.js', + defaultExtension: 'js' + }, + rxjs: { + defaultExtension: 'js' + }, + 'angular2-in-memory-web-api': { + main: './index.js', + defaultExtension: 'js' + } + } + }); +})(this); diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/tsconfig.json b/examples/demo-template/angular2-product-app/src/main/webapp/tsconfig.json index 52c77a54a4..e6a6eac11d 100644 --- a/examples/demo-template/angular2-product-app/src/main/webapp/tsconfig.json +++ b/examples/demo-template/angular2-product-app/src/main/webapp/tsconfig.json @@ -1,15 +1,12 @@ { "compilerOptions": { "target": "es5", - "module": "system", + "module": "commonjs", "moduleResolution": "node", - "sourceMap": false, + "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false - }, - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + } +} diff --git a/examples/demo-template/angular2-product-app/src/main/webapp/typings.json b/examples/demo-template/angular2-product-app/src/main/webapp/typings.json new file mode 100644 index 0000000000..7da31ca0af --- /dev/null +++ b/examples/demo-template/angular2-product-app/src/main/webapp/typings.json @@ -0,0 +1,7 @@ +{ + "globalDependencies": { + "core-js": "registry:dt/core-js#0.0.0+20160725163759", + "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", + "node": "registry:dt/node#6.0.0+20160909174046" + } +} From 9f4cf0748909802e0163226c9cd4f2d05a1f49b6 Mon Sep 17 00:00:00 2001 From: Brett Epps Date: Sun, 18 Sep 2016 14:10:58 -0500 Subject: [PATCH 48/53] Add Angular2 product app to be deployed with other demo apps --- examples/demo-template/angular2-product-app/pom.xml | 2 +- examples/demo-template/pom.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/demo-template/angular2-product-app/pom.xml b/examples/demo-template/angular2-product-app/pom.xml index 7eecc429dd..d461196042 100644 --- a/examples/demo-template/angular2-product-app/pom.xml +++ b/examples/demo-template/angular2-product-app/pom.xml @@ -21,7 +21,7 @@ keycloak-examples-demo-parent org.keycloak - + 2.2.0-SNAPSHOT 4.0.0 diff --git a/examples/demo-template/pom.xml b/examples/demo-template/pom.xml index b7fc0274ee..dbc0218250 100755 --- a/examples/demo-template/pom.xml +++ b/examples/demo-template/pom.xml @@ -51,6 +51,7 @@ example-ear admin-access-app angular-product-app + angular2-product-app database-service third-party third-party-cdi From 80cc9b0585d0992c4c2b7a48f9dc862993ab4df2 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Mon, 19 Sep 2016 10:32:40 +0200 Subject: [PATCH 49/53] KEYCLOAK-3578 Remove source distribution --- distribution/downloads/pom.xml | 6 --- distribution/pom.xml | 1 - distribution/src-dist/assembly.xml | 38 ---------------- distribution/src-dist/pom.xml | 69 ------------------------------ pom.xml | 6 --- 5 files changed, 120 deletions(-) delete mode 100755 distribution/src-dist/assembly.xml delete mode 100755 distribution/src-dist/pom.xml diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index 71b27d9b63..e18654557e 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -112,12 +112,6 @@ zip keycloak-examples-${project.version}.zip - - org.keycloak - keycloak-src-dist - zip - keycloak-src-${project.version}.zip - target/${project.version} diff --git a/distribution/pom.xml b/distribution/pom.xml index 74e0e402a2..746d5aeae6 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -40,7 +40,6 @@ proxy-dist server-dist server-overlay - src-dist feature-packs diff --git a/distribution/src-dist/assembly.xml b/distribution/src-dist/assembly.xml deleted file mode 100755 index 5b27a8818c..0000000000 --- a/distribution/src-dist/assembly.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - src - - - zip - - true - - - - ../../ - - - **/.idea/** - **/.svn/** - **/target/** - **/*.iml - - - - diff --git a/distribution/src-dist/pom.xml b/distribution/src-dist/pom.xml deleted file mode 100755 index c1842270eb..0000000000 --- a/distribution/src-dist/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - 4.0.0 - - keycloak-distribution-parent - org.keycloak - 2.2.0-SNAPSHOT - - - keycloak-src-dist - pom - Keycloak Source Distribution - - - - keycloak-src-${project.version} - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - maven-assembly-plugin - - - assemble - package - - single - - - - assembly.xml - - - target - - - target/assembly/work - - false - - - - - - - - diff --git a/pom.xml b/pom.xml index 58e343968e..e1a47ce26b 100755 --- a/pom.xml +++ b/pom.xml @@ -1126,12 +1126,6 @@ ${project.version} zip - - org.keycloak - keycloak-src-dist - ${project.version} - zip - org.keycloak keycloak-proxy-dist From f69eb5503b0fd2dbedc03c7226d74f4042bef046 Mon Sep 17 00:00:00 2001 From: mhajas Date: Tue, 13 Sep 2016 10:11:11 +0200 Subject: [PATCH 50/53] KEYCLOAK-3561 Hardcoded artifact version --- .../photoz-restful-api-authz-service.json | 2 +- .../AbstractPhotozExampleAdapterTest.java | 23 ++++++------- .../EAPDefaultAuthzConfigAdapterTest.java | 30 +++++++++++++++++ .../EAPPhotozExampleAdapterTest.java | 30 +++++++++++++++++ .../EAPServletAuthzAdapterTest.java | 33 +++++++++++++++++++ .../tests/other/adapters/pom.xml | 7 +++- 6 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPDefaultAuthzConfigAdapterTest.java create mode 100644 testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPPhotozExampleAdapterTest.java create mode 100644 testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json index 6547d2fcc4..1ce85dd9dc 100644 --- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json +++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json @@ -50,7 +50,7 @@ "mavenArtifactVersion": "2.1.0-SNAPSHOT", "mavenArtifactId": "photoz-authz-policy", "sessionName": "MainOwnerSession", - "mavenArtifactGroupId": "org.keycloak", + "mavenArtifactGroupId": "org.keycloak.testsuite", "moduleName": "PhotozAuthzOwnerPolicy", "applyPolicies": "[]", "scannerPeriod": "1", diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java index 66702a98a0..9d25421724 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java @@ -23,13 +23,7 @@ import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; 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.ResourcesResource; -import org.keycloak.admin.client.resource.RoleResource; -import org.keycloak.admin.client.resource.UserResource; -import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.admin.client.resource.*; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; @@ -45,11 +39,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -80,8 +70,15 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } @Before - public void beforePhotozExampleAdapterTest() { + public void beforePhotozExampleAdapterTest() throws FileNotFoundException { deleteAllCookiesForClientPage(); + + for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { + if ("Only Owner Policy".equals(policy.getName())) { + policy.getConfig().put("mavenArtifactVersion", System.getProperty("project.version")); + getAuthorizationResource().policies().policy(policy.getId()).update(policy); + } + } } @Override diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPDefaultAuthzConfigAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPDefaultAuthzConfigAdapterTest.java new file mode 100644 index 0000000000..6d63c38935 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPDefaultAuthzConfigAdapterTest.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.keycloak.testsuite.adapter.example.authorization.AbstractDefaultAuthzConfigAdapterTest; +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; + +/** + * + * @author tkyjovsk + */ +@AppServerContainer("app-server-eap") +//@AdapterLibsLocationProperty("adapter.libs.wildfly") +public class EAPDefaultAuthzConfigAdapterTest extends AbstractDefaultAuthzConfigAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPPhotozExampleAdapterTest.java new file mode 100644 index 0000000000..07b9844fc5 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPPhotozExampleAdapterTest.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.keycloak.testsuite.adapter.example.authorization.AbstractPhotozExampleAdapterTest; +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; + +/** + * + * @author tkyjovsk + */ +@AppServerContainer("app-server-eap") +//@AdapterLibsLocationProperty("adapter.libs.wildfly") +public class EAPPhotozExampleAdapterTest extends AbstractPhotozExampleAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java new file mode 100644 index 0000000000..3c789a4b34 --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap/src/test/java/org/keycloak/testsuite/adapter/example/authorization/EAPServletAuthzAdapterTest.java @@ -0,0 +1,33 @@ +/* + * 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.adapter.example.authorization.AbstractDefaultAuthzConfigAdapterTest; +import org.keycloak.testsuite.adapter.example.authorization.AbstractServletAuthzAdapterTest; +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; + +/** + * + * @author tkyjovsk + */ +@RunAsClient +@AppServerContainer("app-server-eap") +//@AdapterLibsLocationProperty("adapter.libs.wildfly") +public class EAPServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index 15fc1c4836..572af7fafa 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -53,7 +53,10 @@ -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m false - + + + + -Dapp.server.base.url=http://localhost:${app.server.http.port} -Dauth.server.base.url=http://localhost:${auth.server.http.port} @@ -63,6 +66,8 @@ -Dauth.server.ssl.required=${auth.server.ssl.required} -Dmy.host.name=localhost -Djava.security.krb5.conf=${project.build.directory}/dependency/kerberos/test-krb5.conf + -Dkie.maven.settings.custom=${settings.path} + -Drepo.url=${repo.url} ${containers.home}/app-server-${app.server} From ba0d71704dd0fc00fd542e4dd666ff41b53025e5 Mon Sep 17 00:00:00 2001 From: mhajas Date: Mon, 19 Sep 2016 15:19:41 +0200 Subject: [PATCH 51/53] KEYCLOAK-3590 Fix includes --- .../integration-arquillian/tests/other/adapters/pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index 300491d6cf..a5ec9d2aaa 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -352,7 +352,6 @@ maven-resources-plugin - 3.0.1 example-realms @@ -367,7 +366,10 @@ ${examples.basedir} true - **/*realm.json,**/testsaml.json + + **/*realm.json + **/testsaml.json + From 992268a8e612d07677e220193c62b6322fd3199d Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 16 Sep 2016 14:19:57 +0200 Subject: [PATCH 52/53] KEYCLOAK-3579 Add ability to define profiles --- .../java/org/keycloak/common/Profile.java | 69 +++++++++++++++++++ .../java/org/keycloak/common/Version.java | 2 + .../resources/keycloak-version.properties | 3 +- .../info/ProfileInfoRepresentation.java | 53 ++++++++++++++ .../info/ServerInfoRepresentation.java | 9 +++ pom.xml | 1 + .../services/resources/RealmsResource.java | 3 + .../resources/admin/ClientResource.java | 20 ++++-- .../admin/info/ServerInfoAdminResource.java | 2 + .../org/keycloak/utils/ProfileHelper.java | 36 ++++++++++ .../org/keycloak/testsuite/ProfileAssume.java | 36 ++++++++++ .../AbstractAuthorizationTest.java | 7 ++ .../AuthorizationDisabledInPreviewTest.java | 50 ++++++++++++++ ...entAuthorizationServicesAvailableTest.java | 65 +++++++++++++++++ .../messages/admin-messages_en.properties | 1 + .../theme/base/admin/resources/js/services.js | 2 +- .../resources/partials/client-detail.html | 2 +- .../admin/resources/partials/server-info.html | 4 ++ .../resources/templates/kc-tabs-client.html | 2 +- 19 files changed, 357 insertions(+), 10 deletions(-) create mode 100755 common/src/main/java/org/keycloak/common/Profile.java create mode 100644 core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java create mode 100644 services/src/main/java/org/keycloak/utils/ProfileHelper.java create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.java create mode 100644 testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java diff --git a/common/src/main/java/org/keycloak/common/Profile.java b/common/src/main/java/org/keycloak/common/Profile.java new file mode 100755 index 0000000000..ac16874f22 --- /dev/null +++ b/common/src/main/java/org/keycloak/common/Profile.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.common; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class Profile { + + private enum ProfileValue { + PRODUCT, PREVIEW, COMMUNITY + } + + private static ProfileValue value = load(); + + static ProfileValue load() { + String profile = null; + try { + profile = System.getProperty("keycloak.profile"); + if (profile == null) { + String jbossServerConfigDir = System.getProperty("jboss.server.config.dir"); + if (jbossServerConfigDir != null) { + File file = new File(jbossServerConfigDir, "profile.properties"); + if (file.isFile()) { + Properties props = new Properties(); + props.load(new FileInputStream(file)); + profile = props.getProperty("profile"); + } + } + } + } catch (Exception e) { + } + + if (profile == null) { + return ProfileValue.valueOf(Version.DEFAULT_PROFILE.toUpperCase()); + } else { + return ProfileValue.valueOf(profile.toUpperCase()); + } + } + + public static String getName() { + return value.name().toLowerCase(); + } + + public static boolean isPreviewEnabled() { + return value.ordinal() >= ProfileValue.PREVIEW.ordinal(); + } + +} diff --git a/common/src/main/java/org/keycloak/common/Version.java b/common/src/main/java/org/keycloak/common/Version.java index 42ba52a816..862ccd2def 100755 --- a/common/src/main/java/org/keycloak/common/Version.java +++ b/common/src/main/java/org/keycloak/common/Version.java @@ -32,6 +32,7 @@ public class Version { public static String VERSION; public static String RESOURCES_VERSION; public static String BUILD_TIME; + public static String DEFAULT_PROFILE; static { Properties props = new Properties(); @@ -40,6 +41,7 @@ public class Version { props.load(is); Version.NAME = props.getProperty("name"); Version.NAME_HTML = props.getProperty("name-html"); + Version.DEFAULT_PROFILE = props.getProperty("default-profile"); Version.VERSION = props.getProperty("version"); Version.BUILD_TIME = props.getProperty("build-time"); Version.RESOURCES_VERSION = Version.VERSION.toLowerCase(); diff --git a/common/src/main/resources/keycloak-version.properties b/common/src/main/resources/keycloak-version.properties index 643b6de4af..f66e436852 100755 --- a/common/src/main/resources/keycloak-version.properties +++ b/common/src/main/resources/keycloak-version.properties @@ -18,4 +18,5 @@ name=${product.name} name-html=${product.name-html} version=${product.version} -build-time=${product.build-time} \ No newline at end of file +build-time=${product.build-time} +default-profile=${product.default-profile} \ No newline at end of file diff --git a/core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java new file mode 100644 index 0000000000..3c474d03ba --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java @@ -0,0 +1,53 @@ +/* + * 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.info; + +import org.keycloak.common.Profile; + +/** + * @author Stian Thorgersen + */ +public class ProfileInfoRepresentation { + + private String name; + private boolean previewEnabled; + + public static ProfileInfoRepresentation create() { + ProfileInfoRepresentation info = new ProfileInfoRepresentation(); + info.setName(Profile.getName()); + info.setPreviewEnabled(Profile.isPreviewEnabled()); + return info; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isPreviewEnabled() { + return previewEnabled; + } + + public void setPreviewEnabled(boolean previewEnabled) { + this.previewEnabled = previewEnabled; + } + +} diff --git a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java index 59d400e485..8c98ce3468 100755 --- a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java @@ -32,6 +32,7 @@ public class ServerInfoRepresentation { private SystemInfoRepresentation systemInfo; private MemoryInfoRepresentation memoryInfo; + private ProfileInfoRepresentation profileInfo; private Map> themes; @@ -66,6 +67,14 @@ public class ServerInfoRepresentation { this.memoryInfo = memoryInfo; } + public ProfileInfoRepresentation getProfileInfo() { + return profileInfo; + } + + public void setProfileInfo(ProfileInfoRepresentation profileInfo) { + this.profileInfo = profileInfo; + } + public Map> getThemes() { return themes; } diff --git a/pom.xml b/pom.xml index 58e343968e..b635dc8919 100755 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ \u003Cdiv class="kc-logo-text"\u003E\u003Cspan\u003EKeycloak\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E ${project.version} ${timestamp} + community 7.0.0.Beta diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index 1e42e7b740..d0ca449de0 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -35,6 +35,7 @@ import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resource.RealmResourceProvider; import org.keycloak.services.util.CacheControlUtil; import org.keycloak.services.util.ResolveRelative; +import org.keycloak.utils.ProfileHelper; import org.keycloak.wellknown.WellKnownProvider; import javax.ws.rs.GET; @@ -254,6 +255,8 @@ public class RealmsResource { @Path("{realm}/authz") public Object getAuthorizationService(@PathParam("realm") String name) { + ProfileHelper.requirePreview(); + init(name); AuthorizationProvider authorization = this.session.getProvider(AuthorizationProvider.class); AuthorizationService service = new AuthorizationService(authorization); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index d208b50711..955dff0144 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -18,9 +18,9 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; -import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.admin.AuthorizationService; +import org.keycloak.common.Profile; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; @@ -52,10 +52,12 @@ import org.keycloak.common.util.Time; import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; +import org.keycloak.utils.ProfileHelper; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -153,10 +155,12 @@ public class ClientResource { RepresentationToModel.updateClient(rep, client); - if (TRUE.equals(rep.getAuthorizationServicesEnabled())) { - authorization().enable(); - } else { - authorization().disable(); + if (Profile.isPreviewEnabled()) { + if (TRUE.equals(rep.getAuthorizationServicesEnabled())) { + authorization().enable(); + } else { + authorization().disable(); + } } } @@ -177,7 +181,9 @@ public class ClientResource { ClientRepresentation representation = ModelToRepresentation.toRepresentation(client); - representation.setAuthorizationServicesEnabled(authorization().isEnabled()); + if (Profile.isPreviewEnabled()) { + representation.setAuthorizationServicesEnabled(authorization().isEnabled()); + } return representation; } @@ -562,6 +568,8 @@ public class ClientResource { @Path("/authz") public AuthorizationService authorization() { + ProfileHelper.requirePreview(); + AuthorizationService resource = new AuthorizationService(this.session, this.client, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java index a7195e12c7..638e6570d4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java @@ -43,6 +43,7 @@ import org.keycloak.policy.PasswordPolicyProviderFactory; import org.keycloak.provider.*; import org.keycloak.representations.idm.ComponentTypeRepresentation; import org.keycloak.representations.idm.PasswordPolicyTypeRepresentation; +import org.keycloak.representations.info.ProfileInfoRepresentation; import org.keycloak.theme.Theme; import org.keycloak.theme.ThemeProvider; import org.keycloak.models.KeycloakSession; @@ -84,6 +85,7 @@ public class ServerInfoAdminResource { ServerInfoRepresentation info = new ServerInfoRepresentation(); info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp())); info.setMemoryInfo(MemoryInfoRepresentation.create()); + info.setProfileInfo(ProfileInfoRepresentation.create()); setSocialProviders(info); setIdentityProviders(info); diff --git a/services/src/main/java/org/keycloak/utils/ProfileHelper.java b/services/src/main/java/org/keycloak/utils/ProfileHelper.java new file mode 100644 index 0000000000..719bd24e19 --- /dev/null +++ b/services/src/main/java/org/keycloak/utils/ProfileHelper.java @@ -0,0 +1,36 @@ +/* + * 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.utils; + +import org.keycloak.common.Profile; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +/** + * @author Stian Thorgersen + */ +public class ProfileHelper { + + public static void requirePreview() { + if (!Profile.isPreviewEnabled()) { + throw new WebApplicationException("Feature not available in current profile", Response.Status.NOT_IMPLEMENTED); + } + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java new file mode 100644 index 0000000000..7fbf59b068 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java @@ -0,0 +1,36 @@ +/* + * 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; + +import org.junit.Assume; +import org.keycloak.common.Profile; + +/** + * @author Stian Thorgersen + */ +public class ProfileAssume { + + public static void assumePreview() { + Assume.assumeTrue("Ignoring test as community/preview profile is not enabled", Profile.isPreviewEnabled()); + } + + public static void assumePreviewDisabled() { + Assume.assumeFalse("Ignoring test as community/preview profile is enabled", Profile.isPreviewEnabled()); + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java index c4979e0f67..2c3aac71e0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java @@ -19,11 +19,13 @@ package org.keycloak.testsuite.admin.client.authorization; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ResourceScopeResource; import org.keycloak.admin.client.resource.ResourceScopesResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.admin.client.AbstractClientTest; import javax.ws.rs.core.Response; @@ -38,6 +40,11 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest { protected static final String RESOURCE_SERVER_CLIENT_ID = "test-resource-server"; + @BeforeClass + public static void enabled() { + ProfileAssume.assumePreview(); + } + @Before public void onBeforeAuthzTests() { createOidcClient(RESOURCE_SERVER_CLIENT_ID); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.java new file mode 100644 index 0000000000..072aa5f630 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.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.testsuite.admin.client.authorization; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.admin.client.AbstractClientTest; + +import javax.ws.rs.ServerErrorException; +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; + +/** + * @author Stian Thorgersen + */ +public class AuthorizationDisabledInPreviewTest extends AbstractClientTest { + + @BeforeClass + public static void enabled() { + ProfileAssume.assumePreviewDisabled(); + } + + @Test + public void testAuthzServicesRemoved() { + String id = testRealmResource().clients().findAll().get(0).getId(); + try { + testRealmResource().clients().get(id).authorization().getSettings(); + } catch (ServerErrorException e) { + assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), e.getResponse().getStatus()); + } + } + +} diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java new file mode 100644 index 0000000000..0dcf08bf9d --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java @@ -0,0 +1,65 @@ +/* + * 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.clients; + +import org.jboss.arquillian.graphene.page.Page; +import org.junit.Test; +import org.keycloak.common.Profile; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.console.page.clients.settings.ClientSettings; +import org.openqa.selenium.By; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.keycloak.testsuite.auth.page.login.Login.OIDC; + +/** + * + * @author Vlastislav Ramik + */ +public class ClientAuthorizationServicesAvailableTest extends AbstractClientTest { + + private ClientRepresentation newClient; + + @Page + private ClientSettings clientSettingsPage; + + @Test + public void authzServicesAvailable() { + ProfileAssume.assumePreview(); + + newClient = createClientRep("oidc-public", OIDC); + createClient(newClient); + assertEquals("oidc-public", clientSettingsPage.form().getClientId()); + + assertTrue(driver.findElement(By.xpath("//*[@for='authorizationServicesEnabled']")).isDisplayed()); + } + + @Test + public void authzServicesUnavailable() throws InterruptedException { + ProfileAssume.assumePreviewDisabled(); + + newClient = createClientRep("oidc-public", OIDC); + createClient(newClient); + assertEquals("oidc-public", clientSettingsPage.form().getClientId()); + + assertFalse(driver.findElement(By.xpath("//*[@for='authorizationServicesEnabled']")).isDisplayed()); + + } +} \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index ea3426890f..d3cdd9ee82 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -851,6 +851,7 @@ include-representation=Include Representation include-representation.tooltip=Include JSON representation for create and update requests. clear-admin-events.tooltip=Deletes all admin events in the database. server-version=Server Version +server-profile=Server Profile info=Info providers=Providers server-time=Server Time diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js index 6d8fdcdcf1..a063143fff 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -274,7 +274,7 @@ module.service('ServerInfo', function($resource, $q, $http) { var delay = $q.defer(); $http.get(authUrl + '/admin/serverinfo').success(function(data) { - info = data; + angular.copy(data, info); delay.resolve(info); }); diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index d8f0d246a1..9fb3a2d5be 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -110,7 +110,7 @@
  • -
    +
    {{:: 'authz-authorization-services-enabled.tooltip' | translate}}
    diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html b/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html index 0607250031..299a93b3c9 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html @@ -14,6 +14,10 @@
    {{:: 'server-version' | translate}} {{serverInfo.systemInfo.version}}
    {{:: 'server-profile' | translate}}{{serverInfo.profileInfo.name}}
    {{:: 'server-time' | translate}} {{serverInfo.systemInfo.serverTime}}