/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev$
* Last modified by $Author$
* $Date$
*/
package tigase.db.jdbc;
//~--- non-JDK imports --------------------------------------------------------
import tigase.db.AuthorizationException;
import tigase.db.DBInitException;
import tigase.db.DataRepository;
import tigase.db.RepositoryFactory;
import tigase.db.TigaseDBException;
import tigase.db.AuthRepository;
import tigase.db.UserExistsException;
import tigase.db.UserNotFoundException;
import tigase.util.Base64;
import tigase.util.TigaseStringprepException;
import tigase.xmpp.BareJID;
import static tigase.db.AuthRepository.*;
//~--- JDK imports ------------------------------------------------------------
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.Statement;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
//~--- classes ----------------------------------------------------------------
/**
* Describe class TigaseAuth here.
*
*
* Created: Sat Nov 11 22:22:04 2006
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev$
*/
public class TigaseAuth implements AuthRepository {
/**
* Private logger for class instances.
*/
private static final Logger log = Logger.getLogger("tigase.db.jdbc.TigaseAuth");
private static final String[] non_sasl_mechs = { "password" };
private static final String[] sasl_mechs = { "PLAIN" };
private static final String INIT_DB_QUERY = "{ call TigInitdb() }";
private static final String ADD_USER_PLAIN_PW_QUERY = "{ call TigAddUserPlainPw(?, ?) }";
private static final String REMOVE_USER_QUERY = "{ call TigRemoveUser(?) }";
private static final String GET_PASSWORD_QUERY = "{ call TigGetPassword(?) }";
private static final String UPDATE_PASSWORD_PLAIN_PW_QUERY =
"{ call TigUpdatePasswordPlainPw(?, ?) }";
private static final String USER_LOGIN_PLAIN_PW_QUERY = "{ call TigUserLoginPlainPw(?, ?) }";
private static final String USER_LOGOUT_QUERY = "{ call TigUserLogout(?) }";
private static final String USERS_COUNT_QUERY = "{ call TigAllUsersCount() }";
private static final String USERS_DOMAIN_COUNT_QUERY =
"select count(*) from tig_users where user_id like ?";
//~--- fields ---------------------------------------------------------------
private DataRepository data_repo = null;
//~--- methods --------------------------------------------------------------
/**
* Describe <code>addUser</code> method here.
*
* @param user a <code>String</code> value
* @param password a <code>String</code> value
* @exception UserExistsException if an error occurs
* @exception TigaseDBException if an error occurs
*/
@Override
public void addUser(BareJID user, final String password)
throws UserExistsException, TigaseDBException {
ResultSet rs = null;
try {
PreparedStatement add_user_plain_pw_sp =
data_repo.getPreparedStatement(user, ADD_USER_PLAIN_PW_QUERY);
synchronized (add_user_plain_pw_sp) {
add_user_plain_pw_sp.setString(1, user.toString());
add_user_plain_pw_sp.setString(2, password);
rs = add_user_plain_pw_sp.executeQuery();
}
} catch (SQLIntegrityConstraintViolationException e) {
throw new UserExistsException("Error while adding user to repository, user exists?", e);
} catch (SQLException e) {
throw new TigaseDBException("Problem accessing repository.", e);
} finally {
data_repo.release(null, rs);
}
}
/**
* Describe <code>digestAuth</code> method here.
*
* @param user a <code>String</code> value
* @param digest a <code>String</code> value
* @param id a <code>String</code> value
* @param alg a <code>String</code> value
* @return a <code>boolean</code> value
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
* @exception AuthorizationException if an error occurs
*/
@Override
@Deprecated
public boolean digestAuth(BareJID user, final String digest, final String id, final String alg)
throws UserNotFoundException, TigaseDBException, AuthorizationException {
throw new AuthorizationException("Not supported.");
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public String getResourceUri() {
return data_repo.getResourceUri();
}
/**
* <code>getUsersCount</code> method is thread safe. It uses local variable
* for storing <code>Statement</code>.
*
* @return a <code>long</code> number of user accounts in database.
*/
@Override
public long getUsersCount() {
ResultSet rs = null;
try {
long users = -1;
PreparedStatement users_count_sp = data_repo.getPreparedStatement(null, USERS_COUNT_QUERY);
synchronized (users_count_sp) {
// Load all user count from database
rs = users_count_sp.executeQuery();
if (rs.next()) {
users = rs.getLong(1);
} // end of while (rs.next())
}
return users;
} catch (SQLException e) {
return -1;
// throw new TigaseDBException("Problem loading user list from repository", e);
} finally {
data_repo.release(null, rs);
rs = null;
}
}
/**
* Method description
*
*
* @param domain
*
* @return
*/
@Override
public long getUsersCount(String domain) {
ResultSet rs = null;
try {
long users = -1;
PreparedStatement users_domain_count_st =
data_repo.getPreparedStatement(null, USERS_DOMAIN_COUNT_QUERY);
synchronized (users_domain_count_st) {
// Load all user count from database
users_domain_count_st.setString(1, "%@" + domain);
rs = users_domain_count_st.executeQuery();
if (rs.next()) {
users = rs.getLong(1);
} // end of while (rs.next())
}
return users;
} catch (SQLException e) {
return -1;
// throw new TigaseDBException("Problem loading user list from repository", e);
} finally {
data_repo.release(null, rs);
rs = null;
}
}
//~--- methods --------------------------------------------------------------
/**
* Describe <code>initRepository</code> method here.
*
* @param connection_str a <code>String</code> value
* @param params
* @exception DBInitException if an error occurs
*/
@Override
public void initRepository(final String connection_str, Map<String, String> params)
throws DBInitException {
try {
data_repo = RepositoryFactory.getDataRepository(null, connection_str, params);
data_repo.initPreparedStatement(INIT_DB_QUERY, INIT_DB_QUERY);
data_repo.initPreparedStatement(ADD_USER_PLAIN_PW_QUERY, ADD_USER_PLAIN_PW_QUERY);
data_repo.initPreparedStatement(REMOVE_USER_QUERY, REMOVE_USER_QUERY);
data_repo.initPreparedStatement(GET_PASSWORD_QUERY, GET_PASSWORD_QUERY);
data_repo.initPreparedStatement(UPDATE_PASSWORD_PLAIN_PW_QUERY,
UPDATE_PASSWORD_PLAIN_PW_QUERY);
data_repo.initPreparedStatement(USER_LOGIN_PLAIN_PW_QUERY, USER_LOGIN_PLAIN_PW_QUERY);
data_repo.initPreparedStatement(USER_LOGOUT_QUERY, USER_LOGOUT_QUERY);
data_repo.initPreparedStatement(USERS_COUNT_QUERY, USERS_COUNT_QUERY);
data_repo.initPreparedStatement(USERS_DOMAIN_COUNT_QUERY, USERS_DOMAIN_COUNT_QUERY);
if ((params != null) && (params.get("init-db") != null)) {
data_repo.getPreparedStatement(null, INIT_DB_QUERY).executeQuery();
}
} catch (Exception e) {
data_repo = null;
throw new DBInitException("Problem initializing jdbc connection: " + connection_str, e);
}
}
/**
* Method description
*
*
* @param user
*
* @throws TigaseDBException
* @throws UserNotFoundException
*/
@Override
public void logout(BareJID user) throws UserNotFoundException, TigaseDBException {
try {
PreparedStatement user_logout_sp = data_repo.getPreparedStatement(user, USER_LOGOUT_QUERY);
synchronized (user_logout_sp) {
user_logout_sp.setString(1, user.toString());
user_logout_sp.execute();
}
} catch (SQLException e) {
throw new TigaseDBException("Problem accessing repository.", e);
}
}
/**
* Describe <code>otherAuth</code> method here.
*
* @param props a <code>Map</code> value
* @return a <code>boolean</code> value
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
* @exception AuthorizationException if an error occurs
*/
@Override
public boolean otherAuth(final Map<String, Object> props)
throws UserNotFoundException, TigaseDBException, AuthorizationException {
String proto = (String) props.get(PROTOCOL_KEY);
if (proto.equals(PROTOCOL_VAL_SASL)) {
String mech = (String) props.get(MACHANISM_KEY);
if (mech.equals("PLAIN")) {
try {
return saslAuth(props);
} catch (TigaseStringprepException ex) {
throw new AuthorizationException("Stringprep failed for: " + props, ex);
}
} // end of if (mech.equals("PLAIN"))
throw new AuthorizationException("Mechanism is not supported: " + mech);
} // end of if (proto.equals(PROTOCOL_VAL_SASL))
if (proto.equals(PROTOCOL_VAL_NONSASL)) {
String password = (String) props.get(PASSWORD_KEY);
BareJID user_id = (BareJID) props.get(USER_ID_KEY);
if (password != null) {
return plainAuth(user_id, password);
}
String digest = (String) props.get(DIGEST_KEY);
if (digest != null) {
String digest_id = (String) props.get(DIGEST_ID_KEY);
return digestAuth(user_id, digest, digest_id, "SHA");
}
} // end of if (proto.equals(PROTOCOL_VAL_SASL))
throw new AuthorizationException("Protocol is not supported: " + proto);
}
/**
* Describe <code>plainAuth</code> method here.
*
* @param user a <code>String</code> value
* @param password a <code>String</code> value
* @return a <code>boolean</code> value
*
* @throws AuthorizationException
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
*/
@Override
@Deprecated
public boolean plainAuth(BareJID user, final String password)
throws UserNotFoundException, TigaseDBException, AuthorizationException {
ResultSet rs = null;
String res_string = null;
try {
PreparedStatement user_login_plain_pw_sp =
data_repo.getPreparedStatement(user, USER_LOGIN_PLAIN_PW_QUERY);
synchronized (user_login_plain_pw_sp) {
// String user_id = BareJID.jidToBareJID(user);
user_login_plain_pw_sp.setString(1, user.toString());
user_login_plain_pw_sp.setString(2, password);
rs = user_login_plain_pw_sp.executeQuery();
boolean auth_result_ok = false;
if (rs.next()) {
res_string = rs.getString(1);
if (res_string != null) {
BareJID result = BareJID.bareJIDInstance(res_string);
auth_result_ok = user.equals(result);
}
if (auth_result_ok) {
return true;
} else {
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE,
"Login failed, for user: ''{0}" + "''" + ", password: ''" + "{1}" + "''"
+ ", from DB got: " + "{2}", new Object[] { user,
password, res_string });
}
}
}
throw new UserNotFoundException("User does not exist: " + user);
}
} catch (TigaseStringprepException ex) {
throw new AuthorizationException("Stringprep failed for: " + res_string, ex);
} catch (SQLException e) {
throw new TigaseDBException("Problem accessing repository.", e);
} finally {
data_repo.release(null, rs);
} // end of catch
}
// Implementation of tigase.db.AuthRepository
/**
* Describe <code>queryAuth</code> method here.
*
* @param authProps a <code>Map</code> value
*/
@Override
public void queryAuth(final Map<String, Object> authProps) {
String protocol = (String) authProps.get(PROTOCOL_KEY);
if (protocol.equals(PROTOCOL_VAL_NONSASL)) {
authProps.put(RESULT_KEY, non_sasl_mechs);
} // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))
if (protocol.equals(PROTOCOL_VAL_SASL)) {
authProps.put(RESULT_KEY, sasl_mechs);
} // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))
}
/**
* Describe <code>removeUser</code> method here.
*
* @param user a <code>String</code> value
* @exception UserNotFoundException if an error occurs
* @exception TigaseDBException if an error occurs
*/
@Override
public void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {
try {
PreparedStatement remove_user_sp = data_repo.getPreparedStatement(user, REMOVE_USER_QUERY);
synchronized (remove_user_sp) {
remove_user_sp.setString(1, user.toString());
remove_user_sp.execute();
}
} catch (SQLException e) {
throw new TigaseDBException("Problem accessing repository.", e);
}
}
/**
* Describe <code>updatePassword</code> method here.
*
* @param user a <code>String</code> value
* @param password a <code>String</code> value
* @throws UserNotFoundException
* @exception TigaseDBException if an error occurs
*/
@Override
public void updatePassword(BareJID user, final String password)
throws UserNotFoundException, TigaseDBException {
try {
PreparedStatement update_pass_plain_pw_sp =
data_repo.getPreparedStatement(user, UPDATE_PASSWORD_PLAIN_PW_QUERY);
synchronized (update_pass_plain_pw_sp) {
update_pass_plain_pw_sp.setString(1, user.toString());
update_pass_plain_pw_sp.setString(2, password);
update_pass_plain_pw_sp.execute();
}
} catch (SQLException e) {
throw new TigaseDBException("Problem accessing repository.", e);
}
}
//~--- get methods ----------------------------------------------------------
private String getPassword(BareJID user) throws SQLException, UserNotFoundException {
ResultSet rs = null;
try {
PreparedStatement get_pass_sp = data_repo.getPreparedStatement(user, GET_PASSWORD_QUERY);
synchronized (get_pass_sp) {
get_pass_sp.setString(1, user.toString());
rs = get_pass_sp.executeQuery();
if (rs.next()) {
return rs.getString(1);
} else {
throw new UserNotFoundException("User does not exist: " + user);
} // end of if (isnext) else
}
} finally {
data_repo.release(null, rs);
}
}
//~--- methods --------------------------------------------------------------
private boolean saslAuth(final Map<String, Object> props)
throws UserNotFoundException, TigaseDBException, AuthorizationException,
TigaseStringprepException {
String data_str = (String) props.get(DATA_KEY);
String domain = (String) props.get(REALM_KEY);
props.put(RESULT_KEY, null);
byte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);
int auth_idx = 0;
while ((in_data[auth_idx] != 0) && (auth_idx < in_data.length)) {
++auth_idx;
}
String authoriz = new String(in_data, 0, auth_idx);
int user_idx = ++auth_idx;
while ((in_data[user_idx] != 0) && (user_idx < in_data.length)) {
++user_idx;
}
String user_name = new String(in_data, auth_idx, user_idx - auth_idx);
++user_idx;
BareJID jid = null;
if (BareJID.parseJID(user_name)[0] == null) {
jid = BareJID.bareJIDInstance(user_name, domain);
} else {
jid = BareJID.bareJIDInstance(user_name);
}
props.put(USER_ID_KEY, jid);
String passwd = new String(in_data, user_idx, in_data.length - user_idx);
return plainAuth(jid, passwd);
}
} // TigaseAuth
//~ Formatted in Sun Code Convention
//~ Formatted by Jindent --- http://www.jindent.com