I am using spring.security.version = 3.1.0.RELEASE. The problem I am having is that for some reason AuthenticationFailureCredentialsExpiredEvent is not fired.
While debugging the code I found that AbstractUserDetailsAuthenticationProvider do display in the console that "User account credentials have expired". But I am still baffling as to why the event in concern is not triggered.
Here is my code:
class JpaUserDetails implements UserDetails {
...
...
#Override
public boolean isCredentialsNonExpired() {
if (some logic) {
return true;
}
else {
return false;
}
}
}
I do see AbstractUserDetailsAuthenticationProvider displaying in the console "User account credentials have expired" from the following lines of spring code:
public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitilizeBean, MessageSourceAware {
...
...
private class DefaultPostAuthenticationChecks implements UserDetailsChecker {
public void check(UserDetails user) {
if(!user.isCredentialsNonExpired()) {
logger.debug("User account credentials have expired");
throw new CredentialsExpiredException(message.getMessage(
"AbstractUserDetailsAuthenticationProvider.credentialsExpired",
"User credentials have expired"), user);
}
}
}
}
The issue is that when the user credentials have expired, I am expecting the Spring to generate the event AuthenticationFailureCredentialsExpiredEvent which I am handling in the following way:
class SecurityEventDispatcher implements ApplicationListener<ApplicationEvent> {
final List<SecurityEventListener> listeners = new ArrayList<SecurityEventListener>();
public void registerListener(SecurityEventListener listener) {
this.listener.add(listener);
}
public void onApplicationEvent(ApplicationEvent event) {
for (SecurityEventListener listener : this.listeners) {
if(listener.canHandle(event)) {
listener.handle(event);
}
}
}
}
This is how I am handling the login failure event:
public class LoginFailedEvent extends SecurityEventListener {
#Override
public boolean canHandle(Object event) {
if(event instanceof AbstractAuthenticationFailureEvent) {
return true;
}
else {
return false;
}
}
#Override
public void handle(Object event) {
if (event instanceof AuthenticationFailureBadCredentialsEvent) {
// do something
}
if (event instanceof AuthenticationFailureCredentialsExpiredEvent) {
// do something
}
}
}
The issue as I mentioned before is that AuthenticationFailureCredentialsExpiredEvent is never fired. I have tested the AuthenticationFailureBadCredentialsEvent which works fine.
This is what I get in event for bad credentials: (which is working fine)
org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent
This is what I get in event for expired password:
ServletRequestHandledEvent: url=[/app/loginFailure] with failureCause = null
Does anyone have any idea what could be wrong? Any help will be highly appreciated.
Here is the answer to this question, since there isn't any much literature out there regarding the issue.
You probably need to set the ProviderManager's
('s) eventPublisher to be something other than
NullEventPublisher. There is not a simple way to do this via the
tag, so you will want to create the
AuthenticationProvider using standard beans configuration and inject
it into a standard Spring Bean for the ProviderManager.
Rob Winch - Spring Security Lead
If anyone is running into this issue, just upgrade the spring security to 3.1.2 or +, the issue is fixed.
(Applies to Spring Security 5)
Spring's default publisher is the NullEventPublisher. This will effectively publish nothing.To get events please configure a
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
#Bean
public DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher() {
return new DefaultAuthenticationEventPublisher();
}
Now the events a published and one can just consume them as any other event:
#EventListener
public void logAuditEvents(AbstractAuthenticationEvent event) {
...
}
and
#EventListener
public void logAuditEvents(AbstractAuthorizationEvent event) {
...
}
Related
I consume messages from spring-cloud-stream through a Consumer<MyMessage> Implementation. As part of the message handling I need to access methods that are protected with #PreAuthorize security-checks. By default the Consumer run unauthenticated so message-handling fails.
Consumer:
#Bean
public Consumer<MyMessage> exampleMessageConsumer(MyMessageConsumer consumer) {
return consumer::handleMessage;
}
Secured Method:
#PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER')")
public void doSomething() { ... }
I dont just want to bypass security, so what is the easiest way to authenticate my Consumer so it passes the check?
EDIT: we are using google pubsub as a binder
For the Kafka binder:
Add an #EventListener to listen for ConsumerStartedEvents; you can then add the authentication to the security context via the SecurityContextHolder; this binds it to the thread; the same thread is used to call the listener.
I found two possible solutions to my problem
use springs RunAs support (baeldung) to add permissions to a security context for a specific method. If i do this i need to add ROLE_RUN_AS_USER to my secured methods. At scale this would complicated annotations a lot.
Manually change the security context before executing the handler method and return it to its original state afterwards.
I went with the second option. I would have liked a transparent solution but there does not appear to be one.
To make this work i created a class that wraps a functional interface with the changing code and returns it.
public class RunAs {
#FunctionalInterface
public interface RunAsMethod {
void runWithException() throws Throwable;
}
public static <T> Consumer<T> createWriteConsumer(Consumer<T> originalConsumer) {
return message -> runWithWritePermission(() -> originalConsumer.accept(message));
}
public static void runWithWritePermission(final RunAsMethod func) {
final Authentication originalAuthentication = SecurityContextHolder.getContext().getAuthentication();
final AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(
"system",
originalAuthentication != null ? originalAuthentication.getPrincipal() : "system",
AuthorityUtils.createAuthorityList("ROLE_ADMIN", "SCOPE_write")
);
SecurityContextHolder.getContext().setAuthentication(token);
try {
func.runWithException();
} catch (Throwable e) {
throw new RuntimeException("exception during method with altered permissions", e);
} finally {
SecurityContextHolder.getContext().setAuthentication(originalAuthentication);
}
}
}
Problem
I have a Spring-Boot application in which I am also starting a gRPC server/service. Both the servlet and gRPC code send requests to a common object to process the request. When the request comes in I want to update the logging to display a unique 'ID' so I can track the request through the system.
On the Spring side I have setup a 'Filter' which updates the logging MDC to add some data to the log request (see this example). this works fine
On the gRPC side I have created an 'ServerInterceptor' and added it to the service, while the interceptor gets called the code to update the MDC does not stick, so when a request comes through the gRPC service I do not get the ID printed in the log. I realize this has to do with the fact that I'm intercepting the call in one thread and it's being dispatched by gRPC in another, what I can't seem to figure out is how to either intercept the call in the thread doing the work or add the MDC information so it is properly propagated to the thread doing the work.
What I've tried
I have done a lot of searches and was quite surprised to not find this asked/answered, I can only assume my query skills are lacking :(
I'm fairly new to gRPC and this is the first Interceptor I'm writing. I've tried adding the interceptor several different ways (via ServerInterceptors.intercept, BindableService instance.intercept).
I've looked at LogNet's Spring Boot gRPC Starter, but I'm not sure this would solve the issue.
Here is the code I have added in my interceptor class
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call, final Metadata headers, final ServerCallHandler<ReqT, RespT> next) {
try {
final String mdcData = String.format("[requestID=%s]",
UUID.randomUUID().toString());
MDC.put(MDC_DATA_KEY, mdcData);
return next.startCall(call, headers);
} finally {
MDC.clear();
}
}
Expected Result
When a request comes in via the RESTful API I see log output like this
2019-04-09 10:19:16.331 [requestID=380e28db-c8da-4e35-a097-4b8c90c006f4] INFO 87100 --- [nio-8080-exec-1] c.c.es.xxx: processing request step 1
2019-04-09 10:19:16.800 [requestID=380e28db-c8da-4e35-a097-4b8c90c006f4] INFO 87100 --- [nio-8080-exec-1] c.c.es.xxx: processing request step 2
2019-04-09 10:19:16.803 [requestID=380e28db-c8da-4e35-a097-4b8c90c006f4] INFO 87100 --- [nio-8080-exec-1] c.c.es.xxx: Processing request step 3
...
I'm hoping to get similar output when the request comes through the gRPC service.
Thanks
Since no one replied, I kept trying and came up with the following solution for my interceptCall function. I'm not 100% sure why this works, but it works for my use case.
private class LogInterceptor implements ServerInterceptor {
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call,
final Metadata headers,
final ServerCallHandler<ReqT, RespT> next) {
Context context = Context.current();
final String requestId = UUID.randomUUID().toString();
return Contexts.interceptCall(context, call, headers, new ServerCallHandler<ReqT, RespT>() {
#Override
public ServerCall.Listener<ReqT> startCall(ServerCall<ReqT, RespT> call, Metadata headers) {
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) {
/**
* The actual service call happens during onHalfClose().
*/
#Override
public void onHalfClose() {
try (final CloseableThreadContext.Instance ctc = CloseableThreadContext.put("requestID",
UUID.randomUUID().toString())) {
super.onHalfClose();
}
}
};
}
});
}
}
In my application.properties I added the following (which I already had)
logging.pattern.level=[%X] %-5level
The '%X' tells the logging system to print all of the CloseableThreadContext key/values.
Hopefully this may help someone else.
MDC stores data in ThreadLocal variable and you are right about - "I realize this has to do with the fact that I'm intercepting the call in one thread and it's being dispatched by gRPC in another". Check #Eric Anderson answer about the right way to use ThradLocal in the post -
https://stackoverflow.com/a/56842315/2478531
Here is a working example -
public class GrpcMDCInterceptor implements ServerInterceptor {
private static final String MDC_DATA_KEY = "Key";
#Override
public <R, S> ServerCall.Listener<R> interceptCall(
ServerCall<R, S> serverCall,
Metadata metadata,
ServerCallHandler<R, S> next
) {
log.info("Setting user context, metadata {}", metadata);
final String mdcData = String.format("[requestID=%s]", UUID.randomUUID().toString());
MDC.put(MDC_DATA_KEY, mdcData);
try {
return new WrappingListener<>(next.startCall(serverCall, metadata), mdcData);
} finally {
MDC.clear();
}
}
private static class WrappingListener<R>
extends ForwardingServerCallListener.SimpleForwardingServerCallListener<R> {
private final String mdcData;
public WrappingListener(ServerCall.Listener<R> delegate, String mdcData) {
super(delegate);
this.mdcData = mdcData;
}
#Override
public void onMessage(R message) {
MDC.put(MDC_DATA_KEY, mdcData);
try {
super.onMessage(message);
} finally {
MDC.clear();
}
}
#Override
public void onHalfClose() {
MDC.put(MDC_DATA_KEY, mdcData);
try {
super.onHalfClose();
} finally {
MDC.clear();
}
}
#Override
public void onCancel() {
MDC.put(MDC_DATA_KEY, mdcData);
try {
super.onCancel();
} finally {
MDC.clear();
}
}
#Override
public void onComplete() {
MDC.put(MDC_DATA_KEY, mdcData);
try {
super.onComplete();
} finally {
MDC.clear();
}
}
#Override
public void onReady() {
MDC.put(MDC_DATA_KEY, mdcData);
try {
super.onReady();
} finally {
MDC.clear();
}
}
}
}
Thanks for reading ahead of time. In my main method I have a PublishSubscribeChannel
#Bean(name = "feeSchedule")
public SubscribableChannel getMessageChannel() {
return new PublishSubscribeChannel();
}
In a service that does a long running process it creates a fee schedule that I inject the channel into
#Service
public class FeeScheduleCompareServiceImpl implements FeeScheduleCompareService {
#Autowired
MessageChannel outChannel;
public List<FeeScheduleUpdate> compareFeeSchedules(String oldStudyId) {
List<FeeScheduleUpdate> sortedResultList = longMethod(oldStudyId);
outChannel.send(MessageBuilder.withPayload(sortedResultList).build());
return sortedResultList;
}
}
Now this is the part I'm struggling with. I want to use completable future and get the payload of the event in the future A in another spring bean. I need future A to return the payload from the message. I think want to create a ServiceActivator to be the message end point but like I said, I need it to return the payload for future A.
#org.springframework.stereotype.Service
public class SFCCCompareServiceImpl implements SFCCCompareService {
#Autowired
private SubscribableChannel outChannel;
#Override
public List<SFCCCompareDTO> compareSFCC(String state, int service){
ArrayList<SFCCCompareDTO> returnList = new ArrayList<SFCCCompareDTO>();
CompletableFuture<List<FeeScheduleUpdate>> fa = CompletableFuture.supplyAsync( () ->
{ //block A WHAT GOES HERE?!?!
outChannel.subscribe()
}
);
CompletableFuture<List<StateFeeCodeClassification>> fb = CompletableFuture.supplyAsync( () ->
{
return this.stateFeeCodeClassificationRepository.findAll();
}
);
CompletableFuture<List<SFCCCompareDTO>> fc = fa.thenCombine(fb,(a,b) ->{
//block C
//get in this block when both A & B are complete
Object theList = b.stream().forEach(new Consumer<StateFeeCodeClassification>() {
#Override
public void accept(StateFeeCodeClassification stateFeeCodeClassification) {
a.stream().forEach(new Consumer<FeeScheduleUpdate>() {
#Override
public void accept(FeeScheduleUpdate feeScheduleUpdate) {
returnList new SFCCCompareDTO();
}
});
}
}).collect(Collectors.toList());
return theList;
});
fc.join();
return returnList;
}
}
Was thinking there would be a service activator like:
#MessageEndpoint
public class UpdatesHandler implements MessageHandler{
#ServiceActivator(requiresReply = "true")
public List<FeeScheduleUpdate> getUpdates(Message m){
return (List<FeeScheduleUpdate>) m.getPayload();
}
}
Your question isn't clear, but I'll try to help you with some info.
Spring Integration doesn't provide CompletableFuture support, but it does provide an async handling and replies.
See Asynchronous Gateway for more information. And also see Asynchronous Service Activator.
outChannel.subscribe() should come with the MessageHandler callback, by the way.
I looking for different validation style for forms when I create and update entities.
For Instance, when I create an "UserClass" object it requires an ID to define, but when I update, I do not need ID again, because it is defined by user at the creation step. I have lots of entity and I need to find most proper way.
For instance is this logical?
public interface RecordGroupValidator {
public void validateNew(RecordGroup recordGroup, Errors errors);
public void validateUpdate(RecordGroup recordGroup, Errors errors);
}
Validator :
public class RecordGroupValidatorImpl implements RecordGroupValidator {
#Autowired
RecordGroupService recordGroupService;
#Override
public void validateNew(RecordGroup recordGroup, Errors errors) {
if (!ValidationHandler.validText(recordGroup.getIds())) {
errors.rejectValue(ColumnIdentifier.COLUMN.Ids.name(), TextParameters.SERVLET_RESPONSE.InvalidParameter.getText());
}
if (!ValidationHandler.validText(recordGroup.getName())) {
errors.rejectValue(ColumnIdentifier.COLUMN.Name.name(), TextParameters.SERVLET_RESPONSE.InvalidParameter.getText());
}
if (recordGroup.getRecordGroupType() == null) {
errors.rejectValue(ColumnIdentifier.COLUMN.RecordGroupType.name(), TextParameters.SERVLET_RESPONSE.InvalidParameter.getText());
}
if (recordGroupService.idsExist(recordGroup.getIds())) {
errors.rejectValue(ColumnIdentifier.COLUMN.Ids.name(), TextParameters.SERVLET_RESPONSE.DuplicateEntry.getText());
}
if (recordGroupService.nameExist(recordGroup.getName())) {
errors.rejectValue(ColumnIdentifier.COLUMN.Name.name(), TextParameters.SERVLET_RESPONSE.DuplicateEntry.getText());
}
}
#Override
public void validateUpdate(RecordGroup recordGroup, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, ColumnIdentifier.COLUMN.Name.name(), TextParameters.SERVLET_RESPONSE.InvalidParameter.getText());
if (recordGroup.getRecordGroupType() == null) {
errors.rejectValue(ColumnIdentifier.COLUMN.Type.name(), TextParameters.SERVLET_RESPONSE.InvalidParameter.getText());
}
}
}
I think you should create two validation. One for create and one for update. This will create clever architecture. Because for now you have only one difference but in the future you can have more. In my opinion you should split them now.
My current setup is JBoss Seam 2.2 on JBoss 4.2.3.GA.
I have two Beans like so:
#Name("mailingManager")
#Scope(ScopeType.PAGE)
public class MailingMgr {
private Mailing selectedMailing;
#Observer("mailing.letter.success")
public void recordSuccess(final Object arg) {
if (null != selectedMailing) { // store arg }
}
public void send() {
selectedMailing = new Mailing();
if ('EMAIL' == determineType()) {
EmailSender mailer = (EmailSender) Component.getInstance(EmailSender.class);
mailer.send(getAddresses());
}
// ... more options
}
}
#Name("emailSender")
#Scope(ScopeType.PAGE)
public class EmailSender {
public void send(final Set<String> addresses) {
for (String addr : addresses) {
// ... create a mail
Events.instance().raiseEvent("mailing.letter.success", getGeneratedMail());
}
}
}
The problem is that when recordSuccess() is called selectedMailing is always null.
As a workaround I'm setting selectedMailing in the conversation context manually before calling any code that could potentially trigger my events, and then annotate my field with #In(required=false) to inject it again before recordSuccess is called. But is there a more elegant solution (keeping the decoupling intact)? And why isn't the calling bean reused to handle the event?