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.resource;
017
018import java.util.Locale;
019import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
020import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
021
022import org.opengion.fukurou.db.DBUtil;
023import org.opengion.fukurou.db.ApplicationInfo;
024import org.opengion.fukurou.util.StringUtil;
025import org.opengion.hayabusa.common.HybsSystem;
026import static org.opengion.fukurou.system.HybsConst.CR ;                        // 6.1.0.0 (2014/12/26)
027import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
028
029/**
030 * データロールは、データへのアクセス権限を管理するクラスです。
031 *
032 * データロール情報は、データロールマスタ(GEA06)で管理されます。
033 *
034 * あるユーザーのデータロール情報に対してひもつくデータロールマスタの
035 * カラム、条件値、条件式の一覧に対して、カラム単位に条件式を構築します。
036 *
037 * このクラスでは、インスタンス作成時にデータロールマスタのDBを直接検索しています。
038 * このため、データ変更時の変更内容を次回ログイン時から反映させるため、
039 * 自身のオブジェクトキャッシュは保持していません。
040 *
041 * また、各条件式について、1つのカラムに対して複数の条件式が適用される場合、
042 * 条件式が"="または"LIKE"だけの場合は、"OR"結合されます。
043 * "!="及び"NOT LIKE"条件が1つでも含まれる場合は、"AND"結合されます。
044 *
045 * データロールがNULLの場合、全データへのアクセス可能となり、条件式としては、
046 *  "LIKE '%'" が付加されます。
047 * また、'--'の場合、全データへのアクセスが不可能となり、条件式としては、
048 *  "NOT LIKE '%'" が付加されます。
049 * ユーザーのデータロールが指定されているにも関わらず、データロールの検索ができない
050 * 場合は、全禁止になります。
051 *
052 * 検索条件を取得する際に、テーブル名又は、テーブル名の別名が指定された場合、
053 * 条件の取得する際には、テーブル名は無視されますが、返される条件には、テーブル名
054 * は付加された状態になります。
055 *
056 * 例)
057 *  ABC(=)              BCD(=)          ⇒ (CLM = 'ABC' or CLM = 'BCD' )
058 *  ABC(=)              BCD(LIKE)       ⇒ (CLM = 'ABC' or CLM like 'BCD%' )
059 *  ABC(=)              BCD(!=)         ⇒ (CLM = 'ABC' and CLM != 'BCD' )
060 *  ABC(LIKE)   BCD(LIKE)       ⇒ (CLM like 'ABC%' or CLM like 'BCD%' )
061 *  ABC(LIKE)   BCD(!=)         ⇒ (CLM like 'ABC%' and CLM != 'BCD' )
062 *  ABC(LIKE)   BCD(!=)         ⇒ (CLM != 'ABC' and CLM != 'BCD' )
063 *  ABC(=)              BCD(=)          ⇒ (A.CLM = 'ABC' or A.CLM = 'BCD' ) ※ {@SEC.A.CLM}でアクセス
064 *
065 * @og.rev 4.4.0.0 (2009/08/02) 新規作成
066 * @og.group リソース管理
067 *
068 * @version  4.0
069 * @author   Hiroki Nakamura
070 * @since    JDK5.0,
071 */
072public final class DataRole {
073        private static final String QUERY_GEA06_SELECT
074                                        = "select CLM, CVALUE, VALCDTN from GEA06"
075                                                + " where SYSTEM_ID = ? and DROLE = ? and FGJ = '1'";
076
077        private static final int IDX_CLM        = 0;
078        private static final int IDX_CVALUE     = 1;
079        private static final int IDX_VALCDTN= 2;
080
081        private static final DataRole FULL_ACCESS_DATAROLE_OBJ = new DataRole( true ) ;
082        private static final DataRole FULL_DENY_DATAROLE_OBJ = new DataRole( false ) ;
083
084        private static final String FULL_ACCESS_CONDITION = null ;
085        private static final String FULL_DENY_CONDITION = " NOT LIKE '%'" ;
086        private static final String FULL_DENY_DROLES_KEY = "--";
087
088        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
089        private final ConcurrentMap<String,String> cdtnMap = new ConcurrentHashMap<>( BUFFER_MIDDLE );
090
091        private final String                    droles ;                // データロールズ
092        private final String                    systemId ;              // システムID
093        private final ApplicationInfo   appInfo ;
094        private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );          // 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応
095
096        private boolean         isFullAcess             ;
097        private boolean         isFullDeny              ;
098
099        /**
100         * 固定データロール(全アクセス可 or 全アクセス不可)のインスタンスを生成します。
101         *
102         * @param isFull true:全アクセス可 false:全アクセス不可
103         */
104        private DataRole( final boolean isFull ) {
105                droles = null;
106                systemId = null;
107                appInfo = null;
108
109                if( isFull )    { isFullAcess = true; }
110                else                    { isFullDeny = true; }
111        }
112
113        /**
114         * ロール文字列から、データロールマスタ(GEA05)を検索し、カラム単位の
115         * 条件式を生成します。
116         *
117         * @param droles "|"で区切られた データロール文字列
118         * @param systemId システムID
119         * @param appInfo 接続情報
120         */
121        private DataRole( final String droles,final String systemId, final ApplicationInfo appInfo ) {
122                this.droles = droles ;          // データロールズ
123                this.systemId = systemId;       // システムID
124                this.appInfo = appInfo ;
125
126                if( appInfo != null ) {
127                        appInfo.setModuleInfo( "DataRole",null,"CreateInstance" );
128                }
129
130                final String[] drole = StringUtil.csv2Array( droles, '|' );
131                if( drole == null || drole.length == 0 ) {
132                        isFullAcess = true;
133                        return;
134                }
135                else {
136                        makeConditionMap( drole );
137                }
138        }
139
140        /**
141         * ロール文字列から、データロールマスタ(GEA05)を検索し、カラム単位の
142         * 条件式を生成します。
143         *
144         * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
145         * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加
146         *
147         * @param drole データロール文字列の配列(可変長引数)
148         */
149        private void makeConditionMap( final String... drole ) {
150                String clm     = null;
151                String cvalue  = null;
152                String valcdtn = null;
153
154                for( int i=0; i<drole.length; i++ ) {
155                        final String[] args = new String[] { systemId, drole[i] };
156                        final String[][] vals = DBUtil.dbExecute( QUERY_GEA06_SELECT,args,appInfo, DBID );      // 5.5.5.1 (2012/08/07)
157                        for( int j=0; j<vals.length; j++ ) {
158                                clm     = vals[j][IDX_CLM];
159                                if( clm == null ) { continue; }         // 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限
160
161                                cvalue  = vals[j][IDX_CVALUE];
162                                valcdtn = vals[j][IDX_VALCDTN];
163
164                                String cdtn = cdtnMap.get( clm );
165                                if( cdtn == null ) { cdtn = ""; }
166                                else if( "=".equals( valcdtn ) || "LIKE".equalsIgnoreCase( valcdtn ) ) {
167                                        cdtn += " or ";
168                                }
169                                else {
170                                        cdtn += " and ";
171                                }
172                                cdtn += clm + " " + valcdtn + " '" + cvalue;
173                                if( valcdtn.toUpperCase( Locale.JAPAN ).indexOf( "LIKE" ) >= 0 ) {
174                                        cdtn += "%";
175                                }
176                                cdtn += "'";
177
178                                if( cdtn.indexOf( " and " ) >= 0 ) { cdtn = cdtn.replace( " or ", " and " ); }          // 6.4.2.1 (2016/02/05) よう判らん。
179
180                                cdtnMap.put( clm, cdtn );
181                        }
182                }
183        }
184
185        /**
186         * ロール文字列から、データロールマスタ(GEA05)を検索し、カラム単位の
187         * 条件式を生成します。
188         *
189         * @param       droles  "|"で区切られた データロール文字列
190         * @param systemId システムID
191         * @param appInfo 接続情報
192         *
193         * @return データロールオブジェクト
194         * @og.rtnNotNull
195         */
196        public static DataRole newInstance( final String droles, final String systemId, final ApplicationInfo appInfo ) {
197                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
198                return droles == null || droles.isEmpty()
199                                        ? FULL_ACCESS_DATAROLE_OBJ
200                                        : FULL_DENY_DROLES_KEY.equals( droles )
201                                                ? FULL_DENY_DATAROLE_OBJ
202                                                : new DataRole( droles, systemId, appInfo );
203        }
204
205        /**
206         * ロールズを返します。
207         *
208         * @return ロールズ文字列
209         */
210        public String getDataRoles() { return droles; }
211
212        /**
213         * ロールズを返します。
214         *
215         * @og.rev 4.4.0.1 (2009/08/08) テーブルIDが付加されている場合の条件を追加
216         *
217         * @param clm カラム名
218         *
219         * @return ロールズ文字列
220         */
221        public String getCondition( final String clm ) {
222                if( isFullAcess ) { return FULL_ACCESS_CONDITION; }
223                if( isFullDeny  ) { return "(" + clm + FULL_DENY_CONDITION + ")"; }
224
225                String rtn = null;
226                if( clm.indexOf( '.' ) >= 0 ) {
227                        final String clmTmp = clm.substring( clm.lastIndexOf( '.' ) + 1 );
228                        rtn = cdtnMap.get( clmTmp );
229                        if( rtn == null || rtn.isEmpty() ) {
230                                return "(" + clm + FULL_DENY_CONDITION + ")";
231                        }
232                        return "(" + rtn.replace( clmTmp, clm ) + ")";
233                }
234                else {
235                        rtn = cdtnMap.get( clm );
236                        if( rtn == null || rtn.isEmpty() ) {
237                                return "(" + clm + FULL_DENY_CONDITION + ")";
238                        }
239                        return "(" + rtn + ")";
240                }
241        }
242
243        /** オブジェクトの識別子として,詳細なユーザー情報を返します。
244         *
245         * @return  詳細な画面情報
246         * @og.rtnNotNull
247         */
248        @Override
249        public String toString() {
250                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
251                rtn.append( "droles  : " ).append( droles ).append( CR );
252                return rtn.toString();
253        }
254}