class CasAuthenticator extends ProfileDefinitionAware implements Authenticator {

    protected CasConfiguration configuration;

    protected String clientName;

    protected UrlResolver urlResolver;

    protected CallbackUrlResolver callbackUrlResolver;

    protected String callbackUrl;

    protected SessionLogoutHandler sessionLogoutHandler;

    /**
     * <p>Constructor for CasAuthenticator.</p>
     *
     * @param configuration a {@link CasConfiguration} object
     * @param clientName a {@link String} object
     * @param urlResolver a {@link UrlResolver} object
     * @param callbackUrlResolver a {@link CallbackUrlResolver} object
     * @param callbackUrl a {@link String} object
     * @param sessionLogoutHandler the sessionLogoutHandler
     */
    public CasAuthenticator(final CasConfiguration configuration, final String clientName, final UrlResolver urlResolver,
                            final CallbackUrlResolver callbackUrlResolver, final String callbackUrl,
                            final SessionLogoutHandler sessionLogoutHandler) {
        this.configuration = configuration;
        this.clientName = clientName;
        this.urlResolver = urlResolver;
        this.callbackUrlResolver = callbackUrlResolver;
        this.callbackUrl = callbackUrl;
        this.sessionLogoutHandler = sessionLogoutHandler;
    }

    /** {@inheritDoc} */
    @Override
    protected void internalInit(final boolean forceReinit) {
        CommonHelper.assertNotNull("urlResolver", urlResolver);
        CommonHelper.assertNotNull("callbackUrlResolver", callbackUrlResolver);
        CommonHelper.assertNotBlank("clientName", clientName);
        CommonHelper.assertNotBlank("callbackUrl", callbackUrl);
        CommonHelper.assertNotNull("configuration", configuration);

        setProfileDefinitionIfUndefined(new CasProfileDefinition());
    }

    @Override
    public Optional<Credentials> validate(final CallContext ctx, final Credentials cred) {
        if (cred instanceof TokenCredentials credentials) {
            init();

            val webContext = ctx.webContext();

            val ticket = credentials.getToken();
            try {
                val assertion = validateTicket(webContext, ticket);

                recordSession(ctx, ticket);

                val principal = assertion.getPrincipal();
                LOGGER.debug("principal: {}", principal);
                val profile = createUserProfile(principal);

                createUserProfileAttributes(assertion, principal, profile);
                LOGGER.debug("profile returned by CAS: {}", profile);

                credentials.setUserProfile(profile);
            } catch (final TicketValidationException e) {
                var message = "cannot validate CAS ticket: " + ticket;
                throw new TechnicalException(message, e);
            }
        }

        return Optional.ofNullable(cred);
    }

    protected void recordSession(final CallContext ctx, final String ticket) {
        if (sessionLogoutHandler != null) {
            sessionLogoutHandler.recordSession(ctx, ticket);
        }
    }

    protected void createUserProfileAttributes(final Assertion assertion, final AttributePrincipal principal,
                                               final UserProfile profile) {
        Map<String, Object> newPrincipalAttributes = new HashMap<>();
        Map<String, Object> newAuthenticationAttributes = new HashMap<>();
        // restore both sets of attributes
        val oldPrincipalAttributes = principal.getAttributes();
        val oldAuthenticationAttributes = assertion.getAttributes();
        if (oldPrincipalAttributes != null) {
            newPrincipalAttributes.putAll(oldPrincipalAttributes);
        }
        if (oldAuthenticationAttributes != null) {
            newAuthenticationAttributes.putAll(oldAuthenticationAttributes);
        }

        getProfileDefinition().convertAndAdd(profile, newPrincipalAttributes, newAuthenticationAttributes);
    }

    protected UserProfile createUserProfile(final AttributePrincipal principal) {
        val id = principal.getName();
        val profile = getProfileDefinition().newProfile(id, configuration.getProxyReceptor(), principal);
        profile.setId(ProfileHelper.sanitizeIdentifier(id));
        return profile;
    }

    protected Assertion validateTicket(final WebContext webContext, final String ticket) throws TicketValidationException {<FILL_FUNCTION_BODY>}
}

        val finalCallbackUrl = callbackUrlResolver.compute(urlResolver, callbackUrl, clientName, webContext);
        return configuration.retrieveTicketValidator(webContext).validate(ticket, finalCallbackUrl);
    
<methods>public non-sealed void <init>() ,public org.pac4j.core.profile.definition.ProfileDefinition getProfileDefinition() ,public void setProfileDefinition(org.pac4j.core.profile.definition.ProfileDefinition) <variables>private org.pac4j.core.profile.definition.ProfileDefinition profileDefinition