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