Merge with aa6c509f154d3cfec350ea4cf1803ac7af982bda

This commit is contained in:
Heng Sin Low 2012-08-01 18:18:37 +08:00
commit cb10841f6f
16 changed files with 758 additions and 144 deletions

View File

@ -0,0 +1,78 @@
-- 29/06/2011 1:33:27 PM
-- -
UPDATE AD_Column SET FieldLength=1024,Updated=TO_DATE('2011-06-29 13:33:27','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=417
;
-- 29/06/2011 1:33:31 PM
-- -
ALTER TABLE AD_User MODIFY Password NVARCHAR2(1024) DEFAULT NULL
;
-- 29/06/2011 1:34:52 PM
-- -
INSERT INTO AD_Element (AD_Client_ID,AD_Element_ID,AD_Org_ID,ColumnName,Created,CreatedBy,Description,EntityType,IsActive,Name,PrintName,Updated,UpdatedBy) VALUES (0,55218,0,'Salt',TO_DATE('2011-06-29 13:34:50','YYYY-MM-DD HH24:MI:SS'),100,'Random data added to improve password hash effectiveness','D','Y','Salt','Salt',TO_DATE('2011-06-29 13:34:50','YYYY-MM-DD HH24:MI:SS'),100)
;
-- 29/06/2011 1:34:52 PM
-- -
INSERT INTO AD_Element_Trl (AD_Language,AD_Element_ID, Description,Help,Name,PO_Description,PO_Help,PO_Name,PO_PrintName,PrintName, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Element_ID, t.Description,t.Help,t.Name,t.PO_Description,t.PO_Help,t.PO_Name,t.PO_PrintName,t.PrintName, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Element t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Element_ID=55218 AND NOT EXISTS (SELECT * FROM AD_Element_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Element_ID=t.AD_Element_ID)
;
-- 29/06/2011 1:36:39 PM
-- -
ALTER TABLE AD_User ADD Salt NVARCHAR2(16) DEFAULT NULL
;
-- 29/06/2011 1:37:48 PM
-- -
INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Table_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,61756,55218,0,10,114,'Salt',TO_DATE('2011-06-29 13:37:47','YYYY-MM-DD HH24:MI:SS'),100,'Random data added to improve password hash effectiveness','D',16,'Y','Y','N','N','N','N','N','N','N','N','N','N','N','Salt',0,TO_DATE('2011-06-29 13:37:47','YYYY-MM-DD HH24:MI:SS'),100,0)
;
-- 29/06/2011 1:37:48 PM
-- -
INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=61756 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID)
;
-- 29/06/2011 4:59:43 PM
-- -
INSERT INTO AD_Process (AccessLevel,AD_Client_ID,AD_Org_ID,AD_Process_ID,Classname,CopyFromProcess,Created,CreatedBy,Description,EntityType,Help,IsActive,IsBetaFunctionality,IsDirectPrint,IsReport,IsServerProcess,Name,ShowHelp,Statistic_Count,Statistic_Seconds,Updated,UpdatedBy,Value) VALUES ('4',0,0,53259,'org.compiere.process.HashPasswords','N',TO_DATE('2011-06-29 16:59:41','YYYY-MM-DD HH24:MI:SS'),100,'Convert existing plain text/encrypted user passwords to one way hash','D','This process will overwrite existing user passwords with a salted SHA-512 hash of the password so that they cannot be recovered if your database is compromised.
(Note: If your password column is currently encrypted, the hash will also be encrypted.)','Y','N','N','N','N','Convert passwords to hashes','Y',0,0,TO_DATE('2011-06-29 16:59:41','YYYY-MM-DD HH24:MI:SS'),100,'AD_User_HashPassword')
;
-- 29/06/2011 4:59:43 PM
-- -
INSERT INTO AD_Process_Trl (AD_Language,AD_Process_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Process_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Process t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Process_ID=53259 AND NOT EXISTS (SELECT * FROM AD_Process_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Process_ID=t.AD_Process_ID)
;
-- 29/06/2011 5:00:28 PM
-- -
INSERT INTO AD_Menu (Action,AD_Client_ID,AD_Menu_ID,AD_Org_ID,AD_Process_ID,Created,CreatedBy,EntityType,IsActive,IsCentrallyMaintained,IsReadOnly,IsSOTrx,IsSummary,Name,Updated,UpdatedBy) VALUES ('P',0,53348,0,53259,TO_DATE('2011-06-29 17:00:27','YYYY-MM-DD HH24:MI:SS'),100,'D','Y','Y','N','N','N','Hash Passwords',TO_DATE('2011-06-29 17:00:27','YYYY-MM-DD HH24:MI:SS'),100)
;
-- 29/06/2011 5:00:28 PM
-- -
INSERT INTO AD_Menu_Trl (AD_Language,AD_Menu_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Menu_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Menu t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Menu_ID=53348 AND NOT EXISTS (SELECT * FROM AD_Menu_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Menu_ID=t.AD_Menu_ID)
;
-- 29/06/2011 5:00:28 PM
-- -
INSERT INTO AD_TreeNodeMM (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, AD_Tree_ID, Node_ID, Parent_ID, SeqNo) SELECT t.AD_Client_ID, 0, 'Y', SysDate, 100, SysDate, 100,t.AD_Tree_ID, 53348, 0, 999 FROM AD_Tree t WHERE t.AD_Client_ID=0 AND t.IsActive='Y' AND t.IsAllNodes='Y' AND t.TreeType='MM' AND NOT EXISTS (SELECT * FROM AD_TreeNodeMM e WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=53348)
;
-- 29/06/2011 5:00:53 PM
-- -
UPDATE AD_TreeNodeMM SET Parent_ID=367, SeqNo=999, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=53348
;
-- Jul 24, 2012 5:50:06 PM COT
INSERT INTO AD_SysConfig (AD_SysConfig_ID,EntityType,ConfigurationLevel,Value,Description,AD_SysConfig_UU,Created,Updated,AD_Client_ID,AD_Org_ID,CreatedBy,IsActive,UpdatedBy,Name) VALUES (200013,'D','S','N','Enable hash passwords, please use Hash Password process to enable','569577c3-3bfa-4d0f-a2e3-d98752164667',TO_DATE('2012-07-24 17:50:04','YYYY-MM-DD HH24:MI:SS'),TO_DATE('2012-07-24 17:50:04','YYYY-MM-DD HH24:MI:SS'),0,0,0,'Y',0,'USER_PASSWORD_HASH')
;
UPDATE AD_System
SET LastMigrationScriptApplied='854_PasswordHash_IDEMPIERE-347.sql'
WHERE LastMigrationScriptApplied<'854_PasswordHash_IDEMPIERE-347.sql'
OR LastMigrationScriptApplied IS NULL
;

View File

@ -0,0 +1,78 @@
-- 29/06/2011 1:33:27 PM
-- -
UPDATE AD_Column SET FieldLength=1024,Updated=TO_TIMESTAMP('2011-06-29 13:33:27','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=417
;
-- 29/06/2011 1:33:31 PM
-- -
INSERT INTO t_alter_column values('ad_user','Password','VARCHAR(1024)',null,'NULL')
;
-- 29/06/2011 1:34:52 PM
-- -
INSERT INTO AD_Element (AD_Client_ID,AD_Element_ID,AD_Org_ID,ColumnName,Created,CreatedBy,Description,EntityType,IsActive,Name,PrintName,Updated,UpdatedBy) VALUES (0,55218,0,'Salt',TO_TIMESTAMP('2011-06-29 13:34:50','YYYY-MM-DD HH24:MI:SS'),100,'Random data added to improve password hash effectiveness','D','Y','Salt','Salt',TO_TIMESTAMP('2011-06-29 13:34:50','YYYY-MM-DD HH24:MI:SS'),100)
;
-- 29/06/2011 1:34:52 PM
-- -
INSERT INTO AD_Element_Trl (AD_Language,AD_Element_ID, Description,Help,Name,PO_Description,PO_Help,PO_Name,PO_PrintName,PrintName, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Element_ID, t.Description,t.Help,t.Name,t.PO_Description,t.PO_Help,t.PO_Name,t.PO_PrintName,t.PrintName, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Element t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Element_ID=55218 AND NOT EXISTS (SELECT * FROM AD_Element_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Element_ID=t.AD_Element_ID)
;
-- 29/06/2011 1:36:39 PM
-- -
ALTER TABLE AD_User ADD COLUMN Salt VARCHAR(16) DEFAULT NULL
;
-- 29/06/2011 1:37:48 PM
-- -
INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Table_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,61756,55218,0,10,114,'Salt',TO_TIMESTAMP('2011-06-29 13:37:47','YYYY-MM-DD HH24:MI:SS'),100,'Random data added to improve password hash effectiveness','D',16,'Y','Y','N','N','N','N','N','N','N','N','N','N','N','Salt',0,TO_TIMESTAMP('2011-06-29 13:37:47','YYYY-MM-DD HH24:MI:SS'),100,0)
;
-- 29/06/2011 1:37:48 PM
-- -
INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=61756 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID)
;
-- 29/06/2011 4:59:43 PM
-- -
INSERT INTO AD_Process (AccessLevel,AD_Client_ID,AD_Org_ID,AD_Process_ID,Classname,CopyFromProcess,Created,CreatedBy,Description,EntityType,Help,IsActive,IsBetaFunctionality,IsDirectPrint,IsReport,IsServerProcess,Name,ShowHelp,Statistic_Count,Statistic_Seconds,Updated,UpdatedBy,Value) VALUES ('4',0,0,53259,'org.compiere.process.HashPasswords','N',TO_TIMESTAMP('2011-06-29 16:59:41','YYYY-MM-DD HH24:MI:SS'),100,'Convert existing plain text/encrypted user passwords to one way hash','D','This process will overwrite existing user passwords with a salted SHA-512 hash of the password so that they cannot be recovered if your database is compromised.
(Note: If your password column is currently encrypted, the hash will also be encrypted.)','Y','N','N','N','N','Convert passwords to hashes','Y',0,0,TO_TIMESTAMP('2011-06-29 16:59:41','YYYY-MM-DD HH24:MI:SS'),100,'AD_User_HashPassword')
;
-- 29/06/2011 4:59:43 PM
-- -
INSERT INTO AD_Process_Trl (AD_Language,AD_Process_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Process_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Process t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Process_ID=53259 AND NOT EXISTS (SELECT * FROM AD_Process_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Process_ID=t.AD_Process_ID)
;
-- 29/06/2011 5:00:28 PM
-- -
INSERT INTO AD_Menu (Action,AD_Client_ID,AD_Menu_ID,AD_Org_ID,AD_Process_ID,Created,CreatedBy,EntityType,IsActive,IsCentrallyMaintained,IsReadOnly,IsSOTrx,IsSummary,Name,Updated,UpdatedBy) VALUES ('P',0,53348,0,53259,TO_TIMESTAMP('2011-06-29 17:00:27','YYYY-MM-DD HH24:MI:SS'),100,'D','Y','Y','N','N','N','Hash Passwords',TO_TIMESTAMP('2011-06-29 17:00:27','YYYY-MM-DD HH24:MI:SS'),100)
;
-- 29/06/2011 5:00:28 PM
-- -
INSERT INTO AD_Menu_Trl (AD_Language,AD_Menu_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Menu_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Menu t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Menu_ID=53348 AND NOT EXISTS (SELECT * FROM AD_Menu_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Menu_ID=t.AD_Menu_ID)
;
-- 29/06/2011 5:00:28 PM
-- -
INSERT INTO AD_TreeNodeMM (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, AD_Tree_ID, Node_ID, Parent_ID, SeqNo) SELECT t.AD_Client_ID, 0, 'Y', CURRENT_TIMESTAMP, 100, CURRENT_TIMESTAMP, 100,t.AD_Tree_ID, 53348, 0, 999 FROM AD_Tree t WHERE t.AD_Client_ID=0 AND t.IsActive='Y' AND t.IsAllNodes='Y' AND t.TreeType='MM' AND NOT EXISTS (SELECT * FROM AD_TreeNodeMM e WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=53348)
;
-- 29/06/2011 5:00:53 PM
-- -
UPDATE AD_TreeNodeMM SET Parent_ID=367, SeqNo=999, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=53348
;
-- Jul 24, 2012 5:50:06 PM COT
INSERT INTO AD_SysConfig (AD_SysConfig_ID,EntityType,ConfigurationLevel,Value,Description,AD_SysConfig_UU,Created,Updated,AD_Client_ID,AD_Org_ID,CreatedBy,IsActive,UpdatedBy,Name) VALUES (200013,'D','S','N','Enable hash passwords, please use Hash Password process to enable','569577c3-3bfa-4d0f-a2e3-d98752164667',TO_TIMESTAMP('2012-07-24 17:50:04','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2012-07-24 17:50:04','YYYY-MM-DD HH24:MI:SS'),0,0,0,'Y',0,'USER_PASSWORD_HASH')
;
UPDATE AD_System
SET LastMigrationScriptApplied='854_PasswordHash_IDEMPIERE-347.sql'
WHERE LastMigrationScriptApplied<'854_PasswordHash_IDEMPIERE-347.sql'
OR LastMigrationScriptApplied IS NULL
;

View File

@ -0,0 +1,78 @@
/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.process;
import java.util.List;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTable;
import org.compiere.model.MUser;
import org.compiere.model.SystemIDs;
/**
* Hash existing passwords
*
*/
public class HashPasswords extends SvrProcess
{
/**
* Prepare - e.g., get Parameters.
*/
protected void prepare()
{
} // prepare
/**
* Perform process.
* @return Message
* @throws Exception if not successful
*/
protected String doIt() throws Exception
{
boolean hash_password = MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
if (hash_password)
throw new AdempiereException("Passwords already hashed");
String where = " Password IS NOT NULL AND Salt IS NULL ";
// update the sysconfig key to Y out of trx and reset the cache
MSysConfig conf = new MSysConfig(getCtx(), SystemIDs.SYSCONFIG_USER_HASH_PASSWORD, null);
conf.setValue("Y");
conf.saveEx();
MSysConfig.resetCache();
int count = 0;
try {
List<MUser> users = MTable.get(getCtx(), MUser.Table_ID).createQuery( where, get_TrxName()).list();
for ( MUser user : users )
{
user.setPassword(user.getPassword());
count++;
user.saveEx();
}
} catch (Exception e) {
// reset to N on exception
conf.setValue("N");
conf.saveEx();
throw e;
}
return "@Updated@ " + count;
} // doIt
} // HashPasswords

View File

@ -18,8 +18,8 @@ package org.compiere.process;
import java.util.logging.Level;
import org.compiere.model.MSysConfig;
import org.compiere.model.MUser;
import org.compiere.util.DB;
import org.compiere.util.Util;
/**
@ -78,7 +78,7 @@ public class UserPassword extends SvrProcess
MUser operator = MUser.get(getCtx(), getAD_User_ID());
log.fine("User=" + user + ", Operator=" + operator);
boolean hash_password = MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
// Do we need a password ?
if (Util.isEmpty(p_OldPassword)) // Password required
@ -88,31 +88,17 @@ public class UserPassword extends SvrProcess
|| !operator.isAdministrator())
throw new IllegalArgumentException("@OldPasswordMandatory@");
}
// is entered Password correct ?
else if (!p_OldPassword.equals(user.getPassword()))
else {
if (hash_password){
if (!user.authenticateHash(p_OldPassword) )
throw new IllegalArgumentException("@OldPasswordNoMatch@");
} else{
if (!p_OldPassword.equals(user.getPassword()))
throw new IllegalArgumentException("@OldPasswordNoMatch@");
// Change Super User
if (p_AD_User_ID == 0)
{
String sql = "UPDATE AD_User SET Updated=SysDate, UpdatedBy=" + getAD_User_ID();
if (!Util.isEmpty(p_NewPassword))
sql += ", Password=" + DB.TO_STRING(p_NewPassword);
if (!Util.isEmpty(p_NewEMail))
sql += ", Email=" + DB.TO_STRING(p_NewEMail);
if (!Util.isEmpty(p_NewEMailUser))
sql += ", EmailUser=" + DB.TO_STRING(p_NewEMailUser);
if (!Util.isEmpty(p_NewEMailUserPW))
sql += ", EmailUserPW=" + DB.TO_STRING(p_NewEMailUserPW);
sql += " WHERE AD_User_ID=0";
if (DB.executeUpdate(sql, get_TrxName()) == 1)
return "OK";
else
return "@Error@";
}
else
{
}
if (!Util.isEmpty(p_NewPassword))
user.setPassword(p_NewPassword);
if (!Util.isEmpty(p_NewEMail))
@ -121,13 +107,9 @@ public class UserPassword extends SvrProcess
user.setEMailUser(p_NewEMailUser);
if (!Util.isEmpty(p_NewEMailUserPW))
user.setEMailUserPW(p_NewEMailUserPW);
//
if (user.save())
user.saveEx();
return "OK";
else
return "@Error@";
}
} // doIt
} // UserPassword

View File

@ -1443,18 +1443,9 @@ public class GridTable extends AbstractTableModel
&& (Env.getAD_User_ID(m_ctx) == USER_SYSTEM || Env.getAD_User_ID(m_ctx) == USER_SUPERUSER) // user must know what is doing -> just allowed to System or SuperUser (Hardcoded)
&& getKeyID(m_rowChanged) == 0) { // the record being changed has ID = 0
String tablename = getTableName(); // just the allowed tables (HardCoded)
if (tablename.equals("AD_Org") ||
tablename.equals("AD_ReportView") ||
tablename.equals("AD_Role") ||
tablename.equals("AD_System") ||
tablename.equals("AD_User") ||
tablename.equals("C_DocType") ||
tablename.equals("GL_Category") ||
tablename.equals("M_AttributeSet") ||
tablename.equals("M_AttributeSetInstance")) {
if (MTable.isZeroIDTable(tablename))
specialZeroUpdate = true;
}
}
// Check Mandatory
String missingColumns = getMandatory(rowData);

View File

@ -459,6 +459,19 @@ public interface I_AD_User
/** Get Process Now */
public boolean isProcessing();
/** Column name Salt */
public static final String COLUMNNAME_Salt = "Salt";
/** Set Salt.
* Random data added to improve password hash effectiveness
*/
public void setSalt (String Salt);
/** Get Salt.
* Random data added to improve password hash effectiveness
*/
public String getSalt();
/** Column name Supervisor_ID */
public static final String COLUMNNAME_Supervisor_ID = "Supervisor_ID";

View File

@ -50,11 +50,10 @@ import org.compiere.util.DB;
*/
public class MTable extends X_AD_Table
{
/**
*
*/
private static final long serialVersionUID = -2367316254623142732L;
private static final long serialVersionUID = 8264472455498363565L;
public final static int MAX_OFFICIAL_ID = 999999;
@ -587,4 +586,20 @@ public class MTable extends X_AD_Table
return sb.toString ();
} // toString
/**
* Verify if the table contains ID=0
* @return true if table has zero ID
*/
public static boolean isZeroIDTable(String tablename) {
return (tablename.equals("AD_Org") ||
tablename.equals("AD_ReportView") ||
tablename.equals("AD_Role") ||
tablename.equals("AD_System") ||
tablename.equals("AD_User") ||
tablename.equals("C_DocType") ||
tablename.equals("GL_Category") ||
tablename.equals("M_AttributeSet") ||
tablename.equals("M_AttributeSetInstance"));
}
} // MTable

View File

@ -16,6 +16,9 @@
*****************************************************************************/
package org.compiere.model;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -33,6 +36,7 @@ import org.compiere.util.CCache;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Secure;
import org.compiere.util.SecureEngine;
/**
@ -50,7 +54,7 @@ public class MUser extends X_AD_User
/**
*
*/
private static final long serialVersionUID = -5845477151929518375L;
private static final long serialVersionUID = -5343496366428193731L;
/**
* Get active Users of BPartner
@ -167,9 +171,13 @@ public class MUser extends X_AD_User
s_log.warning ("Invalid Name/Password = " + name + "/" + password);
return null;
}
boolean hash_password = MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
MUser retValue = null;
if (!hash_password)
{
int AD_Client_ID = Env.getAD_Client_ID(ctx);
MUser retValue = null;
String sql = "SELECT * FROM AD_User "
+ "WHERE COALESCE(LDAPUser, Name)=? " // #1
+ " AND ((Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') " // #2
@ -204,6 +212,40 @@ public class MUser extends X_AD_User
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
} else {
String where = " COALESCE(LDAPUser,Name) = ? AND" +
" EXISTS (SELECT * FROM AD_User_Roles ur" +
" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID)" +
" WHERE ur.AD_User_ID=AD_User.AD_User_ID AND ur.IsActive='Y' AND r.IsActive='Y') AND " +
" EXISTS (SELECT * FROM AD_Client c" +
" WHERE c.AD_Client_ID=AD_User.AD_Client_ID" +
" AND c.IsActive='Y') AND " +
" AD_User.IsActive='Y'";
MUser user = MTable.get(ctx, MUser.Table_ID).createQuery( where, null).setParameters(name).firstOnly(); // throws error if username collision occurs
String hash = null;
String salt = null;
if (user != null )
{
hash = user.getPassword();
salt = user.getSalt();
}
// always do calculation to confuse timing based attacks
if ( user == null )
user = MUser.get(ctx, 0);
if ( hash == null )
hash = "0000000000000000";
if ( salt == null )
salt = "0000000000000000";
if ( user.authenticateHash(password) )
{
retValue=user;
}
}
return retValue;
} // get
@ -308,6 +350,8 @@ public class MUser extends X_AD_User
private Boolean m_isAdministrator = null;
/** User Access Rights */
private X_AD_UserBPAccess[] m_bpAccess = null;
/** Password Hashed **/
private boolean hashed = false;
/**
@ -376,6 +420,77 @@ public class MUser extends X_AD_User
return sb.toString ();
} // cleanValue
/**
* Convert Password to SHA-512 hash with salt * 1000 iterations https://www.owasp.org/index.php/Hashing_Java
* @param password -- plain text password
*/
@Override
public void setPassword(String password) {
if ( password == null )
{
super.setPassword(password);
return;
}
boolean hash_password = MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
if(!hash_password){
super.setPassword(password);
return;
}
if ( hashed )
return;
hashed = true; // prevents double call from beforeSave
// Uses a secure Random not a simple Random
SecureRandom random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
// Salt generation 64 bits long
byte[] bSalt = new byte[8];
random.nextBytes(bSalt);
// Digest computation
String hash;
hash = SecureEngine.getSHA512Hash(1000,password,bSalt);
String sSalt = Secure.convertToHexString(bSalt);
super.setPassword(hash);
setSalt(sSalt);
} catch (NoSuchAlgorithmException e) {
super.setPassword(password);
} catch (UnsupportedEncodingException e) {
super.setPassword(password);
}
}
/**
* check if hashed password matches
*/
public boolean authenticateHash (String password) {
String hash = null;
String salt = null;
hash = getPassword();
salt = getSalt();
// always do calculation to prevent timing based attacks
if ( hash == null )
hash = "0000000000000000";
if ( salt == null )
salt = "0000000000000000";
try {
return SecureEngine.getSHA512Hash(1000, password, Secure.convertHexString(salt)).equals(hash);
} catch (NoSuchAlgorithmException ignored) {
log.log(Level.WARNING, "Password hashing not supported by JVM");
} catch (UnsupportedEncodingException ignored) {
log.log(Level.WARNING, "Password hashing not supported by JVM");
}
return false;
}
/**
* Get First Name
* @return first name
@ -801,6 +916,13 @@ public class MUser extends X_AD_User
setEMailVerifyDate(null);
if (newRecord || super.getValue() == null || is_ValueChanged("Value"))
setValue(super.getValue());
if (newRecord || is_ValueChanged("Password")) {
boolean hash_password = MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
if (hash_password)
setPassword(getPassword());
}
return true;
} // beforeSave

View File

@ -1921,6 +1921,8 @@ public abstract class PO
continue;
return false; // one value is non-zero
}
if (MTable.isZeroIDTable(get_TableName()))
return false;
return true;
} // is_new

View File

@ -145,4 +145,6 @@ public interface SystemIDs
public final static int WINDOW_SHIPMENT_CUSTOMER = 169;
public final static int WINDOW_WAREHOUSE_LOCATOR = 139;
public final static int WINDOW_WINDOW_TAB_FIELD = 102;
public final static int SYSCONFIG_USER_HASH_PASSWORD = 200013;
}

View File

@ -665,6 +665,25 @@ public class X_AD_User extends PO implements I_AD_User, I_Persistent
return false;
}
/**
* Set Salt.
*
* @param Salt
* Random data added to improve password hash effectiveness
*/
public void setSalt(String Salt) {
set_ValueNoCheck(COLUMNNAME_Salt, Salt);
}
/**
* Get Salt.
*
* @return Random data added to improve password hash effectiveness
*/
public String getSalt() {
return (String) get_Value(COLUMNNAME_Salt);
}
public I_AD_User getSupervisor() throws RuntimeException
{
return (I_AD_User)MTable.get(getCtx(), I_AD_User.Table_Name)

View File

@ -16,6 +16,8 @@
*****************************************************************************/
package org.compiere.util;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.sql.Connection;
import java.sql.PreparedStatement;
@ -35,8 +37,11 @@ import org.compiere.model.MAcctSchema;
import org.compiere.model.MClientInfo;
import org.compiere.model.MCountry;
import org.compiere.model.MRole;
import org.compiere.model.MSysConfig;
import org.compiere.model.MSystem;
import org.compiere.model.MTable;
import org.compiere.model.MTree_Base;
import org.compiere.model.MUser;
import org.compiere.model.ModelValidationEngine;
@ -233,6 +238,7 @@ public class Login
private KeyNamePair[] getRoles (String app_user, String app_pwd, boolean force)
{
log.info("User=" + app_user);
long start = System.currentTimeMillis();
if (app_user == null)
{
@ -240,7 +246,7 @@ public class Login
return null;
}
// Authentification
// Authentication
boolean authenticated = false;
MSystem system = MSystem.get(m_ctx);
if (system == null)
@ -251,40 +257,130 @@ public class Login
log.warning("No Apps Password");
return null;
}
if (system.isLDAP())
{
authenticated = system.isLDAP(app_user, app_pwd);
if (authenticated)
if (authenticated){
app_pwd = null;
authenticated=true;
}
// if not authenticated, use AD_User as backup
}
boolean hash_password=MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
KeyNamePair[] retValue = null;
ArrayList<KeyNamePair> list = new ArrayList<KeyNamePair>();
//
if(hash_password){
// adaxa-pb: try to authenticate using hashed password -- falls back to plain text/encrypted
String where = " COALESCE(LDAPUser,Name) = ? AND" +
" EXISTS (SELECT * FROM AD_User_Roles ur" +
" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID)" +
" WHERE ur.AD_User_ID=AD_User.AD_User_ID AND ur.IsActive='Y' AND r.IsActive='Y') AND " +
" EXISTS (SELECT * FROM AD_Client c" +
" WHERE c.AD_Client_ID=AD_User.AD_Client_ID" +
" AND c.IsActive='Y') AND " +
" AD_User.IsActive='Y'";
MUser user = MTable.get(m_ctx, MUser.Table_ID).createQuery( where, null).setParameters(app_user).firstOnly(); // throws error if username collision occurs
String hash = null;
String salt = null;
int AD_User_ID = -1;
if (user != null )
{
hash = user.getPassword();
salt = user.getSalt();
}
// always do calculation to confuse timing based attacks
if ( user == null )
user = MUser.get(m_ctx, 0);
if ( hash == null )
hash = "0000000000000000";
if ( salt == null )
salt = "0000000000000000";
if ( user.authenticateHash(app_pwd) )
{
authenticated = true;
AD_User_ID = user.getAD_User_ID();
app_pwd = null;
}
}
else{
StringBuffer sql = new StringBuffer("SELECT u.AD_User_ID,")
.append(" u.ConnectionProfile ")
.append(" FROM AD_User u");
sql.append(" WHERE COALESCE(u.LDAPUser,u.Name)=?");
sql.append(" AND u.IsActive='Y'").append(" AND EXISTS (SELECT * FROM AD_Client c WHERE u.AD_Client_ID=c.AD_Client_ID AND c.IsActive='Y')");
if (app_pwd != null)
sql.append(" AND ((u.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') "
+ "OR (u.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))"); // #2/3
PreparedStatement pstmt1=null;
ResultSet rs1=null;
try{
pstmt1 = DB.prepareStatement(sql.toString(), null);
pstmt1.setString(1, app_user);
if (app_pwd != null)
{
pstmt1.setString(2, app_pwd);
pstmt1.setString(3, SecureEngine.encrypt(app_pwd));
}
rs1 = pstmt1.executeQuery();
while(rs1.next()){
authenticated=true;
}
}catch (Exception ex) {
// TODO: handle exception
log.log(Level.SEVERE, sql.toString(), ex);
log.saveError("DBLogin", ex);
retValue = null;
}
finally
{
DB.close(rs1, pstmt1);
rs1 = null; pstmt1 = null;
}
}
if(authenticated){
StringBuffer sql = new StringBuffer("SELECT u.AD_User_ID, r.AD_Role_ID,r.Name,")
.append(" u.ConnectionProfile ")
.append("FROM AD_User u")
.append(" INNER JOIN AD_User_Roles ur ON (u.AD_User_ID=ur.AD_User_ID AND ur.IsActive='Y')")
.append(" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID AND r.IsActive='Y') ")
.append("WHERE COALESCE(u.LDAPUser,u.Name)=?") // #1
.append(" AND u.IsActive='Y'")
.append(" AND EXISTS (SELECT * FROM AD_Client c WHERE u.AD_Client_ID=c.AD_Client_ID AND c.IsActive='Y')");
if (app_pwd != null)
.append(" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID AND r.IsActive='Y') ");
sql.append("WHERE COALESCE(u.LDAPUser,u.Name)=?"); // #1
sql.append(" AND u.IsActive='Y'").append(" AND EXISTS (SELECT * FROM AD_Client c WHERE u.AD_Client_ID=c.AD_Client_ID AND c.IsActive='Y')");
/* if (app_pwd != null && !hash_password)
sql.append(" AND ((u.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') "
+ "OR (u.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))"); // #2/3
+ "OR (u.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))"); // #2/3*/
sql.append(" ORDER BY r.Name");
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql.toString(), null);
pstmt.setString(1, app_user);
if (app_pwd != null)
/*if (app_pwd != null && !hash_password)
{
pstmt.setString(2, app_pwd);
pstmt.setString(3, SecureEngine.encrypt(app_pwd));
}
}*/
// execute a query
rs = pstmt.executeQuery();
@ -313,7 +409,7 @@ public class Login
Env.setContext(m_ctx, "#AD_User_Name", app_user);
Env.setContext(m_ctx, "#AD_User_ID", rs.getInt(1));
Env.setContext(m_ctx, "#SalesRep_ID", rs.getInt(1));
//
if (Ini.isClient())
{
if (MSystem.isSwingRememberUserAllowed())
@ -350,7 +446,9 @@ public class Login
retValue = new KeyNamePair[list.size()];
list.toArray(retValue);
log.fine("User=" + app_user + " - roles #" + retValue.length);
}
catch (Exception ex)
{
log.log(Level.SEVERE, sql.toString(), ex);
@ -363,6 +461,7 @@ public class Login
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
}
long ms = System.currentTimeMillis () - start;
return retValue;
} // getRoles

View File

@ -16,6 +16,7 @@
*****************************************************************************/
package org.compiere.util;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.AlgorithmParameters;
import java.security.MessageDigest;
@ -378,6 +379,30 @@ public class Secure implements SecureInterface
return (convertHexString(value) != null);
} // isDigest
/**
* Convert String and salt to SHA-512 hash with iterations
* https://www.owasp.org/index.php/Hashing_Java
*
* @param value message
* @return HexString of message (length = 128 characters)
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
public String getSHA512Hash (int iterations, String value, byte[] salt) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
MessageDigest digest = MessageDigest.getInstance("SHA-512");
digest.reset();
digest.update(salt);
byte[] input = digest.digest(value.getBytes("UTF-8"));
for (int i = 0; i < iterations; i++) {
digest.reset();
input = digest.digest(input);
}
digest.reset();
//
return convertToHexString(input);
} // getSHA512Hash
/**
* String Representation
* @return info

View File

@ -16,6 +16,8 @@
*****************************************************************************/
package org.compiere.util;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import java.util.logging.Logger;
@ -70,6 +72,22 @@ public class SecureEngine
return s_engine.implementation.getClass().getName();
} // getClassName
/**
* Convert String and salt to SHA-512 hash with iterations
* https://www.owasp.org/index.php/Hashing_Java
*
* @param value message
* @return HexString of message (length = 128 characters)
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static String getSHA512Hash (int iterations, String value, byte[] salt) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
if (s_engine == null)
init(System.getProperties());
return s_engine.implementation.getSHA512Hash(iterations, value, salt);
} // getDigest
/**
* Convert String to Digest.
* JavaScript version see - http://pajhome.org.uk/crypt/md5/index.html

View File

@ -16,7 +16,9 @@
*****************************************************************************/
package org.compiere.util;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
/**
@ -126,4 +128,15 @@ public interface SecureInterface
*/
public boolean isDigest (String value);
/**
* Convert String and salt to SHA-512 hash with iterations
* https://www.owasp.org/index.php/Hashing_Java
*
* @param value message
* @return HexString of message (length = 128 characters)
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
public String getSHA512Hash (int iterations, String value, byte[] salt) throws NoSuchAlgorithmException, UnsupportedEncodingException;
} // SecureInterface

View File

@ -30,6 +30,8 @@ import org.compiere.model.MBPartner;
import org.compiere.model.MBPartnerLocation;
import org.compiere.model.MLocation;
import org.compiere.model.MRefList;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTable;
import org.compiere.model.MUser;
/**
@ -367,6 +369,7 @@ public class WebUser
m_loc = new MLocation (m_ctx, 0, null);
//
log.info("= " + m_bp + " - " + m_bpc);
} // load
/**
@ -669,8 +672,84 @@ public class WebUser
public boolean login (String password)
{
m_loggedIn = isValid () // we have a contact
&& WebUtil.exists (password) // we have a password
&& password.equals (getPassword ());
&& WebUtil.exists (password); // we have a password
//&& password.equals (getPassword ());
boolean retValue = false;
if(m_loggedIn)
{
boolean hash_password=MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
if(!hash_password)
{
String sql = "SELECT * FROM AD_User "
+ "WHERE COALESCE(LDAPUser, Name)=? " // #1
+ " AND ((Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') " // #2
+ "OR (Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))" // #3
+ " AND IsActive='Y' " // #4
;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setString (1, m_bpc.getName());
pstmt.setString (2, password);
pstmt.setString (3, SecureEngine.encrypt(password));
rs = pstmt.executeQuery ();
if (rs.next ())
{
retValue = true;
if (rs.next())
log.warning ("More then one user with Name/Password = " + m_bpc.getName());
}
else
log.fine("No record");
}
catch (Exception e)
{
log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
}
else{
String where = " COALESCE(LDAPUser,Name) = ? AND" +
" EXISTS (SELECT * FROM AD_User_Roles ur" +
" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID)" +
" WHERE ur.AD_User_ID=AD_User.AD_User_ID AND ur.IsActive='Y' AND r.IsActive='Y') AND " +
" EXISTS (SELECT * FROM AD_Client c" +
" WHERE c.AD_Client_ID=AD_User.AD_Client_ID" +
" AND c.IsActive='Y') AND " +
" AD_User.IsActive='Y'";
MUser user = MTable.get(m_ctx, MUser.Table_ID).createQuery( where, null).setParameters(m_bpc.getName()).firstOnly(); // throws error if username collision occurs
String hash = null;
String salt = null;
if (user != null )
{
hash = user.getPassword();
salt = user.getSalt();
}
// always do calculation to confuse timing based attacks
if ( user == null )
user = MUser.get(null, 0);
if ( hash == null )
hash = "0000000000000000";
if ( salt == null )
salt = "0000000000000000";
if ( user.authenticateHash(password) )
{
retValue=true;
}
}
}
m_loggedIn=m_loggedIn && retValue;
setPasswordOK (m_loggedIn, password);
log.fine("success=" + m_loggedIn);
if (m_loggedIn)