Skip to content

Commit

Permalink
closes #234, closes #233
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Furer committed Aug 25, 2021
1 parent e5ae67f commit 1c17622
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 3 deletions.
Expand Up @@ -7,9 +7,13 @@
import lombok.extern.slf4j.Slf4j;
import org.lognet.springboot.grpc.GRpcService;
import org.lognet.springboot.grpc.security.GrpcSecurity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.util.Assert;

@Slf4j
@GRpcService(interceptors = { LogInterceptor.class })
Expand All @@ -29,7 +33,9 @@ public void sayAuthHello(Empty request, StreamObserver<GreeterOuterClass.HelloRe


final Authentication auth = GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get();
Assert.isTrue(SecurityContextHolder.getContext().getAuthentication() == auth,()->"Authentication object should be the same as in GRPC context");
if(null!=auth) {

String user = auth.getName();
if (auth instanceof JwtAuthenticationToken) {
user = JwtAuthenticationToken.class.cast(auth).getTokenAttributes().get("preferred_username").toString();
Expand All @@ -41,6 +47,17 @@ public void sayAuthHello(Empty request, StreamObserver<GreeterOuterClass.HelloRe
responseObserver.onCompleted();
}

@Override
@PreAuthorize("#person.getAge()<12")
public void sayPreAuthHello(GreeterOuterClass.Person person, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
responseObserver.onNext(GreeterOuterClass.HelloReply
.newBuilder()
.setMessage("Only kids are welcome!")
.build());
responseObserver.onCompleted();

}

@Override
@Secured({})
public void sayAuthOnlyHello(Empty request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
Expand Down
Expand Up @@ -7,8 +7,11 @@
import org.lognet.springboot.grpc.GRpcService;
import org.lognet.springboot.grpc.security.GrpcSecurity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.util.Assert;

import java.util.Optional;

Expand All @@ -20,6 +23,7 @@ public class SecuredGreeterService extends SecuredGreeterGrpc.SecuredGreeterImpl
@Override
public void sayAuthHello(Empty request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
final Authentication auth = GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get();
Assert.isTrue(SecurityContextHolder.getContext().getAuthentication() == auth,()->"Authentication object should be the same as in GRPC context");
String user = auth.getName();
if(auth instanceof JwtAuthenticationToken){
user = JwtAuthenticationToken.class.cast(auth).getTokenAttributes().get("preferred_username").toString();
Expand All @@ -28,9 +32,16 @@ public void sayAuthHello(Empty request, StreamObserver<GreeterOuterClass.HelloRe
reply(user,responseObserver);
}



@Override
public void sayAuthHello2(Empty request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
String userName = Optional.ofNullable(GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get())
final Optional<Authentication> authentication = Optional.ofNullable(GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get());


Assert.isTrue(SecurityContextHolder.getContext().getAuthentication() == authentication.orElse(null),()->"Authentication object should be the same as in GRPC context");

String userName = authentication
.map(Authentication::getName)
.orElse("anonymous");
reply(userName,responseObserver);
Expand Down
2 changes: 2 additions & 0 deletions grpc-spring-boot-starter-demo/src/main/proto/greeter.proto
Expand Up @@ -10,6 +10,7 @@ service Greeter {
rpc SayHello ( HelloRequest) returns ( HelloReply) {}
rpc SayAuthHello ( google.protobuf.Empty) returns ( HelloReply) {}
rpc SayAuthOnlyHello ( google.protobuf.Empty) returns ( HelloReply) {}
rpc SayPreAuthHello ( Person) returns ( HelloReply) {}
rpc HelloPersonValidResponse ( Person) returns ( Person) {}
rpc HelloPersonInvalidResponse ( Person) returns ( Person) {}

Expand All @@ -18,6 +19,7 @@ service SecuredGreeter {
rpc SayAuthHello ( google.protobuf.Empty) returns ( HelloReply) {}
rpc SayAuthHello2 ( google.protobuf.Empty) returns ( HelloReply) {}


}

message Person {
Expand Down
Expand Up @@ -69,7 +69,8 @@
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DemoApp.class, TestConfig.class}, webEnvironment = RANDOM_PORT
, properties = {"grpc.enableReflection=true",
"grpc.shutdownGrace=-1"
"grpc.shutdownGrace=-1",
"spring.main.web-application-type=servlet"
})
@ActiveProfiles({"disable-security", "measure"})

Expand Down
Expand Up @@ -9,9 +9,13 @@
import io.grpc.examples.CalculatorGrpc;
import io.grpc.examples.CalculatorOuterClass;
import io.grpc.examples.GreeterGrpc;
import io.grpc.examples.GreeterOuterClass;
import io.grpc.examples.SecuredCalculatorGrpc;
import io.grpc.examples.SecuredGreeterGrpc;
import org.hamcrest.Matchers;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.jupiter.api.Disabled;
import org.junit.runner.RunWith;
import org.lognet.springboot.grpc.GrpcServerTestBase;
import org.lognet.springboot.grpc.demo.DemoApp;
Expand All @@ -25,6 +29,7 @@
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
Expand All @@ -35,6 +40,8 @@
import java.util.concurrent.ExecutionException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
Expand Down Expand Up @@ -101,6 +108,32 @@ public void simpleAuthHeaderFormat() throws ExecutionException, InterruptedExcep

}

@Test
@Ignore("@PreAuthorize is not supported yet")
public void preAuthorizeTest() {
final GreeterGrpc.GreeterBlockingStub greeterBlockingStub = GreeterGrpc.newBlockingStub(getChannel(false));


final GreeterOuterClass.HelloReply helloReply = greeterBlockingStub
.sayPreAuthHello(GreeterOuterClass.Person.newBuilder()
.setName("Frodo")
.setAddress(GreeterOuterClass.Address.newBuilder().setCity("Shire"))
.setAge(11)
.build());
assertThat(helloReply.getMessage(),not(emptyOrNullString()));

final StatusRuntimeException statusRuntimeException = assertThrows(StatusRuntimeException.class, () -> {
greeterBlockingStub
.sayPreAuthHello(GreeterOuterClass.Person.newBuilder()
.setName("Aragorn")
.setAddress(GreeterOuterClass.Address.newBuilder().setCity("Isildur"))
.setAge(45)
.build());
});
assertThat(statusRuntimeException.getStatus().getCode(), Matchers.is(Status.Code.PERMISSION_DENIED));

}

@Test
public void shouldFailWithPermissionDenied() {

Expand Down
Expand Up @@ -12,6 +12,9 @@ management:
logging:
level:
root: INFO
spring:
main:
web-application-type: none

---
spring:
Expand Down
Expand Up @@ -87,12 +87,53 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
} catch (Exception e) {
return fail(next, call, headers, Status.UNAUTHENTICATED, e);
}
return Contexts.interceptCall(grpcSecurityContext, call, headers, next);
return Contexts.interceptCall(grpcSecurityContext, call, headers, authenticationPropagatingHandler(next));
} finally {
SecurityContextHolder.getContext().setAuthentication(null);
}


}
private <ReqT, RespT> ServerCallHandler<ReqT, RespT> authenticationPropagatingHandler(ServerCallHandler<ReqT, RespT> next) {

return (call, headers) -> new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) {

@Override
public void onMessage(ReqT message) {
propagateAuthentication(() -> super.onMessage(message));
}

@Override
public void onHalfClose() {
propagateAuthentication(super::onHalfClose);
}

@Override
public void onCancel() {
propagateAuthentication(super::onCancel);
}

@Override
public void onComplete() {
propagateAuthentication(super::onComplete);
}

@Override
public void onReady() {
propagateAuthentication(super::onReady);
}

private void propagateAuthentication(Runnable runnable) {
try {
SecurityContextHolder.getContext().setAuthentication(GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get());
runnable.run();
} finally {
SecurityContextHolder.clearContext();
}
}

};

}

private Context setupGRpcSecurityContext(ServerCall<?, ?> call, CharSequence authorization) {
Expand Down

0 comments on commit 1c17622

Please sign in to comment.