Skip to content

Commit 0116f2c

Browse files
author
Alexander Furer
committed
closes #194, progress with #193
1 parent 28c4550 commit 0116f2c

File tree

15 files changed

+269
-15
lines changed

15 files changed

+269
-15
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
grpcVersion=1.35.0
1+
grpcVersion=1.36.0
22
springBootVersion=2.4.1
33
springCloudVersion=2020.0.0
44

grpc-spring-boot-starter-demo/src/main/protoGen/io/grpc/examples/CalculatorGrpc.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/**
66
*/
77
@javax.annotation.Generated(
8-
value = "by gRPC proto compiler (version 1.35.0)",
8+
value = "by gRPC proto compiler (version 1.36.0)",
99
comments = "Source: calculator.proto")
1010
public final class CalculatorGrpc {
1111

grpc-spring-boot-starter-demo/src/main/protoGen/io/grpc/examples/GreeterGrpc.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* </pre>
99
*/
1010
@javax.annotation.Generated(
11-
value = "by gRPC proto compiler (version 1.35.0)",
11+
value = "by gRPC proto compiler (version 1.36.0)",
1212
comments = "Source: greeter.proto")
1313
public final class GreeterGrpc {
1414

grpc-spring-boot-starter-demo/src/main/protoGen/io/grpc/examples/SecuredCalculatorGrpc.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/**
66
*/
77
@javax.annotation.Generated(
8-
value = "by gRPC proto compiler (version 1.35.0)",
8+
value = "by gRPC proto compiler (version 1.36.0)",
99
comments = "Source: calculator.proto")
1010
public final class SecuredCalculatorGrpc {
1111

grpc-spring-boot-starter-demo/src/main/protoGen/io/grpc/examples/SecuredGreeterGrpc.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/**
66
*/
77
@javax.annotation.Generated(
8-
value = "by gRPC proto compiler (version 1.35.0)",
8+
value = "by gRPC proto compiler (version 1.36.0)",
99
comments = "Source: greeter.proto")
1010
public final class SecuredGreeterGrpc {
1111

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package org.lognet.springboot.grpc;
2+
3+
4+
import io.grpc.Channel;
5+
import io.grpc.ClientInterceptors;
6+
import io.grpc.Metadata;
7+
import io.grpc.ServerCall;
8+
import io.grpc.ServerCallHandler;
9+
import io.grpc.ServerInterceptor;
10+
import io.grpc.Status;
11+
import io.grpc.StatusRuntimeException;
12+
import io.grpc.examples.GreeterGrpc;
13+
import io.grpc.examples.GreeterOuterClass;
14+
import org.hamcrest.Matchers;
15+
import org.junit.Before;
16+
import org.junit.runner.RunWith;
17+
import org.lognet.springboot.grpc.demo.DemoApp;
18+
import org.lognet.springboot.grpc.security.AuthClientInterceptor;
19+
import org.lognet.springboot.grpc.security.AuthHeader;
20+
import org.lognet.springboot.grpc.security.EnableGrpcSecurity;
21+
import org.lognet.springboot.grpc.security.GrpcSecurity;
22+
import org.lognet.springboot.grpc.security.GrpcSecurityConfigurerAdapter;
23+
import org.lognet.springboot.grpc.security.SecurityInterceptor;
24+
import org.lognet.springboot.grpc.validation.ValidatingInterceptor;
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.boot.test.context.SpringBootTest;
27+
import org.springframework.boot.test.context.TestConfiguration;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Import;
30+
import org.springframework.context.annotation.Lazy;
31+
import org.springframework.core.annotation.Order;
32+
import org.springframework.security.authentication.AuthenticationProvider;
33+
import org.springframework.security.core.Authentication;
34+
import org.springframework.security.core.AuthenticationException;
35+
import org.springframework.security.core.userdetails.User;
36+
import org.springframework.security.core.userdetails.UserDetails;
37+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
38+
import org.springframework.security.crypto.password.PasswordEncoder;
39+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
40+
import org.springframework.test.context.junit4.SpringRunner;
41+
42+
import java.util.ArrayList;
43+
import java.util.Arrays;
44+
import java.util.List;
45+
import java.util.stream.Collectors;
46+
47+
import static org.hamcrest.MatcherAssert.assertThat;
48+
import static org.junit.Assert.assertThrows;
49+
import static org.mockito.Mockito.mock;
50+
import static org.mockito.Mockito.when;
51+
52+
53+
@SpringBootTest(classes = DemoApp.class,
54+
properties = {
55+
"grpc.security.auth.interceptor-order=3", //third
56+
"grpc.metrics.interceptor-order=2", //second
57+
"grpc.validation.interceptor-order=1" //first
58+
})
59+
@RunWith(SpringRunner.class)
60+
@Import({CustomInterceptorsOrderTest.TestCfg.class})
61+
public class CustomInterceptorsOrderTest extends GrpcServerTestBase {
62+
63+
private final static List<CustomInterceptorsOrderTest.Interceptor> calledInterceptors = new ArrayList<>();
64+
65+
enum Interceptor {
66+
BUILTIN_SECURITY, USER_DEFINED
67+
}
68+
69+
70+
@Autowired
71+
@Lazy
72+
private List<ServerInterceptor> interceptors;
73+
74+
75+
@Before
76+
public void setUp() throws Exception {
77+
calledInterceptors.clear();
78+
final List<Class<? extends ServerInterceptor>> orderedInterceptorsClasses = Arrays.asList( //according the order define by properties
79+
TestCfg.UserDefinedInterceptor.class,
80+
ValidatingInterceptor.class,
81+
SecurityInterceptor.class
82+
);
83+
final List<Class<?>> orderedInterceptors = interceptors
84+
.stream()
85+
.map(Object::getClass)
86+
.filter(orderedInterceptorsClasses::contains)
87+
.collect(Collectors.toList());
88+
89+
assertThat(orderedInterceptors, Matchers.is(orderedInterceptorsClasses));
90+
91+
}
92+
93+
// @Test
94+
public void validationShouldInvokedBeforeAuthTest() {
95+
final GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(super.getChannel());
96+
StatusRuntimeException e = assertThrows(StatusRuntimeException.class, () -> {
97+
stub.helloPersonValidResponse(GreeterOuterClass.Person.newBuilder()
98+
.setAge(49)// valid
99+
.clearName()//invalid
100+
.build());
101+
});
102+
assertThat(e.getStatus().getCode(), Matchers.is(Status.Code.INVALID_ARGUMENT));
103+
104+
}
105+
106+
@Override
107+
protected void afterGreeting() {
108+
assertThat(calledInterceptors, Matchers.contains(Interceptor.USER_DEFINED, Interceptor.BUILTIN_SECURITY));
109+
}
110+
111+
@TestConfiguration
112+
static class TestCfg {
113+
114+
@EnableGrpcSecurity
115+
public class DemoGrpcSecurityConfig extends GrpcSecurityConfigurerAdapter {
116+
117+
118+
static final String pwd = "strongPassword1";
119+
120+
@Bean
121+
public PasswordEncoder passwordEncoder() {
122+
return new BCryptPasswordEncoder();
123+
}
124+
125+
@Bean
126+
public UserDetails user() {
127+
return User.
128+
withUsername("user1")
129+
.password(passwordEncoder().encode(pwd))
130+
.roles("reader")
131+
.build();
132+
}
133+
134+
135+
@Override
136+
public void configure(GrpcSecurity builder) throws Exception {
137+
138+
139+
builder.authorizeRequests()
140+
.anyMethod().authenticated()
141+
.and()
142+
.authenticationProvider(new AuthenticationProvider() {
143+
@Override
144+
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
145+
calledInterceptors.add(Interceptor.BUILTIN_SECURITY);
146+
Authentication fakeAuthentication = mock(Authentication.class);
147+
when(fakeAuthentication.isAuthenticated()).thenReturn(true);
148+
return fakeAuthentication;
149+
}
150+
151+
@Override
152+
public boolean supports(Class<?> authentication) {
153+
return true;
154+
}
155+
})
156+
.userDetailsService(new InMemoryUserDetailsManager(user()));
157+
}
158+
}
159+
160+
@Bean
161+
@GRpcGlobalInterceptor
162+
public ServerInterceptor userDefinedInterceptor() {
163+
return new UserDefinedInterceptor();
164+
}
165+
166+
@Order(0)
167+
static class UserDefinedInterceptor implements ServerInterceptor {
168+
169+
@Override
170+
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
171+
calledInterceptors.add(CustomInterceptorsOrderTest.Interceptor.USER_DEFINED);
172+
return next.startCall(call, headers);
173+
}
174+
}
175+
176+
}
177+
178+
@Autowired
179+
private UserDetails user;
180+
181+
182+
@Override
183+
protected Channel getChannel() {
184+
185+
186+
final AuthClientInterceptor interceptor = new AuthClientInterceptor(AuthHeader.builder()
187+
.basic(user.getUsername(), TestCfg.DemoGrpcSecurityConfig.pwd.getBytes())
188+
);
189+
return ClientInterceptors.intercept(super.getChannel(), interceptor);
190+
}
191+
192+
193+
}

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/GRpcServerRunner.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ private Comparator<Object> serverInterceptorOrderComparator() {
151151
.ifPresent(sources::add);
152152

153153
return sources.toArray();
154-
}).reversed();
154+
})
155+
.reversed();
155156
}
156157

157158
private void startDaemonAwaitThread() {

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/autoconfigure/GRpcServerProperties.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ public Integer getRunningPort() {
8181
public static class SecurityProperties {
8282
private Resource certChain;
8383
private Resource privateKey;
84+
private Auth auth;
85+
@Getter
86+
@Setter
87+
public static class Auth {
88+
private Integer interceptorOrder;
89+
}
8490
}
8591

8692
@Getter

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/autoconfigure/GRpcValidationConfiguration.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.lognet.springboot.grpc.validation.ValidatingInterceptor;
55
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
66
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
7+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
78
import org.springframework.context.annotation.Bean;
89
import org.springframework.context.annotation.Configuration;
910
import org.springframework.context.annotation.Lazy;
@@ -12,11 +13,13 @@
1213

1314
@Configuration
1415
@ConditionalOnClass(Validator.class)
16+
@EnableConfigurationProperties(GRpcValidationProperties.class)
1517
public class GRpcValidationConfiguration {
1618
@Bean
1719
@ConditionalOnBean(Validator.class)
1820
@GRpcGlobalInterceptor
19-
public ValidatingInterceptor validatingInterceptor(@Lazy Validator validator){
20-
return new ValidatingInterceptor(validator);
21+
public ValidatingInterceptor validatingInterceptor(@Lazy Validator validator,GRpcValidationProperties validationProperties){
22+
return new ValidatingInterceptor(validator)
23+
.order(validationProperties.getInterceptorOrder());
2124
}
2225
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.lognet.springboot.grpc.autoconfigure;
2+
3+
import lombok.Getter;
4+
import lombok.Setter;
5+
import org.springframework.boot.context.properties.ConfigurationProperties;
6+
7+
@ConfigurationProperties("grpc.validation")
8+
@Getter
9+
@Setter
10+
public class GRpcValidationProperties {
11+
private Integer interceptorOrder ;
12+
}

0 commit comments

Comments
 (0)