package org.exist.security;
import org.apache.log4j.Logger;
import org.exist.EXistException;
import org.exist.storage.BrokerPool;
import org.exist.util.DatabaseConfigurationException;
import org.exist.xmldb.XmldbURI;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.util.Properties;
/**
* Represents a user within the database.
*
*@author Wolfgang Meier <wolfgang@exist-db.org>
* Modified by {Marco.Tampucci, Massimo.Martinelli} @isti.cnr.it
*/
public class User {
private final static Logger LOG = Logger.getLogger(User.class);
public final static User DEFAULT =
new User( "guest", null, "guest" );
private final static String GROUP = "group";
private final static String NAME = "name";
private final static String PASS = "password";
private final static String DIGEST_PASS = "digest-password";
private final static String USER_ID = "uid";
private final static String HOME = "home";
private static String realm = "exist";
public final static int PLAIN_ENCODING = 0;
public final static int SIMPLE_MD5_ENCODING = 1;
public final static int MD5_ENCODING = 2;
public static int PASSWORD_ENCODING;
public static boolean CHECK_PASSWORDS = true;
static {
Properties props = new Properties();
try {
props.load(
User.class.getClassLoader().getResourceAsStream("org/exist/security/security.properties")
);
} catch (IOException e) {
}
String option = props.getProperty("passwords.encoding", "md5");
setPasswordEncoding(option);
option = props.getProperty("passwords.check", "yes");
CHECK_PASSWORDS = option.equalsIgnoreCase("yes") || option.equalsIgnoreCase("true");
}
static public void enablePasswordChecks(boolean check) {
CHECK_PASSWORDS = check;
}
static public void setPasswordEncoding(String encoding) {
if (encoding != null) {
LOG.equals("Setting password encoding to "+encoding);
if (encoding.equalsIgnoreCase("plain")) {
PASSWORD_ENCODING = PLAIN_ENCODING;
} else if (encoding.equalsIgnoreCase("md5")) {
PASSWORD_ENCODING = MD5_ENCODING;
} else {
PASSWORD_ENCODING = SIMPLE_MD5_ENCODING;
}
}
}
static public void setPasswordRealm(String value) {
realm = value;
}
private String[] groups = null;
private String password = null;
private String digestPassword = null;
private String user;
private int uid = -1;
private XmldbURI home = null;
/**
* Indicates if the user belongs to the dba group,
* i.e. is a superuser.
*/
private boolean hasDbaRole = false;
/**
* Create a new user with name and password
*
*@param user Description of the Parameter
*@param password Description of the Parameter
*/
public User( String user, String password ) {
this.user = user;
setPassword( password );
}
/**
* Create a new user with name
*
*@param user Description of the Parameter
*/
public User( String user ) {
this.user = user;
}
/**
* Create a new user with name, password and primary group
*
*@param user Description of the Parameter
*@param password Description of the Parameter
*@param primaryGroup Description of the Parameter
*/
public User( String user, String password, String primaryGroup ) {
this( user, password );
addGroup( primaryGroup );
}
/**
* Read a new user from the given DOM node
*
*@param node Description of the Parameter
*@exception DatabaseConfigurationException Description of the Exception
*/
public User( int majorVersion, int minorVersion,Element node ) throws DatabaseConfigurationException {
this.user = node.getAttribute( NAME );
if ( user == null || user.length() == 0)
throw new DatabaseConfigurationException( "user needs a name" );
Attr attr;
if (majorVersion==0) {
attr = node.getAttributeNode(PASS);
this.digestPassword = attr == null ? null : attr.getValue();
this.password = null;
} else {
attr = node.getAttributeNode(PASS);
this.password = attr == null ? null : attr.getValue();
if (this.password!=null && this.password.length() > 0) {
if (this.password.startsWith("{MD5}")) {
this.password = this.password.substring(5);
}
if (this.password.charAt(0)=='{') {
throw new DatabaseConfigurationException("Unrecognized password encoding "+password+" for user "+user);
}
}
attr = node.getAttributeNode(DIGEST_PASS);
this.digestPassword = attr == null ? null : attr.getValue();
}
Attr userId = node.getAttributeNode( USER_ID );
if(userId == null)
throw new DatabaseConfigurationException("attribute id missing");
try {
uid = Integer.parseInt(userId.getValue());
} catch(NumberFormatException e) {
throw new DatabaseConfigurationException("illegal user id: " +
userId + " for user " + user);
}
Attr homeAttr = node.getAttributeNode( HOME );
this.home = homeAttr == null ? null : XmldbURI.create(homeAttr.getValue());
NodeList gl = node.getChildNodes();
Node group;
for ( int i = 0; i < gl.getLength(); i++ ) {
group = gl.item( i );
if(group.getNodeType() == Node.ELEMENT_NODE &&
group.getLocalName().equals(GROUP))
addGroup( group.getFirstChild().getNodeValue() );
}
}
/**
* Add the user to a group
*
*@param group The feature to be added to the Group attribute
*/
public final void addGroup( String group ) {
if (groups == null) {
groups = new String[1];
groups[0] = group;
} else {
int len = groups.length;
String[] ngroups = new String[len + 1];
System.arraycopy(groups, 0, ngroups, 0, len);
ngroups[len] = group;
groups = ngroups;
}
if (SecurityManager.DBA_GROUP.equals(group))
hasDbaRole = true;
}
/**
* Remove the user to a group
* Added by {Marco.Tampucci and Massimo.Martinelli}@isti.cnr.it
*
*@param group The feature to be removed to the Group attribute
*/
public final void remGroup( String group ) {
if (groups == null) {
groups = new String[1];
groups[0] = "guest";
} else {
int len = groups.length;
String[] rgroup = null;
if (len>1)
rgroup = new String[len-1];
else {
rgroup = new String[1];
len=1;
}
boolean found = false;
for (int i=0; i<len; i++) {
if (!groups[i].equals(group)) {
if (found == true)
rgroup[i-1] = groups[i];
else
rgroup[i] = groups[i];
}
else {
found = true;
}
}
if (found == true && len==1)
rgroup[0] = "guest";
groups=rgroup;
}
if (SecurityManager.DBA_GROUP.equals(group))
hasDbaRole = false;
}
public final void setGroups(String[] groups) {
this.groups = groups;
for (int i = 0; i < groups.length; i++)
if (SecurityManager.DBA_GROUP.equals(groups[i]))
hasDbaRole = true;
}
/**
* Get all groups this user belongs to
*
*@return The groups value
*/
public final String[] getGroups() {
return groups == null ? new String[0] : groups;
}
public final boolean hasDbaRole() {
return hasDbaRole;
}
/**
* Get the user name
*
*@return The user value
*/
public final String getName() {
return user;
}
public final int getUID() {
return uid;
}
/**
* Get the user's password
*
*@return Description of the Return Value
*/
public final String getPassword() {
return password;
}
public final String getDigestPassword() {
return digestPassword;
}
/**
* Get the primary group this user belongs to
*
*@return The primaryGroup value
*/
public final String getPrimaryGroup() {
if ( groups == null || groups.length == 0 )
return null;
return groups[0];
}
/**
* Is the user a member of group?
*
*@param group Description of the Parameter
*@return Description of the Return Value
*/
public final boolean hasGroup( String group ) {
if (groups == null)
return false;
for (int i = 0; i < groups.length; i++) {
if (groups[i].equals(group))
return true;
}
return false;
}
/**
* Sets the password attribute of the User object
*
*@param passwd The new password value
*/
public final void setPassword( String passwd ) {
if (passwd==null) {
this.password = null;
this.digestPassword = null;
} else {
this.password = MessageDigester.md5(passwd,true);
this.digestPassword = digest(passwd);
}
}
/**
* Sets the digest passwod value of the User object
*
*@param passwd The new passwordDigest value
*/
public final void setPasswordDigest( String passwd ) {
this.digestPassword = ( passwd == null ) ? null : passwd;
}
/**
* Sets the encoded passwod value of the User object
*
*@param passwd The new passwordDigest value
*/
public final void setEncodedPassword( String passwd ) {
this.password = ( passwd == null ) ? null : passwd;
}
public final String digest(String passwd) {
switch(PASSWORD_ENCODING) {
case PLAIN_ENCODING:
return passwd;
case MD5_ENCODING:
return MessageDigester.md5(user + ":"+realm+":" + passwd,false);
default:
return MessageDigester.md5(passwd,true);
}
}
public final String toString() {
StringBuffer buf = new StringBuffer();
buf.append( "<user name=\"" );
buf.append( user );
buf.append( "\" " );
buf.append( "uid=\"");
buf.append( Integer.toString(uid) );
buf.append( "\"" );
if ( password != null ) {
buf.append( " password=\"{MD5}" );
buf.append( password );
buf.append( '"' );
}
if (digestPassword!=null) {
buf.append( " digest-password=\"" );
buf.append( digestPassword );
buf.append( '"' );
}
if( home != null ) {
buf.append(" home=\"" );
buf.append(home);
buf.append("\">");
} else
buf.append(">");
if (groups != null) {
for (int i = 0; i < groups.length; i++) {
buf.append( "<group>" );
buf.append( groups[i] );
buf.append( "</group>" );
}
}
buf.append( "</user>" );
return buf.toString();
}
/**
* Split up the validate method into two, to make
* it possible to authenticate users, which are not
* defined in the instance named "exist" without
* having impact on the standard functionality.
*
* @param passwd
* @return true if the password was correct, false if not,
* or if there was a problem.
*/
public final boolean validate( String passwd ) {
SecurityManager sm;
try {
sm=BrokerPool.getInstance().getSecurityManager();
return validate(passwd, sm);
} catch (EXistException e) {
LOG.warn("Failed to get security manager in validate: ",e);
return false;
}
}
public final boolean validate( String passwd, SecurityManager sm ) {
// security management is disabled if in repair mode
if (!CHECK_PASSWORDS)
return true;
if (password==null && digestPassword==null) {
return true;
}
if ( passwd == null ) {
return false;
}
// [ 1557095 ] LDAP passwords patch
//Try to authenticate using LDAP
if(sm != null) {
if(sm instanceof LDAPbindSecurityManager ) {
if( ((LDAPbindSecurityManager)sm).bind(user,passwd))
return true;
else
return false;
}
}
if (password!=null) {
if (MessageDigester.md5(passwd,true).equals( password )) {
return true;
}
}
if (digestPassword!=null) {
if (digest( passwd ).equals( digestPassword )) {
return true;
}
}
return false;
}
public final boolean validateDigest( String passwd ) {
if ( digestPassword == null )
return true;
if ( passwd == null )
return false;
return digest( passwd ).equals( digestPassword );
}
public void setUID(int uid) {
this.uid = uid;
}
public void setHome(XmldbURI homeCollection) {
home = homeCollection;
}
public XmldbURI getHome() {
return home;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
User other = (User)obj;
if(other != null)
{
return uid == other.uid;
}
else
{
return(false);
}
}
}