All Downloads are FREE. Search and download functionalities are using the official Maven repository.

play.mvc.FlashStore Maven / Gradle / Ivy

There is a newer version: 2.6.2
Show newest version
package play.mvc;

import static play.mvc.Scope.COOKIE_PREFIX;
import static play.mvc.Scope.COOKIE_SECURE;
import static play.mvc.Scope.SESSION_HTTPONLY;

import jakarta.inject.Singleton;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.exceptions.UnexpectedException;
import play.libs.Signer;
import play.mvc.results.Forbidden;

@Singleton
public class FlashStore {
  private static final Logger logger = LoggerFactory.getLogger(FlashStore.class);

  private final SessionDataEncoder encoder = new SessionDataEncoder();
  private final Signer signer;

  public FlashStore() {
    this(new Signer("флэшрояль"));
  }

  FlashStore(@Nonnull Signer signer) {
    this.signer = signer;
  }

  @Nonnull
  public Scope.Flash restore(@Nonnull Http.Request request) {
    Http.Cookie cookie = request.cookies.get(COOKIE_PREFIX + "_FLASH");
    if (cookie == null) return new Scope.Flash();

    int splitterPosition = cookie.value.indexOf('-');
    if (splitterPosition == -1) {
      logger.warn("Flash cookie without signature: '{}'", cookie.value);
      throw new Forbidden("Flash cookie without signature");
    }

    String signature = cookie.value.substring(0, splitterPosition);
    String base64EncodedContent = cookie.value.substring(splitterPosition + 1);
    if (!signer.isValid(signature, base64EncodedContent)) {
      logger.warn(
          "Invalid flash cookie signature on {} (decoded content: '{}')",
          cookie.value,
          encoder.decode(base64EncodedContent));
      throw new Forbidden("Invalid flash cookie signature");
    }
    return new Scope.Flash(encoder.decode(base64EncodedContent));
  }

  public void save(
      @Nonnull Scope.Flash flash, @Nonnull Http.Request request, @Nullable Http.Response response) {
    if (response == null) {
      // Some requests like WebSocket requests don't have a response
      return;
    }

    warnIfFlashIsGone(flash, request);

    if (flash.out.isEmpty()) {
      if (request.cookies.containsKey(COOKIE_PREFIX + "_FLASH")) {
        response.setCookie(
            COOKIE_PREFIX + "_FLASH", "", null, "/", 0, COOKIE_SECURE, SESSION_HTTPONLY);
      }
      return;
    }
    try {
      String flashData = encoder.encode(flash.out);
      int maximumAcceptableCookieLength = 3980;
      if (flashData.length() > maximumAcceptableCookieLength) {
        logger.error("Too long flash ({}): {}", flashData.length(), flashData);
      } else {
        int recommendedMaximumCookieLength = 2000;
        if (flashData.length() > recommendedMaximumCookieLength) {
          logger.warn(
              "Flash size {} bytes, recommending to redesign the page {} to avoid overusing of flash. Flash content: {}",
              flashData.length(),
              request.path,
              flash.out);
        }
      }
      String signature = signer.sign(flashData);
      response.setCookie(
          COOKIE_PREFIX + "_FLASH",
          signature + '-' + flashData,
          null,
          "/",
          null,
          COOKIE_SECURE,
          SESSION_HTTPONLY);
    } catch (Exception e) {
      throw new UnexpectedException("Flash serializationProblem", e);
    }
  }

  private void warnIfFlashIsGone(@Nonnull Scope.Flash flash, @Nonnull Http.Request request) {
    for (Map.Entry entry : flash.data.entrySet()) {
      if (!flash.out.containsKey(entry.getKey()) && !flash.used.contains(entry.getKey())) {
        logger.debug(
            "Unused flash param: {}={} in request {}",
            entry.getKey(),
            entry.getValue(),
            request.path);
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy