Incompatible method of admin-client in Keycloak 26.1 and missing javadoc (#36091)

closes #36090

Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
Marek Posolda 2024-12-20 16:35:51 +01:00 committed by GitHub
parent 4ab34f4816
commit 0d8a23b684
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 52 additions and 40 deletions

View File

@ -114,9 +114,25 @@ public interface ClientResource {
@Path("certificates/{attr}")
ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix);
/**
* Return installation provider as a String. String is typically XML format specific to the requested provider
*
* @param providerId installation provider ID
* @return response as a string
*/
@GET
@Path("installation/providers/{providerId}")
Response getInstallationProvider(@PathParam("providerId") String providerId);
String getInstallationProvider(@PathParam("providerId") String providerId);
/**
* Return installation provider as a response
*
* @param providerId installation provider ID
* @return Jakarta response
*/
@GET
@Path("installation/providers/{providerId}")
Response getInstallationProviderAsResponse(@PathParam("providerId") String providerId);
@Path("session-count")
@GET

View File

@ -91,10 +91,11 @@ public interface OrganizationMembersResource {
* @param exact if {@code true}, the members will be searched using exact match for the {@code search} param - i.e.
* at least one of the username main attributes must match exactly the {@code search} param. If false,
* the method returns all members with at least one main attribute partially matching the {@code search} param.
* @param membershipType The {@link org.keycloak.representations.idm.MembershipType}.
* @param membershipType The {@link org.keycloak.representations.idm.MembershipType}. The parameter is supported since Keycloak 26.1
* @param first index of the first element (pagination offset).
* @param max the maximum number of results.
* @return a list containing the matched organization members.
* @since Keycloak 26.1. Use method {@link #search(String, Boolean, Integer, Integer)} for the older versions of the Keycloak server
*/
@GET
@Produces(MediaType.APPLICATION_JSON)

View File

@ -108,8 +108,9 @@ public interface OrganizationsResource {
* the method returns all organizations whose name or (domains) partially match the {@code search} param.
* @param first the position of the first result to be processed (pagination offset). Ignored if negative or {@code null}.
* @param max the maximum number of results to be returned. Ignored if negative or {@code null}.
* @param briefRepresentation if {@code true} the full representation is to be returned. Otherwise, only the basic fields are returned.
* @param briefRepresentation if {@code true} the full representation is to be returned. Otherwise, only the basic fields are returned. The parameter is supported since Keycloak 26.1
* @return a list containing the matched organizations.
* @since Keycloak 26.1. Use method {@link #search(String, Boolean, Integer, Integer)} for the older versions of the Keycloak server
*/
@GET
@Produces(MediaType.APPLICATION_JSON)

View File

@ -45,7 +45,6 @@ public class SamlUtils {
.map(ClientRepresentation::getId)
.map(res::get)
.map(clientResource -> clientResource.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR))
.map(response -> response.readEntity(String.class))
.orElseThrow(() -> new RuntimeException("Missing descriptor"));
SAMLParser parser = SAMLParser.getInstance();

View File

@ -552,10 +552,9 @@ public class PermissionsTest extends AbstractKeycloakTest {
realm.clients().get(foo.getId()).toRepresentation();
}
}, Resource.CLIENT, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> responseRef) {
Response response = realm.clients().get(foo.getId()).getInstallationProvider("nosuch");
responseRef.set(response);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.clients().get(foo.getId()).getInstallationProvider("nosuch");
}
}, Resource.CLIENT, false);
invoke(new Invocation() {

View File

@ -28,8 +28,11 @@ import java.util.zip.ZipInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
@ -51,7 +54,6 @@ import org.xml.sax.SAXException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.keycloak.common.Profile.Feature.AUTHORIZATION;
@ -110,31 +112,27 @@ public class InstallationTest extends AbstractClientTest {
@Test
public void testOidcJBossXml() {
Response response = oidcClient.getInstallationProvider("keycloak-oidc-jboss-subsystem");
String xml = response.readEntity(String.class);
String xml = oidcClient.getInstallationProvider("keycloak-oidc-jboss-subsystem");
assertOidcInstallationConfig(xml);
assertThat(xml, containsString("<secure-deployment"));
}
@Test
public void testOidcJson() {
Response response = oidcClient.getInstallationProvider("keycloak-oidc-keycloak-json");
String json = response.readEntity(String.class);
String json = oidcClient.getInstallationProvider("keycloak-oidc-keycloak-json");
assertOidcInstallationConfig(json);
}
@Test
public void testOidcJBossCli() {
Response response = oidcClient.getInstallationProvider("keycloak-oidc-jboss-subsystem-cli");
String cli = response.readEntity(String.class);
String cli = oidcClient.getInstallationProvider("keycloak-oidc-jboss-subsystem-cli");
assertOidcInstallationConfig(cli);
assertThat(cli, containsString("/subsystem=keycloak/secure-deployment=\"WAR MODULE NAME.war\""));
}
@Test
public void testOidcBearerOnlyJson() {
Response response = oidcBearerOnlyClient.getInstallationProvider("keycloak-oidc-keycloak-json");
String json = response.readEntity(String.class);
String json = oidcBearerOnlyClient.getInstallationProvider("keycloak-oidc-keycloak-json");
assertOidcInstallationConfig(json);
assertThat(json, containsString("bearer-only"));
assertThat(json, not(containsString("public-client")));
@ -147,8 +145,7 @@ public class InstallationTest extends AbstractClientTest {
// Generate audience client scope
String clientScopeId = testingClient.testing().generateAudienceClientScope("test", OIDC_NAME_BEARER_ONLY_NAME);
Response response = oidcBearerOnlyClient.getInstallationProvider("keycloak-oidc-keycloak-json");
String json = response.readEntity(String.class);
String json = oidcBearerOnlyClient.getInstallationProvider("keycloak-oidc-keycloak-json");
assertOidcInstallationConfig(json);
assertThat(json, containsString("bearer-only"));
assertThat(json, not(containsString("public-client")));
@ -167,8 +164,7 @@ public class InstallationTest extends AbstractClientTest {
oidcBearerOnlyClientWithAuthzId = createOidcConfidentialClientWithAuthz(OIDC_NAME_BEARER_ONLY_WITH_AUTHZ_NAME);
oidcBearerOnlyClientWithAuthz = findClientResource(OIDC_NAME_BEARER_ONLY_WITH_AUTHZ_NAME);
Response response = oidcBearerOnlyClientWithAuthz.getInstallationProvider("keycloak-oidc-keycloak-json");
String json = response.readEntity(String.class);
String json = oidcBearerOnlyClientWithAuthz.getInstallationProvider("keycloak-oidc-keycloak-json");
assertOidcInstallationConfig(json);
assertThat(json, not(containsString("bearer-only")));
assertThat(json, not(containsString("public-client")));
@ -187,14 +183,17 @@ public class InstallationTest extends AbstractClientTest {
@Test
public void testSamlMetadataIdpDescriptor() {
Response response = samlClient.getInstallationProvider("saml-idp-descriptor");
assertEquals(404, response.getStatus());
try {
samlClient.getInstallationProvider("saml-idp-descriptor");
Assert.fail("Successful saml-idp-descriptor not expected");
} catch (NotFoundException nfe) {
// Expected
}
}
@Test
public void testSamlAdapterXml() {
Response response = samlClient.getInstallationProvider("keycloak-saml");
String xml = response.readEntity(String.class);
String xml = samlClient.getInstallationProvider("keycloak-saml");
assertThat(xml, containsString("<keycloak-saml-adapter>"));
assertThat(xml, containsString("SPECIFY YOUR entityID!"));
assertThat(xml, not(containsString(KeyUtils.findActiveSigningKey(testRealmResource()).getCertificate())));
@ -203,8 +202,7 @@ public class InstallationTest extends AbstractClientTest {
@Test
public void testSamlAdapterCli() {
Response response = samlClient.getInstallationProvider("keycloak-saml-subsystem-cli");
String cli = response.readEntity(String.class);
String cli = samlClient.getInstallationProvider("keycloak-saml-subsystem-cli");
assertThat(cli, containsString("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/"));
assertThat(cli, containsString("SPECIFY YOUR entityID!"));
assertThat(cli, not(containsString(KeyUtils.findActiveSigningKey(testRealmResource()).getCertificate())));
@ -213,8 +211,7 @@ public class InstallationTest extends AbstractClientTest {
@Test
public void testSamlMetadataSpDescriptor() throws Exception {
Response response = samlClient.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
String xml = response.readEntity(String.class);
String xml = samlClient.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
Document doc = getDocumentFromXmlString(xml);
assertElements(doc, METADATA_NSURI.get(), "EntityDescriptor", null);
assertElements(doc, METADATA_NSURI.get(), "SPSSODescriptor", null);
@ -223,8 +220,7 @@ public class InstallationTest extends AbstractClientTest {
@Test
public void testSamlJBossXml() {
Response response = samlClient.getInstallationProvider("keycloak-saml-subsystem");
String xml = response.readEntity(String.class);
String xml = samlClient.getInstallationProvider("keycloak-saml-subsystem");
assertThat(xml, containsString("<secure-deployment"));
assertThat(xml, containsString("SPECIFY YOUR entityID!"));
assertThat(xml, not(containsString(KeyUtils.findActiveSigningKey(testRealmResource()).getCertificate())));
@ -238,8 +234,8 @@ public class InstallationTest extends AbstractClientTest {
assertThat(updater.getResource().toRepresentation().getAttributes().get(SamlConfigAttributes.SAML_FORCE_POST_BINDING), equalTo("true"));
//error fallback
Response response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
Document doc = getDocumentFromXmlString(response.readEntity(String.class));
String response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
Document doc = getDocumentFromXmlString(response);
Map<String, String> attrNamesAndValues = new HashMap<>();
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "ERROR:ENDPOINT_NOT_SET");
@ -251,7 +247,7 @@ public class InstallationTest extends AbstractClientTest {
updater.setAdminUrl("https://admin-url").update();
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT);
response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
doc = getDocumentFromXmlString(response.readEntity(String.class));
doc = getDocumentFromXmlString(response);
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "https://admin-url");
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
@ -267,7 +263,7 @@ public class InstallationTest extends AbstractClientTest {
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT);
response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
doc = getDocumentFromXmlString(response.readEntity(String.class));
doc = getDocumentFromXmlString(response);
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "https://saml-logout-post-url");
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
@ -289,8 +285,8 @@ public class InstallationTest extends AbstractClientTest {
assertThat(updater.getResource().toRepresentation().getAttributes().get(SamlConfigAttributes.SAML_FORCE_POST_BINDING), equalTo("false"));
//error fallback
Response response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
Document doc = getDocumentFromXmlString(response.readEntity(String.class));
String response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
Document doc = getDocumentFromXmlString(response);
Map<String, String> attrNamesAndValues = new HashMap<>();
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "ERROR:ENDPOINT_NOT_SET");
@ -302,7 +298,7 @@ public class InstallationTest extends AbstractClientTest {
updater.setAdminUrl("https://admin-url").update();
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT);
response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
doc = getDocumentFromXmlString(response.readEntity(String.class));
doc = getDocumentFromXmlString(response);
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "https://admin-url");
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
@ -317,7 +313,7 @@ public class InstallationTest extends AbstractClientTest {
.update();
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT);
response = updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
doc = getDocumentFromXmlString(response.readEntity(String.class));
doc = getDocumentFromXmlString(response);
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "https://saml-logout-redirect-url");
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
@ -331,7 +327,7 @@ public class InstallationTest extends AbstractClientTest {
@Test
public void testPemsInModAuthMellonExportShouldBeFormattedInRfc7468() throws IOException {
Response response = samlClient.getInstallationProvider("mod-auth-mellon");
Response response = samlClient.getInstallationProviderAsResponse("mod-auth-mellon");
byte[] result = response.readEntity(byte[].class);
String clientPrivateKey = null;