1515 * limitations under the License.
1616 */
1717
18- package org .keycloak .testsuite .admin ;
18+ package org .keycloak .tests .admin ;
1919
20+ import jakarta .ws .rs .core .Response ;
2021import org .apache .commons .lang3 .RandomStringUtils ;
2122import org .apache .commons .lang3 .StringUtils ;
2223import org .apache .commons .lang3 .concurrent .BasicThreadFactory ;
2324import org .hamcrest .Matchers ;
24- import org .junit .Before ;
25- import org .junit .Test ;
25+ import org .jboss .logging .Logger ;
26+ import org .junit .jupiter .api .BeforeEach ;
27+ import org .junit .jupiter .api .Test ;
2628import org .keycloak .admin .client .resource .ComponentResource ;
2729import org .keycloak .admin .client .resource .ComponentsResource ;
2830import org .keycloak .admin .client .resource .RealmResource ;
2931import org .keycloak .common .util .MultivaluedHashMap ;
32+ import org .keycloak .component .ComponentModel ;
33+ import org .keycloak .models .RealmModel ;
3034import org .keycloak .models .utils .KeycloakModelUtils ;
31- import org .keycloak .representations .idm .*;
32- import org .keycloak .testsuite .components .TestProvider ;
35+ import org .keycloak .provider .ProviderFactory ;
36+ import org .keycloak .representations .idm .AdminEventRepresentation ;
37+ import org .keycloak .representations .idm .ComponentRepresentation ;
38+ import org .keycloak .representations .idm .ErrorRepresentation ;
39+ import org .keycloak .testframework .annotations .InjectAdminEvents ;
40+ import org .keycloak .testframework .annotations .InjectRealm ;
41+ import org .keycloak .testframework .annotations .KeycloakIntegrationTest ;
42+ import org .keycloak .testframework .events .AdminEvents ;
43+ import org .keycloak .testframework .injection .LifeCycle ;
44+ import org .keycloak .testframework .realm .ManagedRealm ;
45+ import org .keycloak .testframework .remote .providers .runonserver .FetchOnServer ;
46+ import org .keycloak .testframework .remote .providers .runonserver .FetchOnServerWrapper ;
47+ import org .keycloak .testframework .remote .runonserver .InjectRunOnServer ;
48+ import org .keycloak .testframework .remote .runonserver .RunOnServerClient ;
49+ import org .keycloak .testframework .server .KeycloakServerConfig ;
50+ import org .keycloak .testframework .server .KeycloakServerConfigBuilder ;
51+ import org .keycloak .testsuite .components .TestComponentProvider ;
52+ import org .keycloak .testsuite .components .TestComponentProviderFactory ;
53+ import org .keycloak .tests .utils .admin .ApiUtil ;
3354
34- import jakarta .ws .rs .WebApplicationException ;
35- import jakarta .ws .rs .core .Response ;
3655import java .util .Collections ;
3756import java .util .List ;
3857import java .util .Map ;
4160import java .util .concurrent .Executors ;
4261import java .util .concurrent .TimeUnit ;
4362import java .util .function .BiConsumer ;
63+ import java .util .stream .Collectors ;
4464
4565import static org .hamcrest .MatcherAssert .assertThat ;
4666import static org .hamcrest .Matchers .contains ;
4767import static org .hamcrest .Matchers .containsString ;
4868import static org .hamcrest .Matchers .equalTo ;
4969import static org .hamcrest .Matchers .not ;
50- import static org .junit .Assert .assertEquals ;
51- import static org .junit .Assert .assertFalse ;
52- import static org .junit .Assert .assertNotNull ;
53- import static org .junit .Assert .assertTrue ;
54- import static org .junit .Assert .fail ;
70+ import static org .junit .jupiter . api . Assertions .assertEquals ;
71+ import static org .junit .jupiter . api . Assertions .assertFalse ;
72+ import static org .junit .jupiter . api . Assertions .assertNotNull ;
73+ import static org .junit .jupiter . api . Assertions .assertTrue ;
74+ import static org .junit .jupiter . api . Assertions .fail ;
5575
5676/**
5777 * @author <a href="mailto:[email protected] ">Stian Thorgersen</a> 5878 */
59- public class ComponentsTest extends AbstractAdminTest {
79+ @ KeycloakIntegrationTest (config = ComponentsTest .ComponentsTestServerConfig .class )
80+ public class ComponentsTest {
81+
82+ @ InjectRealm (lifecycle = LifeCycle .METHOD )
83+ ManagedRealm managedRealm ;
84+
85+ @ InjectRunOnServer
86+ RunOnServerClient runOnServer ;
87+
88+ @ InjectAdminEvents
89+ AdminEvents adminEvents ;
90+
91+ private static final Logger log = Logger .getLogger (ComponentsTest .class );
6092
6193 private ComponentsResource components ;
6294
63- @ Before
95+ @ BeforeEach
6496 public void before () throws Exception {
65- components = adminClient . realm ( REALM_NAME ).components ();
97+ components = managedRealm . admin ( ).components ();
6698 }
6799
68100 private volatile CountDownLatch remainingDeleteSubmissions ;
@@ -81,9 +113,9 @@ private void testConcurrency(BiConsumer<ExecutorService, Integer> taskCreator) t
81113 }
82114
83115 try {
84- assertTrue ("Did not create all components in time" , this . remainingDeleteSubmissions . await ( 100 , TimeUnit . SECONDS ) );
116+ assertTrue (this . remainingDeleteSubmissions . await ( 100 , TimeUnit . SECONDS ), "Did not create all components in time" );
85117 s .shutdown ();
86- assertTrue ("Did not finish before timeout" , s .awaitTermination (100 , TimeUnit .SECONDS ));
118+ assertTrue (s .awaitTermination (100 , TimeUnit .SECONDS ), "Did not finish before timeout" );
87119 } finally {
88120 s .shutdownNow ();
89121 }
@@ -116,7 +148,7 @@ public void testNotDeadlocked() {
116148 rep .getConfig ().putSingle ("required" , "required-value" );
117149 createComponent (rep );
118150
119- List <ComponentRepresentation > list = realm . components () .query (realmId , TestProvider .class .getName ());
151+ List <ComponentRepresentation > list = components .query (managedRealm . getId (), TestComponentProvider .class .getName ());
120152 assertEquals (i + 1 , list .size ());
121153 }
122154 }
@@ -126,21 +158,13 @@ public void testCreateValidation() {
126158 ComponentRepresentation rep = createComponentRepresentation ("mycomponent" );
127159
128160 // Check validation is invoked
129- try {
130- createComponent (rep );
131- } catch (WebApplicationException e ) {
132- assertError (e .getResponse (), "'Required' is required" );
133- }
161+ createComponentAndAssertError (rep , "'Required' is required" );
134162
135163 rep .getConfig ().putSingle ("required" , "Required" );
136164 rep .getConfig ().putSingle ("number" , "invalid" );
137165
138166 // Check validation is invoked
139- try {
140- createComponent (rep );
141- } catch (WebApplicationException e ) {
142- assertError (e .getResponse (), "'Number' should be a number" );
143- }
167+ createComponentAndAssertError (rep , "'Number' should be a number" );
144168 }
145169
146170 @ Test
@@ -193,8 +217,6 @@ public void failCreateWithLongName() {
193217
194218 rep .getConfig ().addFirst ("required" , "foo" );
195219
196- ComponentsResource components = realm .components ();
197-
198220 try (Response response = components .add (rep )) {
199221 if (Response .Status .INTERNAL_SERVER_ERROR .getStatusCode () == response .getStatus ()) {
200222 // using database should fail due to constraint violations
@@ -290,11 +312,11 @@ public void testSecretConfig() throws Exception {
290312 assertEquals (ComponentRepresentation .SECRET_VALUE , returned .getConfig ().getFirst ("secret" ));
291313
292314 // Check secret not leaked in admin events
293- AdminEventRepresentation event = testingClient . testing (). pollAdminEvent ();
315+ AdminEventRepresentation event = adminEvents . poll ();
294316 assertFalse (event .getRepresentation ().contains ("some secret value!!" ));
295317 assertTrue (event .getRepresentation ().contains (ComponentRepresentation .SECRET_VALUE ));
296318
297- Map <String , TestProvider .DetailsRepresentation > details = testingClient . testing ( REALM_NAME ). getTestComponentDetails ();
319+ Map <String , TestComponentProvider .DetailsRepresentation > details = runOnServer . fetch ( new TestComponents ()). componentsDetailsMap ();
298320
299321 // Check value is set correctly
300322 assertEquals ("some secret value!!" , details .get ("mycomponent" ).getConfig ().get ("secret" ).get (0 ));
@@ -306,19 +328,19 @@ public void testSecretConfig() throws Exception {
306328 assertEquals (ComponentRepresentation .SECRET_VALUE , returned2 .getConfig ().getFirst ("secret" ));
307329
308330 // Check secret not leaked in admin events
309- event = testingClient . testing (). pollAdminEvent ();
331+ event = adminEvents . poll ();
310332 assertThat (event .getRepresentation (), not (containsString ("some secret value!!" )));
311333 assertThat (event .getRepresentation (), containsString (ComponentRepresentation .SECRET_VALUE ));
312334
313335 // Check secret value is not set to '*********'
314- details = testingClient . testing ( REALM_NAME ). getTestComponentDetails ();
336+ details = runOnServer . fetch ( new TestComponents ()). componentsDetailsMap ();
315337 assertEquals ("some secret value!!" , details .get ("mycomponent" ).getConfig ().get ("secret" ).get (0 ));
316338
317339 returned2 .getConfig ().putSingle ("secret" , "updated secret value!!" );
318340 components .component (id ).update (returned2 );
319341
320342 // Check secret value is updated
321- details = testingClient . testing ( REALM_NAME ). getTestComponentDetails ();
343+ details = runOnServer . fetch ( new TestComponents ()). componentsDetailsMap ();
322344 assertEquals ("updated secret value!!" , details .get ("mycomponent" ).getConfig ().get ("secret" ).get (0 ));
323345
324346 ComponentRepresentation returned3 = components .query ().stream ().filter (c -> c .getId ().equals (returned2 .getId ())).findFirst ().get ();
@@ -329,7 +351,7 @@ public void testSecretConfig() throws Exception {
329351 components .component (id ).update (returned2 );
330352
331353 // Check secret value is updated
332- details = testingClient . testing ( REALM_NAME ). getTestComponentDetails ();
354+ details = runOnServer . fetch ( new TestComponents ()). componentsDetailsMap ();
333355 assertThat (details .get ("mycomponent" ).getConfig ().get ("secret" ), contains ("${vault.value}" ));
334356
335357 ComponentRepresentation returned4 = components .query ().stream ().filter (c -> c .getId ().equals (returned2 .getId ())).findFirst ().get ();
@@ -381,27 +403,20 @@ public void testLongValueInComponentConfigExtLatin() throws Exception {
381403 assertEquals (value , returned .getConfig ().getFirst ("val1" ));
382404 }
383405
384- private java . lang . String createComponent (ComponentRepresentation rep ) {
385- return createComponent (realm , rep );
406+ private String createComponent (ComponentRepresentation rep ) {
407+ return createComponent (managedRealm . admin () , rep );
386408 }
387409
388410 private String createComponent (RealmResource realm , ComponentRepresentation rep ) {
389- Response response = null ;
390- try {
391- ComponentsResource components = realm .components ();
392- response = components .add (rep );
393- String id = ApiUtil .getCreatedId (response );
394- getCleanup (realm .toRepresentation ().getRealm ()).addComponentId (id );
395- return id ;
396- } finally {
397- if (response != null ) {
398- response .bufferEntity ();
399- response .close ();
400- }
401- }
411+ ComponentsResource components = realm .components ();
412+ Response response = components .add (rep );
413+ return ApiUtil .getCreatedId (response );
402414 }
403415
404- private void assertError (Response response , String error ) {
416+ private void createComponentAndAssertError (ComponentRepresentation rep , String error ) {
417+ ComponentsResource components = managedRealm .admin ().components ();
418+ Response response = components .add (rep );
419+
405420 if (!response .hasEntity ()) {
406421 fail ("No error message set" );
407422 }
@@ -413,9 +428,9 @@ private void assertError(Response response, String error) {
413428 private ComponentRepresentation createComponentRepresentation (String name ) {
414429 ComponentRepresentation rep = new ComponentRepresentation ();
415430 rep .setName (name );
416- rep .setParentId (realmId );
417- rep .setProviderId ("test" );
418- rep .setProviderType (TestProvider .class .getName ());
431+ rep .setParentId (managedRealm . getId () );
432+ rep .setProviderId ("test-component " );
433+ rep .setProviderType (TestComponentProvider .class .getName ());
419434 rep .setSubType ("foo" );
420435
421436 MultivaluedHashMap <String , String > config = new MultivaluedHashMap <>();
@@ -436,7 +451,7 @@ public CreateComponent(ExecutorService s, int i, RealmResource realm) {
436451 }
437452
438453 public CreateComponent (ExecutorService s , int i ) {
439- this (s , i , ComponentsTest .this .realm );
454+ this (s , i , ComponentsTest .this .managedRealm . admin () );
440455 }
441456
442457 @ Override
@@ -530,12 +545,53 @@ public DeleteComponent(String id) {
530545 public void run () {
531546 log .debugf ("Started, id=%s" , id );
532547
533- ComponentResource c = realm .components ().component (id );
548+ ComponentResource c = managedRealm . admin () .components ().component (id );
534549 assertThat (c .toRepresentation (), Matchers .notNullValue ());
535550 c .remove ();
536551
537552 log .debugf ("Finished, id=%s" , id );
538553 }
539554 }
540555
556+ public static class TestComponents implements FetchOnServerWrapper <ComponentsDetails > {
557+
558+ @ Override
559+ public FetchOnServer getRunOnServer () {
560+ return session -> {
561+ RealmModel realm = session .getContext ().getRealm ();
562+ return new ComponentsDetails (
563+ realm .getComponentsStream (realm .getId (), TestComponentProvider .class .getName ())
564+ .collect (Collectors .toMap (ComponentModel ::getName ,
565+ componentModel -> {
566+ ProviderFactory <TestComponentProvider > f = session .getKeycloakSessionFactory ()
567+ .getProviderFactory (TestComponentProvider .class , componentModel .getProviderId ());
568+ TestComponentProviderFactory factory = (TestComponentProviderFactory ) f ;
569+ TestComponentProvider p = (TestComponentProvider ) factory .create (session , componentModel );
570+ return p .getDetails ();
571+ }))
572+ );
573+ };
574+ }
575+
576+ @ Override
577+ public Class <ComponentsDetails > getResultClass () {
578+ return ComponentsDetails .class ;
579+ }
580+ }
581+
582+ /**
583+ * This class serves only as a wrapper for a {@code Map<String, TestProvider.DetailsRepresentation>}
584+ * This is crucial for deserialization to correctly reconstruct {@link TestComponentProvider.DetailsRepresentation}
585+ * objects, as {@link FetchOnServerWrapper#getResultClass()} cannot retain generic map type information at runtime.
586+ */
587+ private record ComponentsDetails (Map <String , TestComponentProvider .DetailsRepresentation > componentsDetailsMap ) {
588+ }
589+
590+ public static class ComponentsTestServerConfig implements KeycloakServerConfig {
591+
592+ @ Override
593+ public KeycloakServerConfigBuilder configure (KeycloakServerConfigBuilder config ) {
594+ return config .dependency ("org.keycloak.tests" , "keycloak-tests-custom-providers" );
595+ }
596+ }
541597}
0 commit comments