/* * See LICENSE for licensing and NOTICE for copyright. */ package net.shibboleth.idp.cas.ticket; import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty; import net.shibboleth.utilities.java.support.annotation.constraint.Positive; import net.shibboleth.utilities.java.support.logic.Constraint; import net.shibboleth.utilities.java.support.primitive.StringSupport; import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy; import org.cryptacular.generator.IdGenerator; import org.cryptacular.generator.RandomIdGenerator; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; /** * Generates CAS protocol ticket identifiers of the form: * * <pre> * [PREFIX]-[SEQUENCE_PART]-[RANDOM_PART]-[SUFFIX], * </pre> * * where suffix is optional. By default tickets have at least 128 bits of entropy in the random part of the identifier. * * @author Marvin S. Addison */ public class TicketIdentifierGenerationStrategy implements IdentifierGenerationStrategy { /** Ticket prefix. */ @Nonnull @NotEmpty private String prefix; /** Ticket suffix. */ @Nullable private String suffix; /** Generator of random ticket part. */ private IdGenerator randomPartGenerator; /** * Creates a new ticket ID generator. * * @param randomLength Length in characters of random part of the ticket. * @param prefix Ticket ID prefix (e.g. ST, PT, PGT). MUST be a URL safe string. */ public TicketIdentifierGenerationStrategy( @Positive final int randomLength, @Nonnull @NotEmpty final String prefix) { if (randomLength < 1) { throw new IllegalArgumentException("Length of random part of ticket must be positive"); } this.randomPartGenerator = new RandomIdGenerator(randomLength); this.prefix = Constraint.isNotNull(StringSupport.trimOrNull(prefix), "Prefix cannot be null or empty"); if (!isUrlSafe(this.prefix)) { throw new IllegalArgumentException("Unsupported prefix " + this.prefix); } } /** * Sets the ticket ID suffix. * * @param suffix Ticket suffix. */ public void setSuffix(@Nullable final String suffix) { final String s = StringSupport.trimOrNull(suffix); if (s != null) { if (!isUrlSafe(s)) { throw new IllegalArgumentException("Unsupported suffix " + s); } this.suffix = s; } } @Override @Nonnull public String generateIdentifier() { final StringBuilder builder = new StringBuilder(100); builder.append(prefix).append('-'); builder.append(System.currentTimeMillis()).append('-'); builder.append(randomPartGenerator.generate()); if (suffix != null) { builder.append('-').append(suffix); } return builder.toString(); } @Nonnull @Override public String generateIdentifier(final boolean xmlSafe) { return generateIdentifier(); } private static boolean isUrlSafe(final String s) { try { return URLEncoder.encode(s, StandardCharsets.US_ASCII.name()).equals(s); } catch (Exception e) { return false; } } }