Skip to content

Commit 71c1468

Browse files
author
Alexander Furer
committed
When overriding default GRPC security configuration, @Secured annotation is enabled by default.
1 parent ac62697 commit 71c1468

File tree

4 files changed

+105
-30
lines changed

4 files changed

+105
-30
lines changed

ReleaseNotes.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
== Version 4.2.1-SNAPSHOT
2+
* When overriding default GRPC security configuration, `@Secured` annotation is enabled by default.
3+
14
== Version 4.2.0
25
* gRPC version upgraded to 1.33.0
36
* Fixed the issue with default method-level `@Secured` annotation (see #159)

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ public void sayAuthHello(Empty request, StreamObserver<GreeterOuterClass.HelloRe
2929

3030

3131
final Authentication auth = GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get();
32-
String user = auth.getName();
33-
if(auth instanceof JwtAuthenticationToken){
34-
user = JwtAuthenticationToken.class.cast(auth).getTokenAttributes().get("preferred_username").toString();
32+
if(null!=auth) {
33+
String user = auth.getName();
34+
if (auth instanceof JwtAuthenticationToken) {
35+
user = JwtAuthenticationToken.class.cast(auth).getTokenAttributes().get("preferred_username").toString();
36+
}
37+
responseObserver.onNext(GreeterOuterClass.HelloReply.newBuilder().setMessage(user).build());
38+
}else{
39+
responseObserver.onNext(GreeterOuterClass.HelloReply.newBuilder().setMessage("Hello").build());
3540
}
36-
37-
responseObserver.onNext(GreeterOuterClass.HelloReply.newBuilder().setMessage(user).build());
3841
responseObserver.onCompleted();
3942
}
4043
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.lognet.springboot.grpc.auth;
2+
3+
4+
import com.google.protobuf.Empty;
5+
import io.grpc.examples.GreeterGrpc;
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import org.lognet.springboot.grpc.demo.DemoApp;
9+
import org.lognet.springboot.grpc.security.EnableGrpcSecurity;
10+
import org.lognet.springboot.grpc.security.GrpcSecurity;
11+
import org.lognet.springboot.grpc.security.GrpcSecurityConfigurerAdapter;
12+
import org.springframework.boot.test.context.SpringBootTest;
13+
import org.springframework.boot.test.context.TestConfiguration;
14+
import org.springframework.context.annotation.Import;
15+
import org.springframework.test.context.junit4.SpringRunner;
16+
17+
import static org.junit.Assert.assertNotNull;
18+
19+
20+
@SpringBootTest(classes = DemoApp.class)
21+
@RunWith(SpringRunner.class)
22+
@Import({DisabledSecuredAnnTest.TestCfg.class})
23+
public class DisabledSecuredAnnTest extends JwtAuthBaseTest {
24+
25+
26+
@TestConfiguration
27+
@EnableGrpcSecurity
28+
static class TestCfg extends GrpcSecurityConfigurerAdapter {
29+
@Override
30+
public void configure(GrpcSecurity builder) throws Exception {
31+
builder.authorizeRequests().withoutSecuredAnnotation();
32+
}
33+
}
34+
35+
public DisabledSecuredAnnTest() {
36+
super(false);
37+
}
38+
39+
@Test
40+
public void nonSecuredServiceTest() {
41+
42+
final GreeterGrpc.GreeterBlockingStub greeter = GreeterGrpc.newBlockingStub(getChannel(true));
43+
44+
final String reply = greeter.sayAuthHello(Empty.getDefaultInstance()).getMessage();
45+
assertNotNull("Reply should not be null",reply);
46+
47+
48+
}
49+
}

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

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,20 @@
2828
public class GrpcServiceAuthorizationConfigurer
2929
extends SecurityConfigurerAdapter<ServerInterceptor, GrpcSecurity> {
3030

31-
private final GrpcServiceAuthorizationConfigurer.Registry REGISTRY;
31+
private final GrpcServiceAuthorizationConfigurer.Registry registry;
3232

3333
public GrpcServiceAuthorizationConfigurer(ApplicationContext context) {
34-
this.REGISTRY = new GrpcServiceAuthorizationConfigurer.Registry(context);
34+
this.registry = new GrpcServiceAuthorizationConfigurer.Registry(context);
3535
}
3636

3737
public Registry getRegistry() {
38-
return REGISTRY;
38+
return registry;
3939
}
4040

4141
@Override
4242
public void configure(GrpcSecurity builder) throws Exception {
43-
builder.setSharedObject(GrpcSecurityMetadataSource.class, new GrpcSecurityMetadataSource(REGISTRY.securedMethods));
43+
registry.processSecuredAnnotation();
44+
builder.setSharedObject(GrpcSecurityMetadataSource.class, new GrpcSecurityMetadataSource(registry.securedMethods));
4445
}
4546

4647

@@ -58,8 +59,8 @@ private AuthorizedMethod(ServiceDescriptor... serviceDescriptor) {
5859
}
5960

6061
public GrpcServiceAuthorizationConfigurer.Registry authenticated() {
61-
GrpcServiceAuthorizationConfigurer.this.REGISTRY.map(methods);
62-
return GrpcServiceAuthorizationConfigurer.this.REGISTRY;
62+
GrpcServiceAuthorizationConfigurer.this.registry.map(methods);
63+
return GrpcServiceAuthorizationConfigurer.this.registry;
6364
}
6465

6566
public GrpcServiceAuthorizationConfigurer.Registry hasAnyRole(String... roles) {
@@ -76,9 +77,9 @@ public GrpcServiceAuthorizationConfigurer.Registry hasAnyRole(String... roles) {
7677

7778
public GrpcServiceAuthorizationConfigurer.Registry hasAnyAuthority(String... authorities) {
7879
for (String auth : authorities) {
79-
GrpcServiceAuthorizationConfigurer.this.REGISTRY.map(auth, methods);
80+
GrpcServiceAuthorizationConfigurer.this.registry.map(auth, methods);
8081
}
81-
return GrpcServiceAuthorizationConfigurer.this.REGISTRY;
82+
return GrpcServiceAuthorizationConfigurer.this.registry;
8283
}
8384

8485

@@ -88,6 +89,7 @@ public class Registry {
8889

8990
private MultiValueMap<MethodDescriptor<?, ?>, ConfigAttribute> securedMethods = new LinkedMultiValueMap<>();
9091
private ApplicationContext context;
92+
private boolean withSecuredAnnotation = true;
9193

9294
Registry(ApplicationContext context) {
9395
this.context = context;
@@ -104,30 +106,48 @@ public AuthorizedMethod anyMethod() {
104106

105107
}
106108

109+
public GrpcSecurity withoutSecuredAnnotation() {
110+
return withSecuredAnnotation(false);
111+
}
112+
113+
/**
114+
* Same as {@code withSecuredAnnotation(true)}
115+
* @return GrpcSecurity configuration
116+
*/
107117
public GrpcSecurity withSecuredAnnotation() {
108-
final Collection<BindableService> services = context.getBeansOfType(BindableService.class).values();
118+
return withSecuredAnnotation(true);
119+
}
120+
public GrpcSecurity withSecuredAnnotation(boolean withSecuredAnnotation) {
121+
this.withSecuredAnnotation = withSecuredAnnotation;
122+
return and();
123+
}
109124

110-
for (BindableService service : services) {
111-
final ServerServiceDefinition serverServiceDefinition = service.bindService();
112-
// service level security
113-
{
114-
final Secured securedAnn = AnnotationUtils.findAnnotation(service.getClass(), Secured.class);
125+
private void processSecuredAnnotation() {
126+
if (withSecuredAnnotation) {
127+
final Collection<BindableService> services = context.getBeansOfType(BindableService.class).values();
115128

116-
if (null != securedAnn) {
117-
new AuthorizedMethod(serverServiceDefinition.getServiceDescriptor()).hasAnyAuthority(securedAnn.value());
129+
for (BindableService service : services) {
130+
final ServerServiceDefinition serverServiceDefinition = service.bindService();
131+
// service level security
132+
{
133+
final Secured securedAnn = AnnotationUtils.findAnnotation(service.getClass(), Secured.class);
134+
135+
if (null != securedAnn) {
136+
new AuthorizedMethod(serverServiceDefinition.getServiceDescriptor()).hasAnyAuthority(securedAnn.value());
137+
}
118138
}
119-
}
120-
// method level security
121-
for(ServerMethodDefinition<?,?> methodDefinition :serverServiceDefinition.getMethods()){
122-
Stream.of(service.getClass().getMethods()) // get method from methodDefinition
123-
.filter(m -> 0==Objects.compare(methodDefinition.getMethodDescriptor().getBareMethodName(),m.getName(), Comparator.naturalOrder()))
124-
.findFirst()
125-
.flatMap(m->Optional.ofNullable(AnnotationUtils.findAnnotation(m, Secured.class)))
126-
.ifPresent(secured -> new AuthorizedMethod(methodDefinition.getMethodDescriptor()) .hasAnyAuthority(secured.value()));
139+
// method level security
140+
for (ServerMethodDefinition<?, ?> methodDefinition : serverServiceDefinition.getMethods()) {
141+
Stream.of(service.getClass().getMethods()) // get method from methodDefinition
142+
.filter(m -> 0 == Objects.compare(methodDefinition.getMethodDescriptor().getBareMethodName(), m.getName(), Comparator.naturalOrder()))
143+
.findFirst()
144+
.flatMap(m -> Optional.ofNullable(AnnotationUtils.findAnnotation(m, Secured.class)))
145+
.ifPresent(secured -> new AuthorizedMethod(methodDefinition.getMethodDescriptor()).hasAnyAuthority(secured.value()));
127146

147+
}
128148
}
129149
}
130-
return and();
150+
131151
}
132152

133153
public AuthorizedMethod methods(MethodDescriptor<?, ?>... methodDescriptor) {

0 commit comments

Comments
 (0)