IDEMPIERE-347 passwords hash
Thanks to Adaxa Australia -> http://adempiere.hg.sourceforge.net/hgweb/adempiere/contribution_adaxa/rev/6d9090d8a9f6 Ken Longnan for iDempiere integration work at -> https://bitbucket.org/longnan/idempiere-longnan Juliana Corredor for further integration and tests on iDempiere and making the process safer and backward compatible via sysconfig Carlos Ruiz (globalqss) peer reviewed, tested and finished some extra required Sponsored by Trek Global -> http://www.trekglobal.com/
This commit is contained in:
parent
f1f7168861
commit
26a07ca3c8
|
@ -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
|
||||
;
|
||||
|
|
@ -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
|
||||
;
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -77,57 +77,39 @@ public class UserPassword extends SvrProcess
|
|||
MUser user = MUser.get(getCtx(), p_AD_User_ID);
|
||||
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
|
||||
{
|
||||
if (p_AD_User_ID == 0 // change of System
|
||||
|| p_AD_User_ID == 100 // change of SuperUser
|
||||
|| !operator.isAdministrator())
|
||||
|| p_AD_User_ID == 100 // change of SuperUser
|
||||
|| !operator.isAdministrator())
|
||||
throw new IllegalArgumentException("@OldPasswordMandatory@");
|
||||
}
|
||||
|
||||
// is entered Password correct ?
|
||||
else if (!p_OldPassword.equals(user.getPassword()))
|
||||
throw new IllegalArgumentException("@OldPasswordNoMatch@");
|
||||
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))
|
||||
user.setEMail(p_NewEMail);
|
||||
if (!Util.isEmpty(p_NewEMailUser))
|
||||
user.setEMailUser(p_NewEMailUser);
|
||||
if (!Util.isEmpty(p_NewEMailUserPW))
|
||||
user.setEMailUserPW(p_NewEMailUserPW);
|
||||
//
|
||||
if (user.save())
|
||||
return "OK";
|
||||
else
|
||||
return "@Error@";
|
||||
}
|
||||
if (!Util.isEmpty(p_NewPassword))
|
||||
user.setPassword(p_NewPassword);
|
||||
if (!Util.isEmpty(p_NewEMail))
|
||||
user.setEMail(p_NewEMail);
|
||||
if (!Util.isEmpty(p_NewEMailUser))
|
||||
user.setEMailUser(p_NewEMailUser);
|
||||
if (!Util.isEmpty(p_NewEMailUserPW))
|
||||
user.setEMailUserPW(p_NewEMailUserPW);
|
||||
user.saveEx();
|
||||
|
||||
return "OK";
|
||||
} // doIt
|
||||
|
||||
} // UserPassword
|
||||
|
||||
|
|
|
@ -1443,17 +1443,8 @@ 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
|
||||
|
|
|
@ -458,6 +458,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";
|
||||
|
|
|
@ -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
|
|
@ -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,44 +171,82 @@ public class MUser extends X_AD_User
|
|||
s_log.warning ("Invalid Name/Password = " + name + "/" + password);
|
||||
return null;
|
||||
}
|
||||
int AD_Client_ID = Env.getAD_Client_ID(ctx);
|
||||
|
||||
boolean hash_password = MSysConfig.getBooleanValue("USER_PASSWORD_HASH", false);
|
||||
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
|
||||
+ "OR (Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))" // #3
|
||||
+ " AND IsActive='Y' AND AD_Client_ID=?" // #4
|
||||
;
|
||||
PreparedStatement pstmt = null;
|
||||
ResultSet rs = null;
|
||||
try
|
||||
if (!hash_password)
|
||||
{
|
||||
pstmt = DB.prepareStatement (sql, null);
|
||||
pstmt.setString (1, name);
|
||||
pstmt.setString (2, password);
|
||||
pstmt.setString (3, SecureEngine.encrypt(password));
|
||||
pstmt.setInt(4, AD_Client_ID);
|
||||
rs = pstmt.executeQuery ();
|
||||
if (rs.next ())
|
||||
int AD_Client_ID = Env.getAD_Client_ID(ctx);
|
||||
|
||||
|
||||
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' AND AD_Client_ID=?" // #4
|
||||
;
|
||||
PreparedStatement pstmt = null;
|
||||
ResultSet rs = null;
|
||||
try
|
||||
{
|
||||
retValue = new MUser (ctx, rs, null);
|
||||
if (rs.next())
|
||||
s_log.warning ("More then one user with Name/Password = " + name);
|
||||
pstmt = DB.prepareStatement (sql, null);
|
||||
pstmt.setString (1, name);
|
||||
pstmt.setString (2, password);
|
||||
pstmt.setString (3, SecureEngine.encrypt(password));
|
||||
pstmt.setInt(4, AD_Client_ID);
|
||||
rs = pstmt.executeQuery ();
|
||||
if (rs.next ())
|
||||
{
|
||||
retValue = new MUser (ctx, rs, null);
|
||||
if (rs.next())
|
||||
s_log.warning ("More then one user with Name/Password = " + name);
|
||||
}
|
||||
else
|
||||
s_log.fine("No record");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
s_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(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;
|
||||
}
|
||||
else
|
||||
s_log.fine("No record");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
s_log.log(Level.SEVERE, sql, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DB.close(rs, pstmt);
|
||||
rs = null; pstmt = null;
|
||||
}
|
||||
return retValue;
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -664,6 +664,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
|
||||
{
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
* or via info@compiere.org or http://www.compiere.org/license.html *
|
||||
*****************************************************************************/
|
||||
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;
|
||||
|
||||
|
||||
|
@ -232,7 +237,8 @@ public class Login
|
|||
*/
|
||||
private KeyNamePair[] getRoles (String app_user, String app_pwd, boolean force)
|
||||
{
|
||||
log.info("User=" + app_user);
|
||||
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,69 +257,159 @@ 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
|
||||
}
|
||||
|
||||
KeyNamePair[] retValue = null;
|
||||
ArrayList<KeyNamePair> list = new ArrayList<KeyNamePair>();
|
||||
//
|
||||
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)
|
||||
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
|
||||
sql.append(" ORDER BY 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') ");
|
||||
|
||||
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*/
|
||||
|
||||
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)
|
||||
pstmt.setString(1, app_user);
|
||||
|
||||
/*if (app_pwd != null && !hash_password)
|
||||
{
|
||||
pstmt.setString(2, app_pwd);
|
||||
pstmt.setString(3, SecureEngine.encrypt(app_pwd));
|
||||
}
|
||||
}*/
|
||||
// execute a query
|
||||
rs = pstmt.executeQuery();
|
||||
|
||||
if (!rs.next()) // no record found
|
||||
if (force)
|
||||
{
|
||||
Env.setContext(m_ctx, "#AD_User_Name", "System");
|
||||
Env.setContext(m_ctx, "#AD_User_ID", "0");
|
||||
Env.setContext(m_ctx, "#AD_User_Description", "System Forced Login");
|
||||
Env.setContext(m_ctx, "#User_Level", "S "); // Format 'SCO'
|
||||
Env.setContext(m_ctx, "#User_Client", "0"); // Format c1, c2, ...
|
||||
Env.setContext(m_ctx, "#User_Org", "0"); // Format o1, o2, ...
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
retValue = new KeyNamePair[] {new KeyNamePair(0, "System Administrator")};
|
||||
return retValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
log.saveError("UserPwdError", app_user, false);
|
||||
return null;
|
||||
}
|
||||
|
||||
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));
|
||||
//
|
||||
rs = pstmt.executeQuery();
|
||||
|
||||
if (!rs.next()) // no record found
|
||||
if (force)
|
||||
{
|
||||
Env.setContext(m_ctx, "#AD_User_Name", "System");
|
||||
Env.setContext(m_ctx, "#AD_User_ID", "0");
|
||||
Env.setContext(m_ctx, "#AD_User_Description", "System Forced Login");
|
||||
Env.setContext(m_ctx, "#User_Level", "S "); // Format 'SCO'
|
||||
Env.setContext(m_ctx, "#User_Client", "0"); // Format c1, c2, ...
|
||||
Env.setContext(m_ctx, "#User_Org", "0"); // Format o1, o2, ...
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
retValue = new KeyNamePair[] {new KeyNamePair(0, "System Administrator")};
|
||||
return retValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
log.saveError("UserPwdError", app_user, false);
|
||||
return null;
|
||||
}
|
||||
|
||||
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())
|
||||
|
@ -335,7 +431,7 @@ public class Login
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
do // read all roles
|
||||
{
|
||||
int AD_Role_ID = rs.getInt(2);
|
||||
|
@ -349,8 +445,10 @@ public class Login
|
|||
//
|
||||
retValue = new KeyNamePair[list.size()];
|
||||
list.toArray(retValue);
|
||||
log.fine("User=" + app_user + " - roles #" + retValue.length);
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue