Skip to content

Commit

Permalink
Add authentication only check for @secured (#164)
Browse files Browse the repository at this point in the history
* Add authentication only check for @secured

* Move @secured authenticated logic into processSecuredAnnotation()

* Create testcase for authentication only @secured annotation

* Add documentation about new Secured annotation behavior
  • Loading branch information
jvmlet authored Nov 16, 2020
2 parents 5a05830 + fe0ba38 commit 0718ff1
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 7 deletions.
1 change: 1 addition & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ Defining bean with type `GrpcSecurityConfigurerAdapter` annotated with `@EnableG
----

This default configuration secures GRPC methods/services annotated with `org.springframework.security.access.annotation.@Secured` annotation. +
The value passed to the Annotation can be left empty in which case only authentication will be performed. +
If `JwtDecoder` bean exists in your context, it will also register `JwtAuthenticationProvider` to handle the validation of authentication claim.

==== Custom
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ public void sayHello(GreeterOuterClass.HelloRequest request, StreamObserver<Gree
public void sayAuthHello(Empty request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {


final Authentication auth = GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get();
if(null!=auth) {
String user = auth.getName();
if (auth instanceof JwtAuthenticationToken) {
user = JwtAuthenticationToken.class.cast(auth).getTokenAttributes().get("preferred_username").toString();
}
responseObserver.onNext(GreeterOuterClass.HelloReply.newBuilder().setMessage(user).build());
}else{
responseObserver.onNext(GreeterOuterClass.HelloReply.newBuilder().setMessage("Hello").build());
}
responseObserver.onCompleted();
}

@Override
@Secured({})
public void sayAuthOnlyHello(Empty request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {


final Authentication auth = GrpcSecurity.AUTHENTICATION_CONTEXT_KEY.get();
if(null!=auth) {
String user = auth.getName();
Expand Down
1 change: 1 addition & 0 deletions grpc-spring-boot-starter-demo/src/main/proto/greeter.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ service Greeter {
// Sends a greeting
rpc SayHello ( HelloRequest) returns ( HelloReply) {}
rpc SayAuthHello ( google.protobuf.Empty) returns ( HelloReply) {}
rpc SayAuthOnlyHello ( google.protobuf.Empty) returns ( HelloReply) {}

}
service SecuredGreeter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package io.grpc.examples;

import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
import static io.grpc.stub.ClientCalls.futureUnaryCall;
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package io.grpc.examples;

import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
import static io.grpc.stub.ClientCalls.futureUnaryCall;
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;

/**
Expand Down Expand Up @@ -84,6 +92,37 @@ io.grpc.examples.GreeterOuterClass.HelloReply> getSayAuthHelloMethod() {
return getSayAuthHelloMethod;
}

private static volatile io.grpc.MethodDescriptor<com.google.protobuf.Empty,
io.grpc.examples.GreeterOuterClass.HelloReply> getSayAuthOnlyHelloMethod;

@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "SayAuthOnlyHello",
requestType = com.google.protobuf.Empty.class,
responseType = io.grpc.examples.GreeterOuterClass.HelloReply.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<com.google.protobuf.Empty,
io.grpc.examples.GreeterOuterClass.HelloReply> getSayAuthOnlyHelloMethod() {
io.grpc.MethodDescriptor<com.google.protobuf.Empty, io.grpc.examples.GreeterOuterClass.HelloReply> getSayAuthOnlyHelloMethod;
if ((getSayAuthOnlyHelloMethod = GreeterGrpc.getSayAuthOnlyHelloMethod) == null) {
synchronized (GreeterGrpc.class) {
if ((getSayAuthOnlyHelloMethod = GreeterGrpc.getSayAuthOnlyHelloMethod) == null) {
GreeterGrpc.getSayAuthOnlyHelloMethod = getSayAuthOnlyHelloMethod =
io.grpc.MethodDescriptor.<com.google.protobuf.Empty, io.grpc.examples.GreeterOuterClass.HelloReply>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "SayAuthOnlyHello"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.google.protobuf.Empty.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
io.grpc.examples.GreeterOuterClass.HelloReply.getDefaultInstance()))
.setSchemaDescriptor(new GreeterMethodDescriptorSupplier("SayAuthOnlyHello"))
.build();
}
}
}
return getSayAuthOnlyHelloMethod;
}

/**
* Creates a new async stub that supports all call types for the service
*/
Expand Down Expand Up @@ -152,6 +191,13 @@ public void sayAuthHello(com.google.protobuf.Empty request,
asyncUnimplementedUnaryCall(getSayAuthHelloMethod(), responseObserver);
}

/**
*/
public void sayAuthOnlyHello(com.google.protobuf.Empty request,
io.grpc.stub.StreamObserver<io.grpc.examples.GreeterOuterClass.HelloReply> responseObserver) {
asyncUnimplementedUnaryCall(getSayAuthOnlyHelloMethod(), responseObserver);
}

@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
Expand All @@ -168,6 +214,13 @@ public void sayAuthHello(com.google.protobuf.Empty request,
com.google.protobuf.Empty,
io.grpc.examples.GreeterOuterClass.HelloReply>(
this, METHODID_SAY_AUTH_HELLO)))
.addMethod(
getSayAuthOnlyHelloMethod(),
asyncUnaryCall(
new MethodHandlers<
com.google.protobuf.Empty,
io.grpc.examples.GreeterOuterClass.HelloReply>(
this, METHODID_SAY_AUTH_ONLY_HELLO)))
.build();
}
}
Expand Down Expand Up @@ -207,6 +260,14 @@ public void sayAuthHello(com.google.protobuf.Empty request,
asyncUnaryCall(
getChannel().newCall(getSayAuthHelloMethod(), getCallOptions()), request, responseObserver);
}

/**
*/
public void sayAuthOnlyHello(com.google.protobuf.Empty request,
io.grpc.stub.StreamObserver<io.grpc.examples.GreeterOuterClass.HelloReply> responseObserver) {
asyncUnaryCall(
getChannel().newCall(getSayAuthOnlyHelloMethod(), getCallOptions()), request, responseObserver);
}
}

/**
Expand Down Expand Up @@ -242,6 +303,13 @@ public io.grpc.examples.GreeterOuterClass.HelloReply sayAuthHello(com.google.pro
return blockingUnaryCall(
getChannel(), getSayAuthHelloMethod(), getCallOptions(), request);
}

/**
*/
public io.grpc.examples.GreeterOuterClass.HelloReply sayAuthOnlyHello(com.google.protobuf.Empty request) {
return blockingUnaryCall(
getChannel(), getSayAuthOnlyHelloMethod(), getCallOptions(), request);
}
}

/**
Expand Down Expand Up @@ -279,10 +347,19 @@ public com.google.common.util.concurrent.ListenableFuture<io.grpc.examples.Greet
return futureUnaryCall(
getChannel().newCall(getSayAuthHelloMethod(), getCallOptions()), request);
}

/**
*/
public com.google.common.util.concurrent.ListenableFuture<io.grpc.examples.GreeterOuterClass.HelloReply> sayAuthOnlyHello(
com.google.protobuf.Empty request) {
return futureUnaryCall(
getChannel().newCall(getSayAuthOnlyHelloMethod(), getCallOptions()), request);
}
}

private static final int METHODID_SAY_HELLO = 0;
private static final int METHODID_SAY_AUTH_HELLO = 1;
private static final int METHODID_SAY_AUTH_ONLY_HELLO = 2;

private static final class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
Expand All @@ -309,6 +386,10 @@ public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserv
serviceImpl.sayAuthHello((com.google.protobuf.Empty) request,
(io.grpc.stub.StreamObserver<io.grpc.examples.GreeterOuterClass.HelloReply>) responseObserver);
break;
case METHODID_SAY_AUTH_ONLY_HELLO:
serviceImpl.sayAuthOnlyHello((com.google.protobuf.Empty) request,
(io.grpc.stub.StreamObserver<io.grpc.examples.GreeterOuterClass.HelloReply>) responseObserver);
break;
default:
throw new AssertionError();
}
Expand Down Expand Up @@ -372,6 +453,7 @@ public static io.grpc.ServiceDescriptor getServiceDescriptor() {
.setSchemaDescriptor(new GreeterFileDescriptorSupplier())
.addMethod(getSayHelloMethod())
.addMethod(getSayAuthHelloMethod())
.addMethod(getSayAuthOnlyHelloMethod())
.build();
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package io.grpc.examples;

import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ClientCalls.asyncClientStreamingCall;
import static io.grpc.stub.ClientCalls.asyncServerStreamingCall;
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
import static io.grpc.stub.ClientCalls.blockingServerStreamingCall;
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
import static io.grpc.stub.ClientCalls.futureUnaryCall;
import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall;
import static io.grpc.stub.ServerCalls.asyncClientStreamingCall;
import static io.grpc.stub.ServerCalls.asyncServerStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,14 @@ public void securedServiceMethodTest() {
assertTrue(String.format("Reply should contain name '%s'",USER_NAME),reply.contains(USER_NAME));

}
@Test
public void securedAuthOnlyServiceMethodTest() {

final GreeterGrpc.GreeterBlockingStub securedFutureStub = GreeterGrpc.newBlockingStub(getChannel(true));

final String reply = securedFutureStub.sayAuthOnlyHello(Empty.getDefaultInstance()).getMessage();
assertNotNull("Reply should not be null",reply);
assertTrue(String.format("Reply should contain name '%s'",USER_NAME),reply.contains(USER_NAME));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,13 @@ private void processSecuredAnnotation() {
.filter(m -> m.getName().equalsIgnoreCase(methodDefinition.getMethodDescriptor().getBareMethodName()))
.findFirst()
.flatMap(m -> Optional.ofNullable(AnnotationUtils.findAnnotation(m, Secured.class)))
.ifPresent(secured -> new AuthorizedMethod(methodDefinition.getMethodDescriptor()).hasAnyAuthority(secured.value()));
.ifPresent(secured -> {
if (secured.value().length == 0) {
new AuthorizedMethod(methodDefinition.getMethodDescriptor()).authenticated();
} else {
new AuthorizedMethod(methodDefinition.getMethodDescriptor()).hasAnyAuthority(secured.value());
}
});

}
}
Expand Down

0 comments on commit 0718ff1

Please sign in to comment.