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}