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