Package org.jasig.cas.web.flow

Source Code of org.jasig.cas.web.flow.CasFlowExecutionKeyFactory

/*
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
* distributed with this file and available online at
* http://www.ja-sig.org/products/cas/overview/license/
*/
package org.jasig.cas.web.flow;

import org.springframework.util.StringUtils;
import org.springframework.webflow.conversation.*;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionKey;
import org.springframework.webflow.execution.repository.BadlyFormattedFlowExecutionKeyException;
import org.springframework.webflow.execution.repository.FlowExecutionRepositoryException;
import org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository;
import org.springframework.webflow.execution.repository.snapshot.FlowExecutionSnapshotFactory;
import org.springframework.webflow.execution.repository.support.CompositeFlowExecutionKey;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.security.SecureRandom;
import java.util.UUID;

/**
* Extension to the default Spring Web Flow {@link DefaultFlowExecutionRepository}
* that supports {@link CasFlowExecutionKey}, which contains a random component
* that is suitable for use as the LT parameter of the CAS protocol.  The random
* UUID component of the flow key is stored as a conversation attribute to
* support validation when the flow is resumed.
*
* @author Scott Battaglia
* @author Marvin S. Addison
* @version $Revision$ $Date$
* @since 3.4.7
*/
public final class CasFlowExecutionKeyFactory extends DefaultFlowExecutionRepository {

    private static final String UUID_KEY = "CAS_UUID";
   
    private final SecureRandom secureRandom = new SecureRandom();

    private boolean defaultBehavior = false;

    @NotNull
    private final ConversationManager conversationManager;

    public CasFlowExecutionKeyFactory(final ConversationManager conversationManager, final FlowExecutionSnapshotFactory snapshotFactory) {
        super(conversationManager, snapshotFactory);
        this.conversationManager = conversationManager;
    }

    /**
     * Set to true to enable the default Spring Webflow execution key format.
     * <p>
     * <strong>NOTE:</strong>
     * Setting to true is technically a violation of the CAS protocol since the
     * LT is required to have randomness that the default SWF key format lacks.
     *
     * @param defaultBehavior True to enable default behavior, false otherwise.
     * Default is false.
     */
    public void setDefaultBehavior(final boolean defaultBehavior) {
        this.defaultBehavior = defaultBehavior;
    }

    public FlowExecutionKey getKey(final FlowExecution execution) {
        if (this.defaultBehavior) {
            return super.getKey(execution);
        }

    CasFlowExecutionKey key = (CasFlowExecutionKey) execution.getKey();
    if (key == null) {
          final Conversation conversation = this.conversationManager.beginConversation(
              createConversationParameters(execution));
      final ConversationId executionId = conversation.getId();
            key = newKey(conversation, executionId);
    } else {
      if (getAlwaysGenerateNewNextKey()) {
                final Serializable executionId = key.getExecutionId();
                key = newKey(getConversation(executionId), executionId);
      }
    }
    return key;
    }

  public FlowExecutionKey parseFlowExecutionKey(final String encodedKey) throws FlowExecutionRepositoryException {
        if (this.defaultBehavior) {
            return super.parseFlowExecutionKey(encodedKey);
        }

    if (!StringUtils.hasText(encodedKey)) {
      throw new IllegalStateException("The string-encoded flow execution key is required");
    }
    final String[] keyParts = CasFlowExecutionKey.keyParts(encodedKey);
    final UUID uuid;
    try {
        uuid = UUID.fromString(keyParts[0]);
    } catch (Exception e) {
      throw new BadlyFormattedFlowExecutionKeyException(encodedKey, CasFlowExecutionKey.FORMAT);
    }
    Serializable executionId = parseExecutionId(keyParts[1], encodedKey);
    Serializable snapshotId = parseSnapshotId(keyParts[2], encodedKey);
    validateUUID(uuid, executionId);
    return new CasFlowExecutionKey(executionId, snapshotId, uuid);
  }

  private ConversationId parseExecutionId(final String encodedId, final String encodedKey) throws BadlyFormattedFlowExecutionKeyException {
    try {
      return this.conversationManager.parseConversationId(encodedId);
    } catch (ConversationException e) {
      throw new BadlyFormattedFlowExecutionKeyException(encodedKey, CompositeFlowExecutionKey.getFormat(), e);
    }
  }

  private Serializable parseSnapshotId(final String encodedId, final String encodedKey) throws BadlyFormattedFlowExecutionKeyException {
    try {
      return Integer.valueOf(encodedId);
    } catch (NumberFormatException e) {
      throw new BadlyFormattedFlowExecutionKeyException(encodedKey, CompositeFlowExecutionKey.getFormat(), e);
    }
  }
 
  private CasFlowExecutionKey newKey(final Conversation conversation, final Serializable executionId) {
      // Include time component in UUID to prevent successive dupes
      // Use ~24h of millis so we have more bytes for random data
      final long hi_word = (long) this.secureRandom.nextInt() << 32;
      final long lo_word = System.currentTimeMillis() & 0xFFFFFFFF;
      final UUID uuid = new UUID(hi_word | lo_word, this.secureRandom.nextLong());
      conversation.lock();
      try {
          conversation.putAttribute(UUID_KEY, uuid);
      } finally {
          conversation.unlock();
      }
      return new CasFlowExecutionKey(executionId, nextSnapshotId(executionId), uuid);
  }
 
  private void validateUUID(final UUID givenUUID, final Serializable executionId) {
      final Conversation conversation = getConversation(executionId);
      conversation.lock();
      try {
          final UUID savedUUID = (UUID) conversation.getAttribute(UUID_KEY);
          if (!givenUUID.equals(savedUUID)) {
              throw new IllegalStateException("UUID component of flow execution key not recognized.");
          }
      } finally {
          conversation.unlock();
      }
  }

}
TOP

Related Classes of org.jasig.cas.web.flow.CasFlowExecutionKeyFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.