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 }