/******************************************************************************
* 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.model;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.adempiere.exceptions.DBException;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.EMail;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.util.Secure;
import org.compiere.util.SecureEngine;
import org.compiere.util.Util;
import org.idempiere.cache.ImmutableIntPOCache;
import org.idempiere.cache.ImmutablePOSupport;
/**
* User Model
*
* @author Jorg Janke
* @version $Id: MUser.java,v 1.3 2006/07/30 00:58:18 jjanke Exp $
*
* @author Teo Sarca, www.arhipac.ro
*
FR [ 2788430 ] MUser.getOfBPartner add trxName parameter
* https://sourceforge.net/p/adempiere/feature-requests/714/
*/
public class MUser extends X_AD_User implements ImmutablePOSupport
{
/**
* generated serial id
*/
private static final long serialVersionUID = 1351277092193923708L;
/**
* Get active Users of BPartner
* @param ctx context
* @param C_BPartner_ID id
* @return array of users
* @deprecated Since 3.5.3a. Please use {@link #getOfBPartner(Properties, int, String)}.
*/
@Deprecated
public static MUser[] getOfBPartner (Properties ctx, int C_BPartner_ID)
{
return getOfBPartner(ctx, C_BPartner_ID, null);
}
/**
* Get active Users of BPartner
* @param ctx
* @param C_BPartner_ID
* @param trxName
* @return array of users
*/
public static MUser[] getOfBPartner (Properties ctx, int C_BPartner_ID, String trxName)
{
List list = new Query(ctx, I_AD_User.Table_Name, "C_BPartner_ID=?", trxName)
.setParameters(C_BPartner_ID)
.setOnlyActiveRecords(true)
.list();
MUser[] retValue = new MUser[list.size ()];
list.toArray (retValue);
return retValue;
} // getOfBPartner
/**
* Get Users with Role
* @param role role
* @return array of users
*/
public static MUser[] getWithRole (MRole role)
{
ArrayList list = new ArrayList();
String sql = "SELECT * FROM AD_User u "
+ "WHERE u.IsActive='Y'"
+ " AND EXISTS (SELECT * FROM AD_User_Roles ur "
+ "WHERE ur.AD_User_ID=u.AD_User_ID AND ur.AD_Role_ID=? AND ur.IsActive='Y')";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, role.getAD_Role_ID());
rs = pstmt.executeQuery ();
while (rs.next ())
list.add(new MUser(role.getCtx(), rs, null));
}
catch (Exception e)
{
s_log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
}
MUser[] retValue = new MUser[list.size ()];
list.toArray (retValue);
return retValue;
} // getWithRole
/**
* Get User (cached) (immutable).
* Also loads Administrator (0).
* @param AD_User_ID id
* @return user
*/
public static MUser get (int AD_User_ID)
{
return get(Env.getCtx(), AD_User_ID);
}
/**
* Get User (cached) (immutable).
* Also loads Administrator (0).
* @param ctx context
* @param AD_User_ID id
* @return user
*/
public static MUser get (Properties ctx, int AD_User_ID)
{
Integer key = Integer.valueOf(AD_User_ID);
MUser retValue = s_cache.get(ctx, key, e -> new MUser(ctx, e));
if (retValue == null)
{
retValue = new MUser (ctx, AD_User_ID, (String)null);
if (AD_User_ID == SystemIDs.USER_SYSTEM_DEPRECATED)
{
String trxName = null;
retValue.load(trxName); // load System Record
}
if (retValue.get_ID() == AD_User_ID)
{
s_cache.put(key, retValue, e -> new MUser(Env.getCtx(), e));
return retValue;
}
return null;
}
return retValue;
} // get
/**
* Get Current User (cached)
* @param ctx context
* @return user
*/
public static MUser get (Properties ctx)
{
return get(ctx, Env.getAD_User_ID(ctx));
} // get
/**
* Get updateable copy of MUser from cache
* @param ctx
* @param AD_User_ID
* @param trxName
* @return MUser
*/
public static MUser getCopy(Properties ctx, int AD_User_ID, String trxName)
{
MUser user = get(AD_User_ID);
if (user != null)
user = new MUser(ctx, user, trxName);
return user;
}
/**
* Get user via name and password
* @param ctx
* @param name
* @param password
* @return user
*/
public static MUser get (Properties ctx, String name, String password)
{
return MUser.get(ctx, name, password, false);
}
/**
* Get User via name and password
* @param ctx context
* @param name name
* @param password password
* @param isSSOLogin when isSSOLogin is true, password is ignored.
* @return user or null
*/
public static MUser get (Properties ctx, String name, String password, boolean isSSOLogin)
{
if (name == null || name.length() == 0 || (!isSSOLogin && (password == null || password.length() == 0)))
{
s_log.warning ("Invalid Name/Password = " + name);
return null;
}
boolean hash_password = MSysConfig.getBooleanValue(MSysConfig.USER_PASSWORD_HASH, false);
boolean email_login = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
ArrayList clientsValidated = new ArrayList();
MUser retValue = null;
StringBuilder where = new StringBuilder("Password IS NOT NULL AND ");
if (email_login)
where.append("EMail=?");
else
where.append("COALESCE(LDAPUser,Name)=?");
where.append(" AND")
.append(" EXISTS (SELECT * FROM AD_User_Roles ur")
.append(" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID)")
.append(" WHERE ur.AD_User_ID=AD_User.AD_User_ID AND ur.IsActive='Y' AND r.IsActive='Y') AND ")
.append(" EXISTS (SELECT * FROM AD_Client c")
.append(" WHERE c.AD_Client_ID=AD_User.AD_Client_ID")
.append(" AND c.IsActive='Y') AND ")
.append(" AD_User.IsActive='Y'");
List users = new Query(ctx, MUser.Table_Name, where.toString(), null)
.setParameters(name)
.setOrderBy("AD_Client_ID, AD_User_ID") // prefer first user on System
.list();
if (users.size() == 0) {
s_log.saveError("UserPwdError", name, false);
return null;
}
for (MUser user : users) {
if (clientsValidated.contains(user.getAD_Client_ID())) {
s_log.severe("Two users with password with the same name/email combination on same tenant: " + name);
return null;
}
clientsValidated.add(user.getAD_Client_ID());
boolean valid = false;
MSystem system = MSystem.get(Env.getCtx());
if (system == null)
throw new IllegalStateException("No System Info");
if (isSSOLogin) {
valid = true;
} else if (system.isLDAP() && ! Util.isEmpty(user.getLDAPUser())) {
valid = system.isLDAP(name, password);
} else if (hash_password) {
valid = user.authenticateHash(password);
} else {
// password not hashed
valid = user.getPassword().equals(password);
}
if (valid){
retValue=user;
break;
}
}
return retValue;
} // get
/**
* Get Name of AD_User
* @param AD_User_ID System User
* @return Name of user or ?
*/
public static String getNameOfUser (int AD_User_ID)
{
MUser user = get(Env.getCtx(), AD_User_ID);
if (user.getAD_User_ID() != AD_User_ID)
return "?";
return user.getName();
} // getNameOfUser
/**
* User is SalesRep
* @param AD_User_ID user
* @return true if sales rep
*/
public static boolean isSalesRep (int AD_User_ID)
{
if (AD_User_ID == 0)
return false;
String sql = "SELECT MAX(AD_User_ID) FROM AD_User u"
+ " INNER JOIN C_BPartner bp ON (u.C_BPartner_ID=bp.C_BPartner_ID) "
+ "WHERE bp.IsSalesRep='Y' AND AD_User_ID=?";
int no = DB.getSQLValue(null, sql, AD_User_ID);
return no == AD_User_ID;
} // isSalesRep
/**
* Get users with role assignment that is readable by current effective role.
* @param withEmptyElement if true, first element of the return array is an empty element with (-1,"")
* @param trxName optional transaction name
* @return user records (AD_User_ID, Name), order by name
*/
public static KeyNamePair[] getWithRoleKeyNamePairs(boolean withEmptyElement, String trxName)
{
//Internal Users
String sql = "SELECT AD_User_ID, Name "
+ "FROM AD_User u WHERE EXISTS "
+"(SELECT * FROM AD_User_Roles ur WHERE u.AD_User_ID=ur.AD_User_ID) "
+ "ORDER BY 2";
MRole role = MRole.getDefault();
sql = role.addAccessSQL(sql, "u", true, false);
return DB.getKeyNamePairsEx(trxName, sql, withEmptyElement);
}
/** Cache */
static private ImmutableIntPOCache s_cache = new ImmutableIntPOCache(Table_Name, 30, 60);
/** Static Logger */
private static CLogger s_log = CLogger.getCLogger (MUser.class);
/**
* UUID based Constructor
* @param ctx Context
* @param AD_User_UU UUID key
* @param trxName Transaction
*/
public MUser(Properties ctx, String AD_User_UU, String trxName) {
super(ctx, AD_User_UU, trxName);
if (Util.isEmpty(AD_User_UU))
setInitialDefaults();
}
/**
* Default Constructor
* @param ctx context
* @param AD_User_ID id
* @param trxName transaction
*/
public MUser (Properties ctx, int AD_User_ID, String trxName)
{
super (ctx, AD_User_ID, trxName); // 0 is also System
if (AD_User_ID == 0)
setInitialDefaults();
} // MUser
/**
* Set the initial defaults for a new record
*/
private void setInitialDefaults() {
setIsFullBPAccess (true);
setNotificationType(NOTIFICATIONTYPE_None);
}
/**
* Parent Constructor
* @param partner partner
*/
public MUser (X_C_BPartner partner)
{
this (partner.getCtx(), 0, partner.get_TrxName());
setClientOrg(partner);
setC_BPartner_ID (partner.getC_BPartner_ID());
setName(partner.getName());
} // MUser
/**
* Load Constructor
* @param ctx context
* @param rs current row of result set to be loaded
* @param trxName transaction
*/
public MUser (Properties ctx, ResultSet rs, String trxName)
{
super(ctx, rs, trxName);
} // MUser
/**
* Copy constructor
* @param copy
*/
public MUser(MUser copy)
{
this(Env.getCtx(), copy);
}
/**
* Copy constructor
* @param ctx
* @param copy
*/
public MUser(Properties ctx, MUser copy)
{
this(ctx, copy, (String) null);
}
/**
* Copy constructor
* @param ctx
* @param copy
* @param trxName
*/
public MUser(Properties ctx, MUser copy, String trxName)
{
this(ctx, 0, trxName);
copyPO(copy);
this.m_roles = copy.m_roles != null ? Arrays.stream(copy.m_roles).map(e ->{return new MRole(ctx, e, trxName);}).toArray(MRole[]::new) : null;
this.m_rolesAD_Org_ID = copy.m_rolesAD_Org_ID;
this.m_isAdministrator = copy.m_isAdministrator;
this.m_bpAccess = copy.m_bpAccess != null ? Arrays.copyOf(copy.m_bpAccess, copy.m_bpAccess.length) : null;
}
/** Roles of User with Org */
private MRole[] m_roles = null;
/** Roles of User with Org */
private int m_rolesAD_Org_ID = -1;
/** AD_Client_ID for m_roles above */
private int m_rolesAD_Client_ID = -1;
/** Is Administrator */
private Boolean m_isAdministrator = null;
/** User Access Rights */
private MUserBPAccess[] m_bpAccess = null;
/** Password Hashed **/
private boolean being_hashed = false;
/**
* Get Value
* @return value
*/
@Override
public String getValue()
{
String s = super.getValue();
if (s != null)
return s;
setValue(null);
return super.getValue();
} // getValue
/**
* Set Value - lower case alpha numerics and max length of 8.
* If Value is null, use LDAPUser or Name or the "noname" string instead.
* @param Value
*/
@Override
public void setValue(String Value)
{
if (Value == null || Value.trim().length () == 0)
Value = getLDAPUser();
if (Value == null || Value.length () == 0)
Value = getName();
if (Value == null || Value.length () == 0)
Value = "noname";
// lower case alpha numerics and max length of 8
String result = cleanValue(Value);
if (result.length() > 8)
{
String first = getName(Value, true);
String last = getName(Value, false);
if (last.length() > 0)
{
// Concatenate first character of first name and last name
String temp = last;
if (first.length() > 0)
temp = first.substring (0, 1) + last;
result = cleanValue(temp);
}
else
result = cleanValue(first);
}
// Truncate to 8 character
if (result.length() > 8)
result = result.substring (0, 8);
super.setValue(result);
} // setValue
/**
* Convert value to lower case and remove non-digit and non-alphabet character
* @param value
* @return lower case cleaned value
*/
private String cleanValue (String value)
{
char[] chars = value.toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chars.length; i++)
{
char ch = chars[i];
ch = Character.toLowerCase (ch);
if ((ch >= '0' && ch <= '9') // digits
|| (ch >= 'a' && ch <= 'z')) // characters
sb.append(ch);
}
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(MSysConfig.USER_PASSWORD_HASH, false);
if(!hash_password){
super.setPassword(password);
return;
}
if ( being_hashed )
return;
being_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) {
return SecureEngine.isMatchHash (getPassword(), getSalt(), password);
}
/**
* Get First Name
* @return first name
*/
public String getFirstName()
{
return getName (getName(), true);
} // getFirstName
/**
* Get Last Name
* @return first name
*/
public String getLastName()
{
return getName (getName(), false);
} // getLastName
/**
* Get First/Last Name
* @param name name
* @param getFirst if true first name is returned
* @return first/last name
*/
private String getName (String name, boolean getFirst)
{
if (name == null || name.length () == 0)
return "";
String first = null;
String last = null;
// Janke, Jorg R - Jorg R Janke
// double names not handled gracefully nor titles
// nor (former) aristrocratic world de/la/von
boolean lastFirst = name.indexOf(',') != -1;
StringTokenizer st = null;
if (lastFirst)
st = new StringTokenizer(name, ",");
else
st = new StringTokenizer(name, " ");
while (st.hasMoreTokens())
{
String s = st.nextToken().trim();
if (lastFirst)
{
if (last == null)
last = s;
else if (first == null)
first = s;
}
else
{
if (first == null)
first = s;
else
last = s;
}
}
if (getFirst)
{
if (first == null)
return "";
return first.trim();
}
if (last == null)
return "";
return last.trim();
} // getName
/**
* Add to Description
* @param description description to be added
*/
public void addDescription (String description)
{
if (description == null || description.length() == 0)
return;
String descr = getDescription();
if (descr == null || descr.length() == 0)
setDescription (description);
else
setDescription (descr + " - " + description);
} // addDescription
/**
* String Representation
* @return Info
*/
@Override
public String toString ()
{
StringBuilder sb = new StringBuilder ("MUser[")
.append(get_ID())
.append(",Name=").append(getName())
.append(",EMailUserID=").append(getEMailUser())
.append ("]");
return sb.toString ();
} // toString
/**
* Is it an Online Access User
* @return true if it has an email or password
*/
public boolean isOnline ()
{
if (getEMail() == null || getPassword() == null)
return false;
return true;
} // isOnline
/**
* Set EMail - reset email validation date
* @param EMail email
*/
public void setEMail(String EMail)
{
super.setEMail (EMail);
setEMailVerifyDate (null);
} // setEMail
/**
* Convert EMail to InternetAddress
* @return Valid Internet Address
*/
public InternetAddress getInternetAddress ()
{
String email = getEMail();
if (email == null || email.length() == 0)
return null;
try
{
InternetAddress ia = new InternetAddress (email, true);
return ia;
}
catch (AddressException ex)
{
log.warning(email + " - " + ex.getLocalizedMessage());
}
return null;
} // getInternetAddress
/**
* Is the email valid
* @return return true if email is valid (artificial check)
*/
public boolean isEMailValid()
{
return EMail.validate(getEMail());
} // isEMailValid
/**
* Could we send an email
* @return true if EMail User/PW exists
*/
public boolean isCanSendEMail()
{
String s = getEMailUser();
if (s == null || s.length() == 0)
return false;
// If SMTP authorization is not required, then don't check password - teo_sarca [ 1723309 ]
if (!MClient.get(getCtx()).isSmtpAuthorization())
return true;
s = getEMailUserPW();
return s != null && s.length() > 0;
} // isCanSendEMail
/**
* Get EMail Verification Code
* @return code
*/
public String getEMailVerifyCode()
{
long code = getAD_User_ID() + getName().hashCode();
return "C" + String.valueOf(Math.abs(code)) + "C";
} // getEMailValidationCode
/**
* Check and Set EMail Verification Code.
* @param code code
* @param info info
* @return true if valid
*/
public boolean setEMailVerifyCode (String code, String info)
{
boolean ok = code != null
&& code.equals(getEMailVerifyCode());
if (ok)
setEMailVerifyDate(new Timestamp(System.currentTimeMillis()));
else
setEMailVerifyDate(null);
setEMailVerify(info);
return ok;
} // setEMailVerifyCode
/**
* Is EMail Verified by response
* @return true if verified
*/
public boolean isEMailVerified()
{
// UPDATE AD_User SET EMailVerifyDate=SysDate, EMailVerify='Direct' WHERE AD_User_ID=1
return getEMailVerifyDate() != null
&& getEMailVerify() != null
&& getEMailVerify().length() > 0;
} // isEMailVerified
/**
* Is Notification via EMail
* @return true if use email for notification
*/
public boolean isNotificationEMail()
{
String s = getNotificationType();
return s == null || NOTIFICATIONTYPE_EMail.equals(s)
|| NOTIFICATIONTYPE_EMailPlusNotice.equals(s);
} // isNotificationEMail
/**
* Is Notification via Note
* @return true if use note for notification
*/
public boolean isNotificationNote()
{
String s = getNotificationType();
return s != null && (NOTIFICATIONTYPE_Notice.equals(s)
|| NOTIFICATIONTYPE_EMailPlusNotice.equals(s));
} // isNotificationNote
/**
* Get User Roles for Org
* @param AD_Org_ID org
* @return array of roles
*/
public MRole[] getRoles (int AD_Org_ID)
{
if (m_roles != null && m_rolesAD_Org_ID == AD_Org_ID && m_rolesAD_Client_ID == Env.getAD_Client_ID(Env.getCtx()))
return m_roles;
ArrayList list = new ArrayList();
// 2007-06-08, matthiasO.
// Extension of sql query so that not only roles with org access for this user
// are found but also roles which delegate org access to the user level where
// this user has access to the org in question
String sql = "SELECT * FROM AD_Role r "
+ "WHERE r.IsActive='Y' AND r.AD_Client_ID IN (0, ?) "
+ " AND EXISTS (SELECT * FROM AD_User_Roles ur"
+ " WHERE r.AD_Role_ID=ur.AD_Role_ID AND ur.IsActive='Y' AND ur.AD_User_ID=?) "
+ " AND ( ( r.isaccessallorgs = 'Y' ) OR "
+ " ("
+ " r.IsUseUserOrgAccess <> 'Y'"
+ " AND EXISTS (SELECT * FROM AD_Role_OrgAccess ro"
+ " WHERE r.AD_Role_ID=ro.AD_Role_ID AND ro.IsActive='Y' AND ro.AD_Org_ID=?)"
+ " ) OR "
+ " ("
+ " r.IsUseUserOrgAccess = 'Y'"
+ " AND EXISTS (SELECT * FROM AD_User_OrgAccess uo"
+ " WHERE uo.AD_User_ID=? AND uo.IsActive='Y' AND uo.AD_Org_ID=?)"
+ " )"
+ " ) "
+ "ORDER BY AD_Role_ID";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, get_TrxName());
pstmt.setInt (1, Env.getAD_Client_ID(Env.getCtx()));
pstmt.setInt (2, getAD_User_ID());
pstmt.setInt (3, AD_Org_ID);
pstmt.setInt (4, getAD_User_ID());
pstmt.setInt (5, AD_Org_ID);
rs = pstmt.executeQuery ();
while (rs.next ())
list.add (new MRole(Env.getCtx(), rs, get_TrxName()));
}
catch (Exception e)
{
log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
//
if (list.size() > 0 && is_Immutable())
list.stream().forEach(e -> e.markImmutable());
m_rolesAD_Org_ID = AD_Org_ID;
m_rolesAD_Client_ID = Env.getAD_Client_ID(Env.getCtx());
m_roles = new MRole[list.size()];
list.toArray (m_roles);
return m_roles;
} // getRoles
/**
* Is User an Administrator?
* @return true if user is Administrator
*/
public boolean isAdministrator()
{
if (m_isAdministrator == null)
{
m_isAdministrator = Boolean.FALSE;
MRole[] roles = getRoles(0);
for (int i = 0; i < roles.length; i++)
{
if (roles[i].getAD_Role_ID() == SystemIDs.ROLE_SYSTEM)
{
m_isAdministrator = Boolean.TRUE;
break;
}
}
}
return m_isAdministrator.booleanValue();
} // isAdministrator
/**
* User has access to URL form?
* @param url form class name
* @return true if user has access
*/
public boolean hasURLFormAccess(String url)
{
if (Util.isEmpty(url, true)) {
return false;
}
boolean hasAccess = false;
int formId = new Query(getCtx(), MForm.Table_Name, "ClassName=?", get_TrxName())
.setOnlyActiveRecords(true)
.setParameters(url)
.firstId();
if (formId > 0) {
for (MRole role : getRoles(0))
{
Boolean formAccess = role.getFormAccess(formId);
if (formAccess != null && formAccess.booleanValue()) {
hasAccess = true;
break;
}
}
}
return hasAccess;
} // hasURLFormAccess
/**
* Is user has access to BP info and resources
* @param BPAccessType access type
* @param params opt parameter
* @return true if access
*/
public boolean hasBPAccess (String BPAccessType, Object[] params)
{
if (isFullBPAccess())
return true;
getBPAccess(false);
for (int i = 0; i < m_bpAccess.length; i++)
{
if (m_bpAccess[i].getBPAccessType().equals(BPAccessType))
{
return true;
}
}
return false;
} // hasBPAccess
/**
* Get active BP Access records
* @param requery requery
* @return access list
*/
public MUserBPAccess[] getBPAccess (boolean requery)
{
if (m_bpAccess != null && !requery)
return m_bpAccess;
String sql = "SELECT * FROM AD_UserBPAccess WHERE AD_User_ID=? AND IsActive='Y'";
ArrayList list = new ArrayList();
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, getAD_User_ID());
rs = pstmt.executeQuery ();
while (rs.next ())
{
list.add (new MUserBPAccess (getCtx(), rs, null));
}
}
catch (Exception e)
{
log.log (Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
}
if (list.size() > 0 && is_Immutable())
list.stream().forEach(e -> e.markImmutable());
m_bpAccess = new MUserBPAccess[list.size ()];
list.toArray (m_bpAccess);
return m_bpAccess;
} // getBPAccess
@Override
protected boolean beforeSave (boolean newRecord)
{
// New Email Address invalidates previous email verification
if (!newRecord && is_ValueChanged("EMail"))
setEMailVerifyDate(null);
// IDEMPIERE-1409 Validate Email
if (!Util.isEmpty(getEMail()) && (newRecord || is_ValueChanged("EMail"))) {
if (! isEMailValid()) {
log.saveError("SaveError", Msg.getMsg(getCtx(), "InvalidEMailFormat") + Msg.getElement(getCtx(), COLUMNNAME_EMail) + " - [" + getEMail() + "]");
return false;
}
}
if (newRecord || super.getValue() == null || is_ValueChanged("Value"))
setValue(super.getValue());
if (getPassword() != null && getPassword().length() > 0) {
boolean email_login = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
if (email_login) {
// email is mandatory for users with password
if (getEMail() == null || getEMail().length() == 0) {
log.saveError("SaveError", Msg.getMsg(getCtx(), "FillMandatory") + Msg.getElement(getCtx(), COLUMNNAME_EMail) + " - " + toString());
return false;
}
// email with password must be unique on the same tenant
int cnt = DB.getSQLValue(get_TrxName(),
"SELECT COUNT(*) FROM AD_User WHERE Password IS NOT NULL AND EMail=? AND AD_Client_ID=? AND AD_User_ID!=?",
getEMail(), getAD_Client_ID(), getAD_User_ID());
if (cnt > 0) {
log.saveError("SaveError", Msg.getMsg(getCtx(), DBException.SAVE_ERROR_NOT_UNIQUE_MSG, true) + Msg.getElement(getCtx(), COLUMNNAME_EMail));
return false;
}
} else {
// IDEMPIERE-1672 check duplicate name in client
String nameToValidate = getLDAPUser();
if (Util.isEmpty(nameToValidate))
nameToValidate = getName();
int cnt = DB.getSQLValue(get_TrxName(),
"SELECT COUNT(*) FROM AD_User WHERE Password IS NOT NULL AND COALESCE(LDAPUser,Name)=? AND AD_Client_ID=? AND AD_User_ID!=?",
nameToValidate, getAD_Client_ID(), getAD_User_ID());
if (cnt > 0) {
log.saveError("SaveError", Msg.getMsg(getCtx(), DBException.SAVE_ERROR_NOT_UNIQUE_MSG, true) + Msg.getElement(getCtx(), COLUMNNAME_Name) + " / " + Msg.getElement(getCtx(), COLUMNNAME_LDAPUser));
return false;
}
}
}
boolean hasPassword = ! Util.isEmpty(getPassword());
if (hasPassword && (newRecord || is_ValueChanged("Password"))) {
// Validate password policies / IDEMPIERE-221
if (! (get_ValueOld("Salt") == null && get_Value("Salt") != null)) { // not being hashed
MPasswordRule pwdrule = MPasswordRule.getRules(getCtx(), get_TrxName());
if (pwdrule != null){
List passwordHistorys = MPasswordHistory.getPasswordHistoryForCheck(pwdrule.getDays_Reuse_Password(), this.getAD_User_ID());
pwdrule.validate((getLDAPUser() != null ? getLDAPUser() : getName()), getPassword(), passwordHistorys);
}
setDatePasswordChanged(new Timestamp(new Date().getTime()));
}
}
boolean hash_password = MSysConfig.getBooleanValue(MSysConfig.USER_PASSWORD_HASH, false);
if ( hasPassword
&& is_ValueChanged("Password")
&& (!newRecord || (hash_password && getSalt() == null))) {
// Hash password - IDEMPIERE-347
if (hash_password)
setPassword(getPassword());
}
return true;
} // beforeSave
/**
* Is Menu Auto Expand - user preference.
* Check if the user has a preference, otherwise use the value from current role.
* @return true if application menu should auto expand
*/
public boolean isMenuAutoExpand() {
boolean isMenuAutoExpand = false;
if (getIsMenuAutoExpand() != null)
isMenuAutoExpand = ISMENUAUTOEXPAND_Yes.equals(getIsMenuAutoExpand());
else
isMenuAutoExpand = MRole.getDefault().isMenuAutoExpand();
return isMenuAutoExpand;
}
/**
* Get User that has roles (already authenticated)
* @param ctx context
* @param name name
* @return user or null
*/
public static MUser get(Properties ctx, String name) {
if (name == null || name.length() == 0)
{
s_log.warning ("Invalid Name = " + name);
return null;
}
MUser retValue = null;
int AD_Client_ID = Env.getAD_Client_ID(ctx);
StringBuilder sql = new StringBuilder("SELECT DISTINCT u.AD_User_ID ")
.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 u.Password IS NOT NULL AND ur.AD_Client_ID=? AND "); // #1/2
boolean email_login = MSysConfig.getBooleanValue(MSysConfig.USE_EMAIL_FOR_LOGIN, false);
if (email_login)
sql.append("u.EMail=?");
else
sql.append("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')");
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql.toString(), null);
pstmt.setInt(1, AD_Client_ID);
pstmt.setString (2, name);
rs = pstmt.executeQuery ();
if (rs.next())
{
retValue = MUser.getCopy(ctx, rs.getInt(1), (String)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.toString(), e);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
return retValue;
}
@Override
public String getEMailUser() {
// IDEMPIERE-722
if (MClient.isSendCredentialsSystem()) {
MClient sysclient = MClient.get(getCtx(), 0);
return sysclient.getRequestUser();
} else if (MClient.isSendCredentialsClient()) {
MClient client = MClient.get(getCtx());
return client.getRequestUser();
} else {
return super.getEMailUser();
}
}
@Override
public String getEMailUserPW() {
// IDEMPIERE-722
if (MClient.isSendCredentialsSystem()) {
MClient sysclient = MClient.get(getCtx(), 0);
return sysclient.getRequestUserPW();
} else if (MClient.isSendCredentialsClient()) {
MClient client = MClient.get(getCtx());
return client.getRequestUserPW();
} else {
return super.getEMailUserPW();
}
}
@Override
protected boolean afterSave(boolean newRecord, boolean success) {
if (getPassword() != null && getPassword().length() > 0 && (newRecord || is_ValueChanged("Password"))) {
// Save password history for password reuse rule
MPasswordRule pwdrule = MPasswordRule.getRules(getCtx(), get_TrxName());
if (pwdrule != null && pwdrule.getDays_Reuse_Password() > 0) {
boolean hash_password = MSysConfig.getBooleanValue(MSysConfig.USER_PASSWORD_HASH, false);
if (! hash_password) {
log.severe("Saving password history: it is strongly encouraged to save password history just when using hashed passwords - WARNING! table AD_Password_History is possibly keeping plain passwords");
}
MPasswordHistory passwordHistory = new MPasswordHistory(this.getCtx(), 0, this.get_TrxName());
passwordHistory.setSalt(this.getSalt());
passwordHistory.setPassword(this.getPassword());
passwordHistory.setAD_User_ID(this.getAD_User_ID());
passwordHistory.setDatePasswordChanged(this.getUpdated());
passwordHistory.saveEx();
}
}
return super.afterSave(newRecord, success);
}
@Override
protected boolean postDelete() {
if (getAD_Image_ID() > 0) {
MImage img = new MImage(getCtx(), getAD_Image_ID(), get_TrxName());
if (!img.delete(true)) {
log.warning("Associated image could not be deleted for user - AD_Image_ID=" + getAD_Image_ID());
return false;
}
}
return true;
}
@Override
public MUser markImmutable() {
if (is_Immutable())
return this;
makeImmutable();
if (m_roles != null && m_roles.length > 0)
Arrays.stream(m_roles).forEach(e -> e.markImmutable());
if (m_bpAccess != null && m_bpAccess.length > 0)
Arrays.stream(m_bpAccess).forEach(e -> e.markImmutable());
return this;
}
} // MUser