1+ package org .lognet .springboot .grpc .auth ;
2+
3+ import com .google .protobuf .Empty ;
4+ import io .grpc .Status ;
5+ import io .grpc .StatusRuntimeException ;
6+ import io .grpc .examples .GreeterGrpc .GreeterFutureStub ;
7+ import io .grpc .examples .SecuredGreeterGrpc ;
8+ import lombok .extern .slf4j .Slf4j ;
9+ import org .hamcrest .Matchers ;
10+ import org .junit .Test ;
11+ import org .junit .runner .RunWith ;
12+ import org .lognet .springboot .grpc .GrpcServerTestBase ;
13+ import org .lognet .springboot .grpc .demo .DemoApp ;
14+ import org .lognet .springboot .grpc .security .AuthCallCredentials ;
15+ import org .lognet .springboot .grpc .security .AuthHeader ;
16+ import org .lognet .springboot .grpc .security .EnableGrpcSecurity ;
17+ import org .lognet .springboot .grpc .security .GrpcSecurity ;
18+ import org .lognet .springboot .grpc .security .GrpcSecurityConfigurerAdapter ;
19+ import org .springframework .boot .test .context .SpringBootTest ;
20+ import org .springframework .boot .test .context .TestConfiguration ;
21+ import org .springframework .context .annotation .Import ;
22+ import org .springframework .security .authentication .dao .DaoAuthenticationProvider ;
23+ import org .springframework .security .core .userdetails .User ;
24+ import org .springframework .security .core .userdetails .UserDetailsService ;
25+ import org .springframework .security .crypto .password .NoOpPasswordEncoder ;
26+ import org .springframework .security .provisioning .InMemoryUserDetailsManager ;
27+ import org .springframework .test .context .junit4 .SpringRunner ;
28+
29+ import java .util .ArrayList ;
30+ import java .util .Collections ;
31+ import java .util .List ;
32+ import java .util .concurrent .atomic .AtomicInteger ;
33+ import java .util .function .Function ;
34+
35+ import static org .hamcrest .MatcherAssert .assertThat ;
36+ import static org .junit .Assert .assertThrows ;
37+ import static org .junit .jupiter .api .Assertions .assertAll ;
38+ import static org .junit .jupiter .api .Assertions .assertEquals ;
39+
40+ @ SpringBootTest (classes = DemoApp .class , properties = "spring.cloud.service-registry.auto-registration.enabled=false" )
41+ @ RunWith (SpringRunner .class )
42+ @ Import ({ConcurrentAuthConfigTest .TestCfg .class })
43+ @ Slf4j
44+ public class ConcurrentAuthConfigTest extends GrpcServerTestBase {
45+
46+ private static User user1 = new User ("test1" , "test1" , Collections .EMPTY_LIST );
47+ private static User user2 = new User ("test2" , "test2" , Collections .EMPTY_LIST );
48+
49+ private AuthCallCredentials user1CallCredentials = new AuthCallCredentials (
50+ AuthHeader .builder ().basic (user1 .getUsername (), user1 .getPassword ().getBytes ()));
51+
52+ private AuthCallCredentials user2CallCredentials = new AuthCallCredentials (
53+ AuthHeader .builder ().basic (user2 .getUsername (), user2 .getPassword ().getBytes ()));
54+
55+ @ TestConfiguration
56+ static class TestCfg {
57+
58+ @ EnableGrpcSecurity
59+ public class DemoGrpcSecurityConfig extends GrpcSecurityConfigurerAdapter {
60+
61+ @ Override
62+ public void configure (GrpcSecurity builder ) throws Exception {
63+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider ();
64+ UserDetailsService users = new InMemoryUserDetailsManager (user1 , user2 );
65+ provider .setUserDetailsService (users );
66+ provider .setPasswordEncoder (NoOpPasswordEncoder .getInstance ());
67+
68+ builder
69+ .authenticationProvider (provider )
70+ .authorizeRequests ()
71+ .anyMethod ().authenticated ();
72+ }
73+
74+ }
75+ }
76+
77+ @ Test
78+ public void concurrentTest () throws InterruptedException {
79+ System .out .println ();
80+
81+ final SecuredGreeterGrpc .SecuredGreeterBlockingStub unsecuredFutureStub = SecuredGreeterGrpc
82+ .newBlockingStub (selectedChanel );
83+
84+ final SecuredGreeterGrpc .SecuredGreeterBlockingStub securedFutureStub1 = unsecuredFutureStub
85+ .withCallCredentials (user1CallCredentials );
86+
87+ final SecuredGreeterGrpc .SecuredGreeterBlockingStub securedFutureStub2 = unsecuredFutureStub
88+ .withCallCredentials (user2CallCredentials );
89+
90+
91+ int parallelTests = 10 ;
92+
93+ List <Thread > threads = new ArrayList <>();
94+ // Number of threads that passed the test
95+ AtomicInteger successCounter = new AtomicInteger (0 );
96+ AtomicInteger failureCounter = new AtomicInteger (0 );
97+
98+ Function <Integer , Void > authenticated = i -> {
99+ SecuredGreeterGrpc .SecuredGreeterBlockingStub stub = null ;
100+ User user = null ;
101+ if (0 == i % 2 ) {
102+ stub = securedFutureStub1 ;
103+ user = user1 ;
104+ }else {
105+ stub = securedFutureStub2 ;
106+ user = user2 ;
107+ }
108+ final String reply = stub .sayAuthHello (Empty .getDefaultInstance ()).getMessage ();
109+ assertThat (reply , Matchers .containsString (user .getUsername ()));
110+ return null ;
111+ };
112+ Runnable unauthenticated = () -> {
113+ StatusRuntimeException err = assertThrows (StatusRuntimeException .class ,
114+ () -> unsecuredFutureStub .sayAuthHello (Empty .getDefaultInstance ()).getMessage ());
115+ assertEquals (Status .Code .UNAUTHENTICATED , err .getStatus ().getCode ());
116+ };
117+
118+ // Check that the assertions work as is (single threaded)
119+ authenticated .apply (0 );
120+ unauthenticated .run ();
121+
122+ for (int i = 0 ; i < parallelTests ; i ++) {
123+ Thread success = new Thread (() -> {
124+
125+ for (int j = 0 ; j < 1000 ; j ++) {
126+ authenticated .apply (j );
127+ }
128+ successCounter .incrementAndGet ();
129+ log .info ("All passed" );
130+ });
131+ success .setUncaughtExceptionHandler ((thread , ex ) -> {
132+ log .error ("SECURITY ???" , ex );
133+ });
134+ threads .add (success );
135+
136+ Thread failure = new Thread (() -> {
137+
138+ for (int j = 0 ; j < 1000 ; j ++) {
139+ unauthenticated .run ();
140+ }
141+ failureCounter .incrementAndGet ();
142+ log .info ("All passed" );
143+ });
144+ failure .setUncaughtExceptionHandler ((thread , ex ) -> {
145+ log .error ("SECURITY BYPASSED" , ex );
146+ });
147+
148+ threads .add (failure );
149+ }
150+
151+ Collections .shuffle (threads );
152+ for (Thread thread : threads ) {
153+ thread .start ();
154+ }
155+ for (Thread thread : threads ) {
156+ thread .join ();
157+ }
158+
159+ assertAll (() -> assertEquals (parallelTests , successCounter .get ()),
160+ () -> assertEquals (parallelTests , failureCounter .get ()));
161+ }
162+
163+ @ Override
164+ protected GreeterFutureStub beforeGreeting (GreeterFutureStub stub ) {
165+ return stub .withCallCredentials (user1CallCredentials );
166+ }
167+
168+ }
0 commit comments