Uploaded image for project: 'Spring Integration'
  1. Spring Integration
  2. INT-2166

Adding support for SecurityContext Propagation

    Details

    • Type: New Feature
    • Status: Open
    • Priority: Minor
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: 4.2 Backlog
    • Component/s: Security
    • Labels:
      None

      Description

      There are a lot of cases when it is necessary to use the SecurityContext in async mesage flow inside Web application.
      The principal is appeared in HttpRequest from springSecurityFilterChain and stored in the ThreadLocal holder.
      So, at first, I propose to add supoprt of some global ChannelInterceptor which can propagetes that securityContext to async mesasge flow.
      This is my solution. Sorry for Groovy code.

       
      class SecurityContextPropagationChannelInterceptor extends ChannelInterceptorAdapter {
          @Override
          Message<?> preSend(Message<?> message, MessageChannel channel) {
              if (!(channel in AbstractPollableChannel) || message.headers.securityContext || !SecurityContextHolder.context.authentication) return message
              return MessageBuilder.fromMessage(message).with {
                  setHeader 'securityContext', SecurityContextHolder.context
                  build()
              }
          }
       
          @Override
          Message<?> postReceive(Message<?> message, MessageChannel channel) {
              if (channel in AbstractPollableChannel && message.headers.securityContext) {
                  SecurityContextHolder.context = message.headers.securityContext
              }
              return message
          }
       
       
      }

      In this case the end-programmer can use

      SecurityContextHolder.getContext()

      As well as always in any place of his application.

        Issue Links

          Activity

          Hide
          dphollis Damien Hollis added a comment -

          Hi Artem,

          I think your suggests would work for Executor and Queue Channels. However, I couldn't see how to make either work for a Jms Channel. So instead I implemented a MessageHandler and configured it at the beginning of a chain of handlers:

          public class ContextPropagatingMessageHandler extends AbstractMessageHandler implements MessageProducer {
           
              private final MessagingTemplate messagingTemplate = new MessagingTemplate();
           
              private MessageChannel outputChannel;
           
              @Override
              public void setOutputChannel(MessageChannel outputChannel) {
                  this.outputChannel = outputChannel;
              }
           
              @Override
              protected void handleMessageInternal(Message<?> message) throws Exception {
                  SecurityContext securityContext = (SecurityContext) message.getHeaders().get(ContextPropagatingChannelInterceptor.SECURITY_CONTEXT_KEY);
           
                  if (securityContext == null) {
                      sendMessage(message);
                  }
                  else {
                      try {
                          SecurityContextHolder.setContext(securityContext);
                          sendMessage(message);
                      }
                      finally {
                          SecurityContextHolder.clearContext();
                      }
                  }
              }
           
              private void sendMessage(Message<?> message) {
                  messagingTemplate.send(outputChannel, message);
              }
          }

          Can you recommend a better solution?

          Show
          dphollis Damien Hollis added a comment - Hi Artem, I think your suggests would work for Executor and Queue Channels. However, I couldn't see how to make either work for a Jms Channel. So instead I implemented a MessageHandler and configured it at the beginning of a chain of handlers: public class ContextPropagatingMessageHandler extends AbstractMessageHandler implements MessageProducer {   private final MessagingTemplate messagingTemplate = new MessagingTemplate();   private MessageChannel outputChannel;   @Override public void setOutputChannel(MessageChannel outputChannel) { this.outputChannel = outputChannel; }   @Override protected void handleMessageInternal(Message<?> message) throws Exception { SecurityContext securityContext = (SecurityContext) message.getHeaders().get(ContextPropagatingChannelInterceptor.SECURITY_CONTEXT_KEY);   if (securityContext == null) { sendMessage(message); } else { try { SecurityContextHolder.setContext(securityContext); sendMessage(message); } finally { SecurityContextHolder.clearContext(); } } }   private void sendMessage(Message<?> message) { messagingTemplate.send(outputChannel, message); } } Can you recommend a better solution?
          Hide
          abilan Artem Bilan added a comment -

          Hi, Damien!

          Sorry for delay.
          However this issue is very specific and has a lot of places for the imagination
          Instead of coding a MessageHandler you can build some gereric Advice and use it withing <request-handler-advice-chain>: http://docs.spring.io/spring-integration/docs/2.2.6.RELEASE/reference/html/messaging-endpoints-chapter.html#message-handler-advice-chain

          And, as you noticed, provided solution isn't robust and it doesn't cover all use-cases. There is still a window for involving an end-user to achieve the real solution.
          That's why I said that it shouldn't be provided by framework by default.
          SecurityContext is very simple and even Serializable object and Srping Security provides enough tools to work with it in the application with Spring infrastructure.

          Thanks

          Show
          abilan Artem Bilan added a comment - Hi, Damien! Sorry for delay. However this issue is very specific and has a lot of places for the imagination Instead of coding a MessageHandler you can build some gereric Advice and use it withing <request-handler-advice-chain> : http://docs.spring.io/spring-integration/docs/2.2.6.RELEASE/reference/html/messaging-endpoints-chapter.html#message-handler-advice-chain And, as you noticed, provided solution isn't robust and it doesn't cover all use-cases. There is still a window for involving an end-user to achieve the real solution. That's why I said that it shouldn't be provided by framework by default. SecurityContext is very simple and even Serializable object and Srping Security provides enough tools to work with it in the application with Spring infrastructure. Thanks
          Hide
          w_c_smith Christopher Smith added a comment -

          I would like to voice support for built-in (or at least packaged-in-an-extension) support for propagating the SecurityContext, as is found in Spring Remoting. While Spring Security does make the tools available to implement propagation, it's a fairly cookie-cutter requirement, and both DRY and security principles suggest it should be handled in a general-purpose library.

          Even if there is more than one protocol for how to propagate the context (and I can only think of the one where the context follows the message), it seems that they should be limited enough to be enumerated and supported in-library. What other propagation protocol(s) do you think users would want?

          Show
          w_c_smith Christopher Smith added a comment - I would like to voice support for built-in (or at least packaged-in-an-extension) support for propagating the SecurityContext , as is found in Spring Remoting. While Spring Security does make the tools available to implement propagation, it's a fairly cookie-cutter requirement, and both DRY and security principles suggest it should be handled in a general-purpose library. Even if there is more than one protocol for how to propagate the context (and I can only think of the one where the context follows the message), it seems that they should be limited enough to be enumerated and supported in-library. What other propagation protocol(s) do you think users would want?
          Hide
          w_c_smith Christopher Smith added a comment -

          For whatever it's worth, I've written an example project demonstrating the behavior of thread-local data under various combinations of channel types and synchronous/asynchronous invocation. I will note that as a client of a gateway interface, the differing behavior based on a runtime configuration option is surprising in a bad way, and a policy of "all information needs to be written into the API" without some sort of magic summoning-from-the-context like Spring MVC does with controller invocations means that every gateway interface needing security information will have to have its own wrapper that does nothing but propagate the context.

          Show
          w_c_smith Christopher Smith added a comment - For whatever it's worth, I've written an example project demonstrating the behavior of thread-local data under various combinations of channel types and synchronous/asynchronous invocation . I will note that as a client of a gateway interface, the differing behavior based on a runtime configuration option is surprising in a bad way, and a policy of "all information needs to be written into the API" without some sort of magic summoning-from-the-context like Spring MVC does with controller invocations means that every gateway interface needing security information will have to have its own wrapper that does nothing but propagate the context.
          Hide
          david_syer Dave Syer added a comment -

          I think the fact that it is boilerplate tells me we should have something in the framework. The main concern for me is the clean up. You want to be double sure that background threads do not have ThreadLocals with SecurityContext in them after a message has been processed.

          Show
          david_syer Dave Syer added a comment - I think the fact that it is boilerplate tells me we should have something in the framework. The main concern for me is the clean up. You want to be double sure that background threads do not have ThreadLocals with SecurityContext in them after a message has been processed.

            People

            • Assignee:
              abilan Artem Bilan
              Reporter:
              abilan Artem Bilan
            • Votes:
              2 Vote for this issue
              Watchers:
              10 Start watching this issue

              Dates

              • Created:
                Updated: