001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.filter;
017
018import java.io.File;                                                    // 5.7.3.2 (2014/02/28) Tomcat8 対応
019import java.io.BufferedReader;
020import java.io.FileInputStream;
021import java.io.IOException;
022import java.io.InputStreamReader;
023import java.io.PrintWriter;
024import java.io.UnsupportedEncodingException;
025
026import javax.servlet.Filter;
027import javax.servlet.FilterChain;
028import javax.servlet.FilterConfig;
029import javax.servlet.ServletContext;
030import javax.servlet.ServletException;
031import javax.servlet.ServletRequest;
032import javax.servlet.ServletResponse;
033import javax.servlet.http.HttpServletRequest;
034
035import org.opengion.fukurou.security.HybsCryptography;
036import org.opengion.fukurou.util.Closer;
037import org.opengion.fukurou.util.StringUtil;
038import org.opengion.hayabusa.common.HybsSystem;
039
040/**
041 * URLCheckFilter は、Filter インターフェースを継承した URLチェッククラスです。
042 * web.xml で filter 設定することにより、該当のリソースに対して、og:linkタグで、
043 * useURLCheck="true"が指定されたリンクURL以外を拒否することができます。
044 * また、og:linkタグを経由した場合でも、リンクの有効期限を設定することで、
045 * リンクURLの漏洩に対しても、一定時間の経過を持って、アクセスを拒否することができます。
046 * また、リンク時にユーザー情報も埋め込んでいますので(初期値は、ログインユーザー)、
047 * リンクアドレスが他のユーザーに知られた場合でも、アクセスを拒否することができます。
048 *
049 * フィルターに対してweb.xml でパラメータを設定します。
050 *   ・filename :停止時メッセージ表示ファイル名
051 *
052 * 【WEB-INF/web.xml】
053 *     <filter>
054 *         <filter-name>URLCheckFilter</filter-name>
055 *         <filter-class>org.opengion.hayabusa.filter.URLCheckFilter</filter-class>
056 *         <init-param>
057 *             <param-name>filename</param-name>
058 *             <param-value>jsp/custom/refuseAccess.html</param-value>
059 *         </init-param>
060 *     </filter>
061 *
062 *     <filter-mapping>
063 *         <filter-name>URLCheckFilter</filter-name>
064 *         <url-pattern>/jsp/*</url-pattern>
065 *     </filter-mapping>
066 *
067 * @og.group フィルター処理
068 *
069 * @version  4.0
070 * @author   Hiroki Nakamura
071 * @since    JDK5.0,
072 */
073public final class URLCheckFilter implements Filter {
074
075        private static final HybsCryptography HYBS_CRYPTOGRAPHY = new HybsCryptography(); // 4.3.7.0 (2009/06/01)
076
077        private String  filename  = null;                       // アクセス拒否時メッセージ表示ファイル名
078        private boolean  isDebug         = false;
079        private boolean  isDecode        = true;                // 5.4.5.0(2012/02/28) URIDecodeするかどうか
080
081        /**
082         * フィルター処理本体のメソッドです。
083         *
084         * @param       request         ServletRequestオブジェクト
085         * @param       response        ServletResponseオブジェクト
086         * @param       chain           FilterChainオブジェクト
087         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
088         */
089        public void doFilter( final ServletRequest request, final ServletResponse response, final FilterChain chain ) throws IOException, ServletException {
090
091                if( !isValidAccess( request ) ) {
092                        BufferedReader in = null ;
093                        try {
094                                response.setContentType( "text/html; charset=UTF-8" );
095                                PrintWriter out = response.getWriter();
096                                in = new BufferedReader( new InputStreamReader(
097                                                                new FileInputStream( filename ) ,"UTF-8" ) );
098                                String str ;
099                                while( (str = in.readLine()) != null ) {
100                                        out.println( str );
101                                }
102                                out.flush();
103                        }
104                        catch( UnsupportedEncodingException ex ) {
105                                String errMsg = "指定されたエンコーディングがサポートされていません。[UTF-8]" ;
106                                throw new RuntimeException( errMsg,ex );
107                        }
108                        catch( IOException ex ) {
109                                String errMsg = "ストリームがオープン出来ませんでした。[" + filename + "]" ;
110                                throw new RuntimeException( errMsg,ex );
111                        }
112                        finally {
113                                Closer.ioClose( in );
114                        }
115                        return;
116                }
117
118                chain.doFilter(request, response);
119        }
120
121        /**
122         * フィルターの初期処理メソッドです。
123         *
124         * フィルターに対してweb.xml で初期パラメータを設定します。
125         *   ・maxInterval:リンクの有効期限
126         *   ・filename   :停止時メッセージ表示ファイル名
127         *   ・decode     :URLデコードを行ってチェックするか(初期true)
128         *
129         * @og.rev 5.4.5.0 (2102/02/28)
130         * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
131         *
132         * @param filterConfig FilterConfigオブジェクト
133         */
134        public void init(final FilterConfig filterConfig) {
135                ServletContext context = filterConfig.getServletContext();
136                String realPath = context.getRealPath( "" ) + File.separator;           // 5.7.3.2 (2014/02/28) Tomcat8 対応
137
138                filename  = realPath + filterConfig.getInitParameter("filename");
139                isDebug = StringUtil.nval( filterConfig.getInitParameter("debug"), false );
140                isDecode = StringUtil.nval( filterConfig.getInitParameter("decode"), true ); // 5.4.5.0(2012/02/28)
141        }
142
143        /**
144         * フィルターの終了処理メソッドです。
145         *
146         */
147        public void destroy() {
148                // ここでは処理を行いません。
149        }
150
151        /**
152         * フィルターの内部状態をチェックするメソッドです。
153         *
154         * @og.rev 5.4.5.0 (2012/02/28) Decode
155         *
156         * @param request ServletRequestオブジェクト
157         *
158         * @return      (true:許可  false:拒否)
159         */
160        private boolean isValidAccess( final ServletRequest request ) {
161                String checkKey = request.getParameter( HybsSystem.URL_CHECK_KEY );
162                if( checkKey == null || checkKey.length() == 0 ) {
163                        if( isDebug ) {
164                                System.out.println( "  check NG [ No Check Key ]" );
165                        }
166                        return false;
167                }
168
169                boolean rtn = false;
170                try {
171                        checkKey = HYBS_CRYPTOGRAPHY.decrypt( checkKey ).replace( "&", "&" );
172
173                        if( isDebug ) {
174                                System.out.println( "checkKey=" + checkKey );
175                        }
176
177                        String url = checkKey.substring( 0 , checkKey.lastIndexOf( ",time=") );
178                        long time = Long.parseLong( checkKey.substring( checkKey.lastIndexOf( ",time=") + 6, checkKey.lastIndexOf( ",userid=" ) ) );
179                        String userid = checkKey.substring( checkKey.lastIndexOf( ",userid=") + 8 );
180                        // 4.3.8.0 (2009/08/01)
181                        String[] userArr = StringUtil.csv2Array( userid );
182
183                        if( isDebug ) {
184                                System.out.println( " [url]    =" + url );
185                                System.out.println( " [vtime]  =" + time );
186                                System.out.println( " [userid] =" + userid );
187                        }
188
189                        String reqStr =  ((HttpServletRequest)request).getRequestURL().toString() + "?" + ((HttpServletRequest)request).getQueryString();
190                        // 5.4.5.0 (2012/02/28) URLDecodeを行う
191                        if(isDecode){
192                                if( isDebug ) {
193                                        System.out.println( "[BeforeURIDecode]="+reqStr );
194                                }
195                                reqStr = StringUtil.urlDecode( reqStr );
196                        }
197                        reqStr = reqStr.substring( 0, reqStr.lastIndexOf( HybsSystem.URL_CHECK_KEY ) -1 );
198                        //      String reqStr =  ((HttpServletRequest)request).getRequestURL().toString();
199                        String reqUser = ((HttpServletRequest)request).getRemoteUser();
200
201                        if( isDebug ) {
202                                System.out.println( " [reqURL] =" + reqStr );
203                                System.out.println( " [ctime]  =" + System.currentTimeMillis() );
204                                System.out.println( " [reqUser]=" + reqUser );
205                        }
206
207                        if( reqStr.endsWith( url )
208                                        && System.currentTimeMillis() - time < 0
209                                        && userArr != null && userArr.length > 0 ) {
210                                // 4.3.8.0 (2009/08/01)
211                                for( int i=0; i<userArr.length; i++ ) {
212                                        if( "*".equals( userArr[i] ) || reqUser.equals( userArr[i] ) ) {
213                                                rtn = true;
214                                                if( isDebug ) {
215                                                        System.out.println( "  check OK" );
216                                                }
217                                                break;
218                                        }
219                                }
220                        }
221                }
222                catch( RuntimeException ex ) {
223                        if( isDebug ) {
224                                String errMsg = "チェックエラー。 "
225                                                        + " checkKey=" + checkKey
226                                                        + " " + ex.getMessage();                        // 5.1.8.0 (2010/07/01) errMsg 修正
227                                System.out.println( errMsg );
228                                ex.printStackTrace();
229                        }
230                        rtn = false;
231                }
232                return rtn;
233        }
234
235        /**
236         * 内部状態を文字列で返します。
237         *
238         * @return      このクラスの文字列表示
239         */
240        @Override
241        public String toString() {
242                StringBuilder sb = new StringBuilder();
243                sb.append( "UrlCheckFilter" );
244                sb.append( "[" ).append( filename  ).append( "],");
245                return sb.toString();
246        }
247}