-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve TGT deletion behavior #6156
Conversation
Thank you for the patch. Unfortunately, this has many side effects. In most deployments, cleaner is turned off to assist with performance; this means the deployment relies on the ticket storage backend itself to remove tickets. This only works in scenarios where the ticket expiration policy defined in CAS is or can be directly supported by the ticket registry (i.e. max-time-to-live). If the policy is not supported directly by the registry, then CAS needs to evaluate the expiration criteria for a ticket itself, and then delete it if it sees it expired, on demand. That is, a ticket could be perfectly valid from a registry technology point of view, but expired from the CAS point of view. So we cannot let the expired ticket just sit there, specially if you're paying for storage costs. My suggestion would be that we publish an event when we delete the TGT (this might already exist in fact). An event listener can capture the event, and then optionally invoke SLO when necessary, and if turned on. If SLO is disabled, then we simply delete the ticket and nothing further happens. The configuration of the cleaner is of no consequence and should not be taken into account. |
My bad: I forgot that the cleaner must be considered disabled at the ticket registry level for the I don't understand your comment : "In most deployments, cleaner is turned off to assist with performance...". If the cleaner is turned off, then, the behavior of the ticket registry remains the same as it was previously: the TGT (like any other ticket) is removed if expired. My change only occurs when the cleaner is enabled: in that case, the expired TGT is not removed from the ticket registry to give the opportunity to the cleaner to perform a SLO for this TGT. The most relevant part of the PR (https://github.com/apereo/cas/pull/6156/files#diff-87c2fc38c2877ea256bebb5ab765351cc1d4352f72c99069359f6645da58927cR112): val isTgt = ticket instanceof TicketGrantingTicket;
if (!isTgt || !cleanerEnabled) {
deleteSingleTicket(ticket);
}
|
Yes, that is exactly the problem that we should address based on your original explanation. At the moment, the function of SLO is very much tried to the cleaner running active. We need to aim for and cover the following scenario:
Conflating SLO with the cleaner status is the problem here. |
OK. I get it. I will adjust the PR. |
I have studied the source code: we already have a I guess I could add a That said, we have another issue in the source code: if we call the |
I forgot to mention that the |
In the @Override
public int cleanTicket(final Ticket ticket) {
return lockRepository.execute(ticket.getId(), () -> {
try {
if (ticket instanceof final TicketGrantingTicket tgt) {
applicationContext.publishEvent(new CasRequestSingleLogoutEvent(this, tgt, null));
applicationContext.publishEvent(new CasTicketGrantingTicketDestroyedEvent(this, tgt, null));
}
} catch (final Throwable e) {
LoggingUtils.error(LOGGER, e);
}
try {
LOGGER.debug("Cleaning up expired ticket [{}]", ticket.getId());
return ticketRegistry.deleteTicket(ticket);
} catch (final Throwable e) {
LoggingUtils.error(LOGGER, e);
return 0;
}
}).orElse(0);
} |
In the @Override
public Ticket getTicket(final String ticketId) {
val returnTicket = getTicket(ticketId, ticket -> {
if (ticket.isExpired()) {
val ticketAgeSeconds = getTicketAgeSeconds(ticket);
LOGGER.debug("Ticket [{}] has expired according to policy [{}] after [{}] seconds and [{}] uses and will be removed from the ticket registry",
ticketId, ticket.getExpirationPolicy().getName(), ticketAgeSeconds, ticket.getCountOfUses());
if (ticket instanceof final TicketGrantingTicket tgt) {
val clientInfo = ClientInfoHolder.getClientInfo();
applicationContext.publishEvent(new CasRequestSingleLogoutEvent(this, tgt, clientInfo));
applicationContext.publishEvent(new CasTicketGrantingTicketDestroyedEvent(this, tgt, clientInfo));
}
try {
deleteTicket(ticket);
} catch (final Exception e) {
LOGGER.warn("Deletion failed for ticket [{}]", ticket.getId());
}
return false;
}
return true;
});
if (returnTicket != null) {
val ticketAgeSeconds = getTicketAgeSeconds(returnTicket);
if (ticketAgeSeconds < -1) {
LOGGER.warn("Ticket created [{}] second(s) in the future. Check time synchronization on all servers.", ticketAgeSeconds * -1);
}
}
return returnTicket;
} What do you think? |
As our discussion led us to a new approach, I opened a new PR: #6161 Some tests may fail. Waiting for the build to pass and give a better overview. |
Currently, there is a flaw in the current deletion behavior of the expired TGT: depending on the settings and the user browsing, the expired TGT can be removed by the ticket registry during a "get" operation or by the ticket registry cleaner.
Both cases are not the same: in the first case, the TGT only is deleted while in the second case, a whole single logout is performed.
So I propose that the ticket registry does not delete an expired TGT on a "get" operation if the cleaner is enabled, letting the cleaner eventually removes the TGT and performs a full SLO.