001 package org.apache.myfaces.tobago.webapp; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import org.apache.commons.codec.binary.Base64; 021 import org.apache.myfaces.tobago.context.TobagoFacesContext; 022 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes; 023 import org.apache.myfaces.tobago.renderkit.html.HtmlElements; 024 025 import javax.faces.context.FacesContext; 026 import javax.servlet.http.HttpSession; 027 import java.io.IOException; 028 import java.io.Serializable; 029 import java.security.SecureRandom; 030 import java.util.Map; 031 032 public class Secret implements Serializable { 033 034 private static final long serialVersionUID = 1L; 035 036 private static final String KEY = Secret.class.getName(); 037 038 private static final SecureRandom RANDOM = new SecureRandom(); 039 040 private static final int SECRET_LENGTH = 16; 041 042 private static final boolean COMMONS_CODEC_AVAILABLE = commonsCodecAvailable(); 043 044 private static boolean commonsCodecAvailable() { 045 try { 046 Base64.encodeBase64URLSafeString(new byte[0]); 047 return true; 048 } catch (Error e) { 049 return false; 050 } 051 } 052 053 private String secret; 054 055 private Secret() { 056 byte[] bytes = new byte[SECRET_LENGTH]; 057 RANDOM.nextBytes(bytes); 058 secret = COMMONS_CODEC_AVAILABLE ? encodeBase64(bytes) : encodeHex(bytes); 059 } 060 061 private String encodeBase64(byte[] bytes) { 062 return Base64.encodeBase64URLSafeString(bytes); 063 } 064 065 private String encodeHex(byte[] bytes) { 066 StringBuilder builder = new StringBuilder(SECRET_LENGTH * 2); 067 for (byte b : bytes) { 068 builder.append(String.format("%02x", b)); 069 } 070 return builder.toString(); 071 } 072 073 /** 074 * Checks that the request contains a parameter {@link org.apache.myfaces.tobago.webapp.Secret#KEY} 075 * which is equals to a secret value in the session. 076 */ 077 public static boolean check(FacesContext facesContext) { 078 Map requestParameterMap = facesContext.getExternalContext().getRequestParameterMap(); 079 String fromRequest = (String) requestParameterMap.get(Secret.KEY); 080 Map sessionMap = facesContext.getExternalContext().getSessionMap(); 081 Secret secret = (Secret) sessionMap.get(Secret.KEY); 082 return secret != null && secret.secret.equals(fromRequest); 083 } 084 085 /** 086 * Encode a hidden field with the secret value from the session. 087 */ 088 public static void encode(TobagoFacesContext facesContext, TobagoResponseWriter writer) throws IOException { 089 writer.startElement(HtmlElements.INPUT, null); 090 writer.writeAttribute(HtmlAttributes.TYPE, "hidden", false); 091 writer.writeAttribute(HtmlAttributes.NAME, Secret.KEY, false); 092 writer.writeAttribute(HtmlAttributes.ID, Secret.KEY, false); 093 Map sessionMap = facesContext.getExternalContext().getSessionMap(); 094 Secret secret = (Secret) sessionMap.get(Secret.class.getName()); 095 writer.writeAttribute(HtmlAttributes.VALUE, secret.secret, false); 096 writer.endElement(HtmlElements.INPUT); 097 } 098 099 /** 100 * Create a secret attribute in the session. 101 * Should usually be called in a {@link javax.servlet.http.HttpSessionListener}. 102 */ 103 public static void create(HttpSession session) { 104 session.setAttribute(Secret.KEY, new Secret()); 105 } 106 }