Well here is my solution:
Class: CentralAuthenticationServiceImpl

private final Map<TicketGrantingTicket, Map<String, ServiceUse>>
servicesMonitoring =  new WeakHashMap<TicketGrantingTicket, Map<String,
ServiceUse>>();

<code>

@Auditable(action="SERVICE_TICKET",successSuffix="_CREATED",failureSuffix="_NOT_CREATED",actionResolverClass=org.inspektr.audit.spi.support.ObjectCreationAuditableActionResolver.class,resourceResolverClass=org.jasig.cas.audit.spi.ServiceResourceResolver.class)
   
@Statistic(name="GRANT_SERVICE_TICKET",requiredPrecision={Precision.DAY,Precision.MINUTE,Precision.HOUR})
    @Transactional(readOnly = false)
    public String grantServiceTicket(final String ticketGrantingTicketId,
        final Service service, final Credentials credentials)
        throws TicketException {

        Assert.notNull(ticketGrantingTicketId,
            "ticketGrantingticketId cannot be null");
        Assert.notNull(service, "service cannot be null");

        final TicketGrantingTicket ticketGrantingTicket;
        ticketGrantingTicket = (TicketGrantingTicket) this.ticketRegistry
            .getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);

// if a user is not logged out, and did not sign out after 8 hours, the
cookie won't expire, but the ticket WILL
        if (ticketGrantingTicket == null){
                         throw new InvalidTicketException();
        }
        synchronized (ticketGrantingTicket) {
            if (ticketGrantingTicket.isExpired()) {
                this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
                throw new InvalidTicketException();
            }
        }

        final RegisteredService registeredService = this.servicesManager
            .findServiceBy(service);

        if (registeredService == null || !registeredService.isEnabled()) {
            if (log.isDebugEnabled()) {
                log.debug("Service [" + service.getId()
                    + "] not found in ServiceRegistry.");
            }
            throw new UnauthorizedServiceException();
        }

        if (!registeredService.isSsoEnabled() && credentials == null
            && ticketGrantingTicket.getCountOfUses() > 0) {
            throw new UnauthorizedSsoServiceException();
        }

        checkForAbuse(service, ticketGrantingTicket, registeredService);

        if (credentials != null) {
            try {
                final Authentication authentication =
this.authenticationManager
                    .authenticate(credentials);
                final Authentication originalAuthentication =
ticketGrantingTicket.getAuthentication();

                if
(!(authentication.getPrincipal().equals(originalAuthentication.getPrincipal())
&&
authentication.getAttributes().equals(originalAuthentication.getAttributes())))
{
                    throw new TicketCreationException();
                }
            } catch (final AuthenticationException e) {
                throw new TicketCreationException(e);
            }
        }

        // XXX fix this
        final UniqueTicketIdGenerator serviceTicketUniqueTicketIdGenerator =
this.uniqueTicketIdGeneratorsForService
            .get(service.getClass().getName());

        final ServiceTicket serviceTicket = ticketGrantingTicket
            .grantServiceTicket(serviceTicketUniqueTicketIdGenerator
                .getNewTicketId(ServiceTicket.PREFIX), service,
                this.serviceTicketExpirationPolicy, credentials != null);

        // FIXME: when a Data Exception happens, log it to the logfile
        try {
                this.serviceTicketRegistry.addTicket(serviceTicket);
        } catch (Exception de) {
                log.error("Data exception while saving the ticket " +
serviceTicket.getId() + " to the database");
                log.error("The TGT for this service ticket is been used for
following services:");
                StringBuilder strb = new StringBuilder();
                if (serviceTicket.getGrantingTicket() != null) {
                        for (String serviceId :
serviceTicket.getGrantingTicket().getServices()) {
                                strb.append(serviceId + ", ");
                        }
                        log.error(strb.toString());
                }
        }

        if (log.isInfoEnabled()) {
            log.info("Granted service ticket ["
                + serviceTicket.getId()
                + "] for service ["
                + service.getId()
                + "] for user ["
                + serviceTicket.getGrantingTicket().getAuthentication()
                    .getPrincipal().getId() + "]");
        }
        return serviceTicket.getId();
    }

        /**
         * Check for abuse.
         *
         * @param service
         *            the service
         * @param ticketGrantingTicket
         *            the ticket granting ticket
         * @param registeredService
         *            the registered service
         *
         * @throws InvalidTicketException
         *             the invalid ticket exception
         */
        private void checkForAbuse(final Service service, final
TicketGrantingTicket ticketGrantingTicket, final RegisteredService
registeredService) throws InvalidTicketException {
                ServiceUse serviceUse = null;

        // returns null if not exists(any more)

        Map<String, ServiceUse> serviceUseMap = null;
        serviceUseMap = servicesMonitoring.get(ticketGrantingTicket);

        if(serviceUseMap==null){
                serviceUseMap = new HashMap<String, ServiceUse>();
        }

        serviceUse =  serviceUseMap.get(registeredService.getServiceId());


        if(serviceUse == null){
            serviceUse =
serviceUseFactory.createServiceUse(registeredService, service,
ticketGrantingTicket);
        }

        if(serviceUse.testForAbuse()){
                throw new UnauthorizedServiceException();
        }

        serviceUseMap.put(registeredService.getServiceId(), serviceUse);
                this.servicesMonitoring.put(ticketGrantingTicket, 
serviceUseMap);

        if (ticketGrantingTicket == null) {
            throw new InvalidTicketException();
        }
        }
</code>

/**
 * The Class ServiceUse.
 */
<code>
public class ServiceUse implements Serializable {


    /** The Commons Logging instance. */
    private final Log log = LogFactory.getLog(getClass());

        /** The Constant serialVersionUID. */
        private static final long serialVersionUID = 1L;

        /** The TicketGrantingTicket this is associated with. */
        private TicketGrantingTicket ticketGrantingTicket;

        /** The service this ticket is valid for. */
        private Service service;

        /** The time set. */
        private Long timeStartedMonitoring;

        /** The Number redirects. */
        private Integer numberRedirects = 0;

        /** The max time. in miliseconds */
        private Long maxTime;

        /** The maxium redirects. */
        private Integer maximumRedirects;

        /** The abuse service. */
        private Boolean abuseServiceDetected = false;

        private RegisteredService registeredService;


        public final RegisteredService getRegisteredService() {
                return registeredService;
        }

        public final void setRegisteredService(RegisteredService 
registeredService)
{
                this.registeredService = registeredService;
        }

        /**
         * Gets the max used ratio.
         *
         * @return the max used ratio
         */
        private final Long getMaxUsedRatio(){
                return maxTime/maximumRedirects;
        }

        /**
         * Gets the current used ratio, this is calculated from the max time.
         *
         *
         * @return the current used ratio
         */
        private final Long getCurrentUsedRatio(){
                return maxTime/numberRedirects;
        }

        /**
         * Gets the maxium redirects.
         *
         * @return the maxium redirects
         */
        public final Integer getMaximumRedirects() {
                return maximumRedirects;
        }

        /**
         * Sets the maxium redirects.
         *
         * @param maximumRedirects
         *            the new maxium redirects
         */
        public final void setMaximumRedirects(final Integer maxiumRedirects) {
                this.maximumRedirects = maxiumRedirects;
        }

        /**
         * Gets the max time.
         *
         * @return the max time
         */
        public final Long getMaxTime() {
                return maxTime;
        }

        /**
         * Sets the max time.
         *
         * @param maxTime
         *            the new max time
         */
        public final void setMaxTime(final Long maxTime) {
                this.maxTime = maxTime;
        }

        /**
         * Gets the number redirects.
         *
         * @return the number redirects
         */
        public final Integer getNumberRedirects() {
                return numberRedirects;
        }

        /**
         * Sets the number redirects.
         *
         * @param numberRedirects
         *            the new number redirects
         */
        public final void setNumberRedirects(final Integer numberRedirects) {
                this.numberRedirects = numberRedirects;
        }

        /**
         * Gets the service.
         *
         * @return the service
         */
        public final Service getService() {
                return service;
        }

        /**
         * Sets the service.
         *
         * @param service
         *            the new service
         */
        public final void setService(final Service service) {
                this.service = service;
        }

        /**
         * Gets the ticket granting ticket.
         *
         * @return the ticket granting ticket
         */
        public final TicketGrantingTicket getTicketGrantingTicket() {
                return ticketGrantingTicket;
        }

        /**
         * Sets the ticket granting ticket.
         *
         * @param ticketGrantingTicket
         *            the new ticket granting ticket
         */
        public final void setTicketGrantingTicket(
                        TicketGrantingTicket ticketGrantingTicket) {
                this.ticketGrantingTicket = ticketGrantingTicket;
        }

        /**
         * Gets the time set.
         *
         * @return the time set
         */
        public final Long getTimeStartedMonitoring() {
                return timeStartedMonitoring;
        }

        /**
         * Sets the time set.
         *
         * @param timeStartedMonitoring
         *            the new time set
         */
        public final void setTimeStartedMonitoring(final Long timeSet) {
                this.timeStartedMonitoring = timeSet;
        }

        /**
         * Gets the abuse service.
         *
         * @return the abuse service
         */
        public final Boolean getAbuseServiceDetected() {
                return abuseServiceDetected;
        }

        /**
         * Test for abuse.
         *
         * @return true, if abused is detected
         */
        public boolean testForAbuse() {

                Boolean abused = true;
                if (!getAbuseServiceDetected()) {
                        if (checkMonitoringTime()) {
                                setReStartMonitoring();
                        }

                        numberRedirects+=1;

                        if(getMaxUsedRatio() < getCurrentUsedRatio()){
                                abused = false;
                        }else{
                                abuseServiceDetected = true;
                                log.error("BLOCKED<" +  
registeredService.getServiceId()
                                                + "> " + 
"url+params_last_used:" + service.getId() + numberRedirects +
" req in " + (System.currentTimeMillis() - timeStartedMonitoring) + "ms "
                                                + ticketGrantingTicket.getId()
                                                + " TGT_#used: "
                                                + 
ticketGrantingTicket.getCountOfUses());
                        }
                }
                return abused;

        }

        /**
         * Check monitoring time.
         *
         * @return the boolean
         */
        private Boolean checkMonitoringTime() {
                return ((System.currentTimeMillis() - timeStartedMonitoring) > 
maxTime);
        }

        /**
         * Sets the start monitoring.
         */
        private void setReStartMonitoring() {
                timeStartedMonitoring = System.currentTimeMillis();
                numberRedirects = 0;
        }

        /**
         * Instantiates a new service use.
         */
        public ServiceUse() {
                super();
                timeStartedMonitoring = System.currentTimeMillis();

        }

        /* (non-Javadoc)
         * @see java.lang.Object#hashCode()
         */
        @Override
        public int hashCode() {
                final int PRIME = 31;
                int result = 1;
                result = PRIME * result + ((service == null) ? 0 : 
service.hashCode());
                result = PRIME
                                * result
                                + ((ticketGrantingTicket == null) ? 0 : 
ticketGrantingTicket
                                                .hashCode());
                return result;
        }

        /* (non-Javadoc)
         * @see java.lang.Object#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object obj) {
                if (this == obj)
                        return true;
                if (obj == null)
                        return false;
                if (getClass() != obj.getClass())
                        return false;
                final ServiceUse other = (ServiceUse) obj;
                if (service == null) {
                        if (other.service != null)
                                return false;
                } else if (!service.equals(other.service))
                        return false;
                if (ticketGrantingTicket == null) {
                        if (other.ticketGrantingTicket != null)
                                return false;
                } else if 
(!ticketGrantingTicket.equals(other.ticketGrantingTicket))
                        return false;
                return true;
        }

}

</code>

<code>
public interface ServiceUseFactory {

        public abstract ServiceUse createServiceUse(RegisteredService
registeredService, Service service,
                        TicketGrantingTicket ticketGrantingTicket);

}
</code>

<code>
/**
 * The Class ServiceUseFactoryImpl.
 */
public class ServiceUseFactoryImpl implements ServiceUseFactory {



        /** The max time. in miliseconds */
        private Long maxTime;

        /** The maxium redirects. */
        private Integer maximumRedirects;

        /**
         * Gets the maxium redirects.
         *
         * @return the maxium redirects
         */
        public final Integer getMaximumRedirects() {
                return maximumRedirects;
        }

        /**
         * Sets the maxium redirects.
         *
         * @param maximumRedirects
         *            the new maxium redirects
         */
        public final void setMaximumRedirects(final Integer maximumRedirects) {
                this.maximumRedirects = maximumRedirects;
        }

        /**
         * Gets the max time.
         *
         * @return the max time
         */
        public final Long getMaxTime() {
                return maxTime;
        }

        /**
         * Sets the max time.
         *
         * @param maxTime
         *            the new max time
         */
        public final void setMaxTime(final Long maxTime) {
                this.maxTime = maxTime;
        }

        /**
         * Instantiates a new service use factory impl.
         */
        public ServiceUseFactoryImpl(){

        }

        /* (non-Javadoc)
         * @see
org.jasig.cas.ticket.ServiceUseFactory#createServiceUse(org.jasig.cas.authentication.principal.Service,
org.jasig.cas.ticket.TicketGrantingTicket)
         */
        public ServiceUse createServiceUse(RegisteredService registeredService,
Service service, TicketGrantingTicket ticketGrantingTicket){
                ServiceUse serviceUse = new ServiceUse();
                serviceUse.setRegisteredService(registeredService);
                serviceUse.setMaximumRedirects(maximumRedirects);
                serviceUse.setMaxTime(maxTime);
                serviceUse.setService(service);
                serviceUse.setTicketGrantingTicket(ticketGrantingTicket);
                return serviceUse;


        }


}
</code>

Advantage:
If an application has a badly written cas client, we will now see it on our
logs.

I hope this code can be a bit helpful.

Danny B.
-- 
View this message in context: 
http://www.nabble.com/possible-solution-preventing-abuse-cas-infinite-loop-redirect-service-tp23389145p23518436.html
Sent from the CAS Users mailing list archive at Nabble.com.


-- 
You are currently subscribed to [email protected] as: 
[email protected]
To unsubscribe, change settings or access archives, see 
http://www.ja-sig.org/wiki/display/JSG/cas-user

Reply via email to