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.renderkit.html.HtmlAttributes; 022 import org.apache.myfaces.tobago.renderkit.html.HtmlConstants; 023 024 import javax.faces.context.FacesContext; 025 import javax.servlet.http.HttpSession; 026 import java.io.IOException; 027 import java.io.Serializable; 028 import java.security.SecureRandom; 029 import java.util.Map; 030 031 public class Secret implements Serializable { 032 033 private static final long serialVersionUID = 1L; 034 035 private static final String KEY = Secret.class.getName(); 036 037 private static final SecureRandom RANDOM = new SecureRandom(); 038 039 private static final int SECRET_LENGTH = 16; 040 041 private static final boolean COMMONS_CODEC_AVAILABLE = commonsCodecAvailable(); 042 043 private static boolean commonsCodecAvailable() { 044 try { 045 Base64.encodeBase64URLSafeString(new byte[0]); 046 return true; 047 } catch (Error e) { 048 return false; 049 } 050 } 051 052 private String secret; 053 054 private Secret() { 055 byte[] bytes = new byte[SECRET_LENGTH]; 056 RANDOM.nextBytes(bytes); 057 secret = COMMONS_CODEC_AVAILABLE ? encodeBase64(bytes) : encodeHex(bytes); 058 } 059 060 private String encodeBase64(byte[] bytes) { 061 return Base64.encodeBase64URLSafeString(bytes); 062 } 063 064 private String encodeHex(byte[] bytes) { 065 StringBuilder builder = new StringBuilder(SECRET_LENGTH * 2); 066 for (byte b : bytes) { 067 builder.append(String.format("%02x", b)); 068 } 069 return builder.toString(); 070 } 071 072 /** 073 * Checks that the request contains a parameter {@link org.apache.myfaces.tobago.webapp.Secret#KEY} 074 * which is equals to a secret value in the session. 075 */ 076 public static boolean check(FacesContext facesContext) { 077 Map requestParameterMap = facesContext.getExternalContext().getRequestParameterMap(); 078 String fromRequest = (String) requestParameterMap.get(Secret.KEY); 079 Map sessionMap = facesContext.getExternalContext().getSessionMap(); 080 Secret secret = (Secret) sessionMap.get(Secret.KEY); 081 return secret != null && secret.secret.equals(fromRequest); 082 } 083 084 /** 085 * Encode a hidden field with the secret value from the session. 086 */ 087 public static void encode(FacesContext facesContext, TobagoResponseWriter writer) throws IOException { 088 writer.startElement(HtmlConstants.INPUT, null); 089 writer.writeAttribute(HtmlAttributes.TYPE, "hidden", false); 090 writer.writeAttribute(HtmlAttributes.NAME, Secret.KEY, false); 091 writer.writeAttribute(HtmlAttributes.ID, Secret.KEY, false); 092 Map sessionMap = facesContext.getExternalContext().getSessionMap(); 093 Secret secret = (Secret) sessionMap.get(Secret.class.getName()); 094 writer.writeAttribute(HtmlAttributes.VALUE, secret.secret, false); 095 writer.endElement(HtmlConstants.INPUT); 096 } 097 098 /** 099 * Create a secret attribute in the session. 100 * Should usually be called in a {@link javax.servlet.http.HttpSessionListener}. 101 */ 102 public static void create(HttpSession session) { 103 session.setAttribute(Secret.KEY, new Secret()); 104 } 105 }