001: /*
002: Copyright (C) 2003 Know Gate S.L. All rights reserved.
003: C/Oņa, 107 1š2 28050 Madrid (Spain)
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions
007: are met:
008:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011:
012: 2. The end-user documentation included with the redistribution,
013: if any, must include the following acknowledgment:
014: "This product includes software parts from hipergate
015: (http://www.hipergate.org/)."
016: Alternately, this acknowledgment may appear in the software itself,
017: if and wherever such third-party acknowledgments normally appear.
018:
019: 3. The name hipergate must not be used to endorse or promote products
020: derived from this software without prior written permission.
021: Products derived from this software may not be called hipergate,
022: nor may hipergate appear in their name, without prior written
023: permission.
024:
025: This library is distributed in the hope that it will be useful,
026: but WITHOUT ANY WARRANTY; without even the implied warranty of
027: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
028:
029: You should have received a copy of hipergate License with this code;
030: if not, visit http://www.hipergate.org or mail to info@hipergate.org
031: */
032:
033: package com.knowgate.acl;
034:
035: import java.sql.SQLException;
036: import java.sql.CallableStatement;
037: import java.sql.Statement;
038: import java.sql.PreparedStatement;
039: import java.sql.ResultSet;
040:
041: import java.util.Date;
042:
043: import com.knowgate.debug.DebugFile;
044: import com.knowgate.jdc.JDCConnection;
045: import com.knowgate.misc.MD5;
046:
047: /**
048: *
049: * <p>Top Level User Authentication and Access Control List Functions.</p>
050: * @author Sergio Montoro Ten
051: * @version 3.0
052: */
053: public final class ACL {
054:
055: /**
056: * Default Constructor
057: */
058: public ACL() {
059: }
060:
061: /**
062: * <p>Checks whether or not password is valid for given user.</p>
063: * <p>This method calls k_sp_autenticate stored procedure witch looks up tx_pwd field at k_users table and see if it is the same as sAuthStr parameter.</p>
064: * @param oConn Opened Database Connection
065: * @param sUserId User nickname
066: * @param sAuthStr Authentication String (password)
067: * @param iFlags Authentication String Flags
068: * <ul>
069: * <li>ACL.PWD_CLEAR_TEXT Authentication String is passed as clear text (no encryption)
070: * <li>ACL.PWD_DTIP_RC4 Authentication String is given encrypted using RC4 algorithm
071: * </ul>
072: * @return
073: * <ul>
074: * <li>ACL.USER_NOT_FOUND sUserId not found at gu_user field from k_users table
075: * <li>ACL.INVALID_PASSWORD sAuthStr parameter does not match tx_pwd field from k_users for sUserId
076: * <li>ACL.PASSWORD_EXPIRED Password has expired (dt_pwd_expires field value is before current date)
077: * <li>ACL.ACCOUNT_DEACTIVATED User account as been deactivated (field bo_active from k_users table set to zero)
078: * <li>ACL.ACCOUNT_CANCELLED User account as been cancelled (field dt_cancel from k_users table set to date before now)
079: * <li>ACL.INTERNAL_ERROR Internal error while trying to autenticate user
080: * </ul>
081: * @throws SQLException
082: * @throws UnsupportedOperationException If k_sp_autenticate stored procedure is not found
083: */
084:
085: public static short autenticate(JDCConnection oConn,
086: String sUserId, String sAuthStr, int iFlags)
087: throws SQLException, UnsupportedOperationException {
088: short iStatus;
089: CallableStatement oCall;
090: Statement oStmt;
091: ResultSet oRSet;
092: String sPassword;
093:
094: if (DebugFile.trace) {
095: DebugFile.writeln("Begin ACL.autenticate([Connection], "
096: + sUserId + "," + sAuthStr + "," + iFlags + ")");
097: DebugFile.incIdent();
098: }
099:
100: sPassword = encript(sAuthStr, iFlags);
101:
102: switch (oConn.getDataBaseProduct()) {
103:
104: case JDCConnection.DBMS_ORACLE:
105:
106: if (DebugFile.trace)
107: DebugFile
108: .writeln(" Connection.prepareCall({ call k_sp_autenticate ("
109: + sUserId + "," + sPassword + ",?)})");
110:
111: oCall = oConn
112: .prepareCall("{ call k_sp_autenticate (?,?,?)}");
113:
114: try {
115: oCall.setQueryTimeout(20);
116: } catch (SQLException sqle) {
117: }
118:
119: oCall.setString(1, sUserId);
120: oCall.setString(2, sPassword);
121: oCall.registerOutParameter(3, java.sql.Types.DECIMAL);
122:
123: if (DebugFile.trace)
124: DebugFile.writeln(" java.sql.Connection.execute()");
125:
126: oCall.execute();
127: iStatus = Short.parseShort(oCall.getBigDecimal(3)
128: .toString());
129: oCall.close();
130: break;
131:
132: case JDCConnection.DBMS_MSSQL:
133: case JDCConnection.DBMS_MYSQL:
134:
135: if (DebugFile.trace)
136: DebugFile
137: .writeln(" Connection.prepareCall({ call k_sp_autenticate ("
138: + sUserId + "," + sPassword + ",?)})");
139:
140: oCall = oConn
141: .prepareCall("{ call k_sp_autenticate (?,?,?)}");
142:
143: try {
144: oCall.setQueryTimeout(20);
145: } catch (SQLException sqle) {
146: }
147:
148: oCall.setString(1, sUserId);
149: oCall.setString(2, sPassword);
150: oCall.registerOutParameter(3, java.sql.Types.SMALLINT);
151:
152: if (DebugFile.trace)
153: DebugFile.writeln(" java.sql.Connection.execute()");
154:
155: oCall.execute();
156: iStatus = oCall.getShort(3);
157: oCall.close();
158: break;
159:
160: case JDCConnection.DBMS_POSTGRESQL:
161: oStmt = oConn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
162: ResultSet.CONCUR_READ_ONLY);
163:
164: if (DebugFile.trace)
165: DebugFile
166: .writeln(" Statement.executeQuery(SELECT k_sp_autenticate('"
167: + sUserId
168: + "', '"
169: + sPassword
170: + "', ...))");
171:
172: oRSet = oStmt.executeQuery("SELECT k_sp_autenticate('"
173: + sUserId + "','" + sPassword + "')");
174: oRSet.next();
175: iStatus = oRSet.getShort(1);
176: oRSet.close();
177: oStmt.close();
178: break;
179:
180: default:
181: throw new UnsupportedOperationException(
182: "k_sp_autenticate procedure not found");
183: }
184:
185: if (DebugFile.trace) {
186: DebugFile.decIdent();
187: DebugFile.writeln("End ACL.autenticate() : " + iStatus);
188: }
189:
190: return iStatus;
191: } // autenticate
192:
193: /**
194: * <p>Checks password and captcha for a given user </p>
195: * <p>This method </p>
196: * @param oConn Opened Database Connection
197: * @param sUserId User nickname
198: * @param sAuthStr Authentication String (password)
199: * @param iFlags Authentication String Flags
200: * @param lTimestamp Timestamp (in miliseconds) when sPlainCaptcha was generated
201: * @param lTimemout Number of miliseconds after which sPlainCaptcha expires
202: * @param sPlainCaptcha Captcha plain text
203: * @param sTimeCaptchaMD5 Precomputed MD5 hash for String sPlainCaptcha+lTimestamp
204: * @return This method returns the same values as autenticate(JDCConnection,String,String,int) and also
205: * <ul>
206: * <li>ACL.CAPTCHA_MISMATCH The computed MD5 hash for sPlainCaptcha+lTimestamp does not match sTimeCaptchaMD5
207: * <li>ACL.CAPTCHA_TIMEOUT lTimestamp+lTimeout is before current datetime
208: * </ul>
209: * @throws SQLException
210: * @throws UnsupportedOperationException If k_sp_autenticate stored procedure is not found
211: * @since 2.2
212: */
213:
214: public static short autenticate(JDCConnection oConn,
215: String sUserId, String sAuthStr, int iFlags,
216: long lTimestamp, long lTimeout, String sPlainCaptcha,
217: String sTimeCaptchaMD5) throws SQLException,
218: UnsupportedOperationException {
219: short iRetVal = autenticate(oConn, sUserId, sAuthStr, iFlags);
220: if (iRetVal >= (short) 0) {
221: long lNow = new Date().getTime();
222: if (lTimestamp + lTimeout < lNow) {
223: iRetVal = CAPTCHA_TIMEOUT;
224: } else {
225: MD5 oCaptchaMd5 = new MD5(sPlainCaptcha
226: + String.valueOf(lTimestamp));
227: if (!sTimeCaptchaMD5.equalsIgnoreCase(oCaptchaMd5
228: .asHex()))
229: iRetVal = CAPTCHA_MISMATCH;
230: } // fi (lTimestamp+lCaptchaTimeout<lNow)
231: }
232: return iRetVal;
233: } // autenticate
234:
235: /**
236: * <p>Encrypt String</p>
237: * @param sStr String to be encrypted
238: * @param iFlags Encryption flags
239: * <ul>
240: * <li>ACL.PWD_CLEAR_TEXT Do not encrypt sStr (return as it is given)
241: * <li>ACL.PWD_DTIP_RC4 Encrypt using RC4 algorithm
242: * </ul>
243: * @return Encrypted string
244: * @throws NullPointerException if sStr is <b>null</b>
245: * @throws IllegalArgumentException if iFlags!=PWD_CLEAR_TEXT AND iFlags!=PWD_DTIP_RC4
246: */
247:
248: public static String encript(String sStr, int iFlags)
249: throws IllegalArgumentException, NullPointerException {
250:
251: String sEncrypted;
252:
253: if (iFlags != PWD_CLEAR_TEXT && iFlags != PWD_DTIP_RC4)
254: throw new IllegalArgumentException(
255: "ACL.encript() encryption algorithm must be either PWD_CLEAR_TEXT or PWD_DTIP_RC4");
256:
257: if (DebugFile.trace) {
258: DebugFile.writeln("Begin ACL.encript(" + sStr + ","
259: + String.valueOf(iFlags) + ")");
260: DebugFile.incIdent();
261: }
262:
263: if ((iFlags & ACL.PWD_DTIP_RC4) != 0)
264: sEncrypted = RC4EnDeCrypt(sStr, RC4PWD);
265: else
266: sEncrypted = sStr;
267:
268: if (DebugFile.trace) {
269: DebugFile.decIdent();
270: DebugFile.writeln("End ACL.encript() : " + sEncrypted);
271: }
272:
273: return sEncrypted;
274: } // encript
275:
276: /**
277: * <p>Get user unique id given its nickname.</p>
278: * <p>Calls k_get_user_from_nick stored procedure and gets gu_user field from tx_nickname field</p>
279: * @param oConn Database Connection
280: * @param sNickName User nickname (tx_nickname from k_users table)
281: * @param iDomain Domain Identifier (id_domain from k_users table)
282: * @return User Unique Identifier (gu_user from k_users table)
283: * @throws SQLException
284: */
285: public static String getUserIdFromNick(JDCConnection oConn,
286: String sNickName, int iDomain) throws SQLException {
287: String sUserId;
288:
289: if (oConn.getDataBaseProduct() == JDCConnection.DBMS_POSTGRESQL) {
290: PreparedStatement oStmt = oConn
291: .prepareStatement(
292: "SELECT gu_user FROM k_users WHERE id_domain=? AND tx_nickname=?",
293: ResultSet.TYPE_FORWARD_ONLY,
294: ResultSet.CONCUR_READ_ONLY);
295: oStmt.setInt(1, iDomain);
296: oStmt.setString(2, sNickName);
297: ResultSet oRSet = oStmt.executeQuery();
298: if (oRSet.next())
299: sUserId = oRSet.getString(1);
300: else
301: sUserId = null;
302: oRSet.close();
303: oStmt.close();
304: } else {
305: CallableStatement oCall = oConn
306: .prepareCall("{ call k_get_user_from_nick (?,?,?)}");
307:
308: oCall.setInt(1, iDomain);
309: oCall.setString(2, sNickName);
310: oCall.registerOutParameter(3, java.sql.Types.CHAR);
311:
312: oCall.execute();
313:
314: if (JDCConnection.DBMS_ORACLE == oConn.getDataBaseProduct()) {
315: sUserId = oCall.getString(3);
316: if (null != sUserId)
317: sUserId = sUserId.trim();
318: } else
319: sUserId = oCall.getString(3);
320:
321: oCall.close();
322: }
323:
324: return sUserId;
325: } // getUserIdFromNick
326:
327: // ---------------------------------------------------------------------------
328:
329: private static void RC4Init(String sPwd, char cKey[], int byBox[])
330: throws NullPointerException {
331: int iPwdLen = sPwd.length();
332: int a, b, t;
333:
334: for (a = 0; a < 256; a++) {
335: cKey[a] = sPwd.charAt((a % iPwdLen));
336: byBox[a] = a;
337: }
338:
339: for (a = 0, b = 0; a < 256; a++) {
340: b = (b + byBox[a] + cKey[a]) % 256;
341: t = byBox[a];
342: byBox[a] = byBox[b];
343: byBox[b] = t;
344: }
345: }
346:
347: /**
348: * Get RC4 default key for encryption
349: */
350: public static String getRC4key() {
351: return RC4PWD;
352: }
353:
354: /**
355: * Set RC4 default key for encryption
356: * @param sKey
357: */
358: public static void setRC4key(String sKey) {
359: RC4PWD = sKey;
360: }
361:
362: /**
363: * <p>Encrypt text using RC4 algorithm and a default encryption key</p>
364: * @param sTxt Text to be encrypted
365: * @return String Encrypted text
366: * @throws NullPointerException if sTxt is null
367: * @see {@link http://www.4guysfromrolla.com/webtech/010100-1.shtml}
368: */
369:
370: public static String RC4EnDeCrypt(String sTxt)
371: throws NullPointerException {
372: return RC4EnDeCrypt(sTxt, RC4PWD);
373: }
374:
375: /**
376: * <p>Encrypt text using RC4 algorithm</p>
377: * @param sTxt Text to be encrypted
378: * @param sKey Encryption key
379: * @see {@link http://www.4guysfromrolla.com/webtech/010100-1.shtml}
380: */
381: public static String RC4EnDeCrypt(String sTxt, String sKey) {
382:
383: int iTxtLen = sTxt.length();
384: int i = 0, j = 0;
385: char cKey[] = new char[256];
386: int byBox[] = new int[256];
387: char byCipher[] = new char[iTxtLen];
388: int t;
389: short k;
390:
391: RC4Init(sKey, cKey, byBox);
392:
393: for (int a = 0; a < iTxtLen; a++) {
394: i = (i + 1) % 256;
395: j = (j + byBox[i]) % 256;
396: t = byBox[i];
397: byBox[i] = byBox[j];
398: byBox[j] = t;
399: k = (short) byBox[(byBox[i] + byBox[j]) % 256];
400:
401: byCipher[a] = (char) (((short) sTxt.charAt(a)) ^ k);
402: } // next
403:
404: return new String(byCipher);
405: } // RC4EnDeCrypt
406:
407: /**
408: * <p>Gets permissions mask descriptive name for given language</p>
409: * @param iACLMask Permissions Mask, any combination of ACL.PERMISSION_ constants
410: * @param sLanguage Language for localized string {"en", "es"}
411: * @return
412: */
413: public static String getLocalizedMaskName(int iACLMask,
414: String sLanguage) throws IllegalArgumentException {
415: int iName;
416: String es[] = { "Desconocido", "Listar", "Leer", "Aņadir",
417: "Aņadir y Leer", "Moderar", "Modificar",
418: "Control Total" };
419: String en[] = { "Unknown", "List", "Read", "Add", "Add & Read",
420: "Moderate", "Modify", "Full Control" };
421:
422: if (PERMISSION_LIST == iACLMask)
423: iName = 1;
424: else if (PERMISSION_READ == iACLMask
425: || (PERMISSION_LIST | PERMISSION_READ) == iACLMask)
426: iName = 2;
427: else if (PERMISSION_ADD == iACLMask
428: || (PERMISSION_LIST | PERMISSION_ADD) == iACLMask)
429: iName = 3;
430: else if ((PERMISSION_ADD | PERMISSION_READ) == iACLMask
431: || (PERMISSION_LIST | PERMISSION_ADD | PERMISSION_READ) == iACLMask)
432: iName = 4;
433: else if ((PERMISSION_MODERATE) == iACLMask
434: || (PERMISSION_READ | PERMISSION_MODERATE) == iACLMask
435: || (PERMISSION_LIST | PERMISSION_READ | PERMISSION_MODERATE) == iACLMask
436: || (PERMISSION_LIST | PERMISSION_READ | PERMISSION_ADD | PERMISSION_MODERATE) == iACLMask)
437: iName = 5;
438: else if ((PERMISSION_MODIFY & iACLMask) != 0
439: && iACLMask != 2147483647)
440: iName = 6;
441: else if (iACLMask >= 255)
442: iName = 7;
443: else
444: iName = 0;
445:
446: if (sLanguage.compareToIgnoreCase("es") == 0)
447: return es[iName];
448: else if (sLanguage.compareToIgnoreCase("en") == 0)
449: return en[iName];
450: else
451: throw new IllegalArgumentException(
452: "language must \"be\" en or \"es\"");
453: } // getLocalizedMaskName
454:
455: // ---------------------------------------------------------------------------
456:
457: public static String getErrorMessage(short iErrCode) {
458: if (iErrCode < 0) {
459: switch (iErrCode) {
460: case USER_NOT_FOUND:
461: return "User not found";
462: case INVALID_PASSWORD:
463: return "Invalid password";
464: case ACCOUNT_DEACTIVATED:
465: return "User not found";
466: case SESSION_EXPIRED:
467: return "Session expired";
468: case DOMAIN_NOT_FOUND:
469: return "Domain not found";
470: case WORKAREA_NOT_FOUND:
471: return "WorkArea not found";
472: case WORKAREA_NOT_SET:
473: return "WorkArea not set";
474: case ACCOUNT_CANCELLED:
475: return "Account cancelled";
476: case PASSWORD_EXPIRED:
477: return "Password expired";
478: case CAPTCHA_MISMATCH:
479: return "Captcha mismatch";
480: case CAPTCHA_TIMEOUT:
481: return "Captcha mismatch";
482: case INTERNAL_ERROR:
483: return "Internal error";
484: default:
485: return "Undefined error";
486: }
487: } else
488: return "";
489: } // getErrorMessage
490:
491: // ---------------------------------------------------------------------------
492: // just a random string for RC4 algorithm, change for your own implementation
493: private static String RC4PWD = "LindtExcellence%70Degustation";
494:
495: public static final int PERMISSION_LIST = 1;
496: public static final int PERMISSION_READ = 2;
497: public static final int PERMISSION_ADD = 4;
498: public static final int PERMISSION_DELETE = 8;
499: public static final int PERMISSION_MODIFY = 16;
500: public static final int PERMISSION_MODERATE = 32;
501: public static final int PERMISSION_SEND = 64;
502: public static final int PERMISSION_GRANT = 128;
503: public static final int PERMISSION_FULL_CONTROL = 2147483647;
504:
505: public static final int ROLE_NONE = 0;
506: public static final int ROLE_ADMIN = 1;
507: public static final int ROLE_POWERUSER = 2;
508: public static final int ROLE_USER = 4;
509: public static final int ROLE_GUEST = 4;
510:
511: public static final int PWD_CLEAR_TEXT = 0;
512: public static final int PWD_DTIP_RC4 = 1;
513:
514: public static final short USER_NOT_FOUND = -1;
515: public static final short INVALID_PASSWORD = -2;
516: public static final short ACCOUNT_DEACTIVATED = -3;
517: public static final short SESSION_EXPIRED = -4;
518: public static final short DOMAIN_NOT_FOUND = -5;
519: public static final short WORKAREA_NOT_FOUND = -6;
520: public static final short WORKAREA_NOT_SET = -7;
521: public static final short ACCOUNT_CANCELLED = -8;
522: public static final short PASSWORD_EXPIRED = -9;
523: public static final short CAPTCHA_MISMATCH = -10;
524: public static final short CAPTCHA_TIMEOUT = -11;
525: public static final short INTERNAL_ERROR = -255;
526:
527: } // ACL
|