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    }