/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://opensource.org/licenses/cddl1.php
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at http://opensource.org/licenses/cddl1.php.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
*/
package org.identityconnectors.mysqluser;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_ACCOUNT_OBJECT_CLASS_REQUIRED;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_AUTH_FAILED;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_INVALID_ATTRIBUTE_SET;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_NAME_BLANK;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_PWD_BLANK;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_UID_BLANK;
import static org.identityconnectors.mysqluser.MySQLUserConstants.MSG_USER_MODEL_NOT_FOUND;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.dbcommon.DatabaseQueryBuilder;
import org.identityconnectors.dbcommon.FilterWhereBuilder;
import org.identityconnectors.dbcommon.SQLParam;
import org.identityconnectors.dbcommon.SQLUtil;
import org.identityconnectors.framework.common.exceptions.AlreadyExistsException;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.exceptions.InvalidCredentialException;
import org.identityconnectors.framework.common.exceptions.UnknownUidException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.OperationalAttributeInfos;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.SchemaBuilder;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.ConnectorClass;
import org.identityconnectors.framework.spi.PoolableConnector;
import org.identityconnectors.framework.spi.operations.AuthenticateOp;
import org.identityconnectors.framework.spi.operations.CreateOp;
import org.identityconnectors.framework.spi.operations.DeleteOp;
import org.identityconnectors.framework.spi.operations.ResolveUsernameOp;
import org.identityconnectors.framework.spi.operations.SchemaOp;
import org.identityconnectors.framework.spi.operations.SearchOp;
import org.identityconnectors.framework.spi.operations.TestOp;
import org.identityconnectors.framework.spi.operations.UpdateOp;
/**
* The MySQLUser connector works with accounts in a mysql database.
* <p>
* It supports create, update, search, and delete operations, authentication.
* </p>
* <p>
* This connector assumes that all account data is stored default in MySQL
* database. The create,update,delete actions are implemented to call
* appropriate SQL statements on the database.
* </p>
*
* @since 1.0
*/
@ConnectorClass(displayNameKey = "MYSQL_CONNECTOR_DISPLAY",
configurationClass = MySQLUserConfiguration.class)
public class MySQLUserConnector implements PoolableConnector, CreateOp,
SearchOp<FilterWhereBuilder>, DeleteOp, UpdateOp, SchemaOp, TestOp, AuthenticateOp,
ResolveUsernameOp {
/**
* Setup logging for the {@link MySQLUserConnector}.
*/
private static final Log LOG = Log.getLog(MySQLUserConnector.class);
private static final String SQL_UPDATE = "UPDATE mysql.user SET {0} WHERE user=?";
private static final String SQL_SET_USER = "user = ?";
private static final String SQL_SET_PASSWORD = "password = password(?)";
/**
* The query to get the user columns.
*
* The Password <> '' mean we want to
* avoid reading duplicates Every base user has password set up
*/
private static final String ALL_USER_QUERY = "SELECT DISTINCT User FROM mysql.user";
private static final String FLUSH_PRIVILEGES = "FLUSH PRIVILEGES";
private static final String SQL_DISTINCT_SELECT =
"SELECT DISTINCT user FROM mysql.user WHERE user = ?";
private static final String SQL_DELETE_TEMPLATE = "DROP USER ?";
private static final String SQL_SHOW_MODEL_USERS = "SELECT host FROM mysql.user WHERE user = ?";
private static final String SQL_SHOW_GRANTS = "SHOW GRANTS FOR ?@?";
private static final String SQL_DELETE_USERS = "DELETE FROM user WHERE User=?";
private static final String SQL_DELETE_DB = "DELETE FROM db WHERE User=?";
private static final String SQL_DELETE_TABLES = "DELETE FROM tables_priv WHERE User=?";
private static final String SQL_DELETE_COLUMNS = "DELETE FROM columns_priv WHERE User=?";
private static final String SQL_RESOLVE_USER =
"SELECT DISTINCT user FROM mysql.user WHERE user = ?";
private static final String SQL_AUTH_SELECT =
"SELECT DISTINCT user FROM mysql.user WHERE user = ? AND password = password(?)";
private static final String SQL_CREATE_TEMPLATE = "CREATE USER ? IDENTIFIED BY ?";
private static final String SQL_GRANT_TEMPLATE =
"GRANT USAGE ON *.* TO ?@'localhost' IDENTIFIED BY ?";
/**
* Place holder for the {@link Configuration} passed into the callback
* {@link MySQLUserConnector#init(Configuration)}.
*/
private MySQLUserConfiguration config;
/**
* Place holder for the Connection.
*/
private MySQLUserConnection conn;
/**
* Gets the Configuration context for this connector.
*
* {@inheritdoc }
*/
public Configuration getConfiguration() {
return this.config;
}
/**
* Callback method to receive the {@link Configuration}.
*
* {@inheritdoc }
*/
public void init(final Configuration cfg) {
this.config = (MySQLUserConfiguration) cfg;
this.conn = MySQLUserConnection.getConnection(this.config);
}
/**
* Method to receive check the connector is valid.
*
* {@inheritdoc }
*/
public void checkAlive() {
if (StringUtil.isNotBlank(config.getDatasource())) {
try {
conn.openConnection();
} catch (SQLException e) {
LOG.error(e, "error in checkAlive");
throw ConnectorException.wrap(e);
}
} else {
conn.test();
conn.commit();
}
// will not close connection, it is expected to be used in following api
// op
LOG.ok("checkAlive");
}
/**
* The connector connection access method.
*
* @return connection
*/
MySQLUserConnection getConnection() {
return conn;
}
/**
* Disposes of the {@link MySQLUserConnector}'s resources.
*
* {@inheritdoc }
*/
public void dispose() {
if (this.conn != null) {
this.conn.dispose();
this.conn = null;
}
this.config = null;
LOG.ok("dispose");
}
/**
* Create a new mysql user using model-user rights.
*
* {@inheritdoc }
*/
public Uid create(ObjectClass oclass, Set<Attribute> attrs, OperationOptions options) {
checkAttributes(oclass, attrs);
Name user = AttributeUtil.getNameFromAttributes(attrs);
if (user == null || StringUtil.isBlank(user.getNameValue())) {
throw new IllegalArgumentException(config.getMessage(MSG_NAME_BLANK));
}
// Password is Operational
GuardedString password = AttributeUtil.getPasswordValue(attrs);
if (password == null) {
throw new IllegalArgumentException(config.getMessage(MSG_PWD_BLANK));
}
try {
// Create the user
LOG.info("Creating user: {0}", user.getNameValue());
conn.openConnection();
createUser(user.getNameValue(), password);
// Don't forget to create Uid
final Uid uid = newUid(user.getNameValue());
// Model name is needed to set rights for the new user
String modelUserName = config.getUsermodel();
LOG.info("Reading the modeluser: {0}", modelUserName);
List<String> grants = readGrantsForModelUser(modelUserName);
// Replace modelUser to newUser for the GRANT statement
List<String> newGrants =
replaceModelUserInGrants(user.getNameValue(), modelUserName, grants);
// Granting rights for the new user
LOG.info("Granting rights for user: {0}", user.getNameValue());
grantingRights(newGrants, user.getNameValue(), password);
// commit all
conn.commit();
LOG.ok("Created user: {0}", user.getNameValue());
return uid;
} catch (SQLException ex) {
LOG.error(ex, "error in create");
throw ConnectorException.wrap(ex);
} finally {
conn.closeConnection();
}
}
/**
* Deletes a mysql user using drop statement.
*
* {@inheritdoc }
*/
public void delete(final ObjectClass oclass, final Uid uid, final OperationOptions options) {
if (oclass == null || (!oclass.equals(ObjectClass.ACCOUNT))) {
throw new IllegalArgumentException(config.getMessage(MSG_ACCOUNT_OBJECT_CLASS_REQUIRED));
}
if (uid == null || (uid.getUidValue() == null)) {
throw new IllegalArgumentException(config.getMessage(MSG_UID_BLANK));
}
try {
LOG.info("Delete user uid: {0}", uid.getName());
conn.openConnection();
// Delete the user
deleteUser(uid);
// Commit delete
conn.commit();
LOG.ok("Deleted user uid: {0}", uid.getName());
} catch (SQLException ex) {
LOG.error(ex, "error in delete");
throw ConnectorException.wrap(ex);
} finally {
conn.closeConnection();
}
}
/**
* Update mysql user.
*
* {@inheritDoc}
*/
public Uid update(final ObjectClass oclass, Uid oldUid, final Set<Attribute> attrs,
final OperationOptions options) {
checkAttributes(oclass, attrs);
// init the return value for old Uid
Uid ret = oldUid;
String updateSet = "";
// Bind values
final List<SQLParam> values = new ArrayList<SQLParam>();
// The update is changing name. The oldUid is a key and the name will be
// new uid for mysql.
Name name = AttributeUtil.getNameFromAttributes(attrs);
// is there a change in name?
if (name != null && !oldUid.getUidValue().equals(name.getNameValue())) {
LOG.info("Update user {0} to (1)", oldUid.getUidValue(), name.getNameValue());
updateSet = SQL_SET_USER;
values.add(new SQLParam("user", name.getNameValue(), Types.VARCHAR));
// update the return value to new Uid
ret = newUid(name.getNameValue());
}
// Password change
GuardedString password = AttributeUtil.getPasswordValue(attrs);
if (password != null) {
if (updateSet.length() != 0) {
updateSet = updateSet + ", ";
}
updateSet = updateSet + SQL_SET_PASSWORD;
values.add(new SQLParam("password", password));
}
// Finalize update
String sql = MessageFormat.format(SQL_UPDATE, updateSet);
values.add(new SQLParam("user", oldUid.getUidValue(), Types.CHAR));
try {
conn.openConnection();
// Update the user, insert bind values into the statement
updateUser(sql, values);
// Commit changes
conn.commit();
} catch (SQLException ex) {
LOG.error(ex, "error in update");
throw ConnectorException.wrap(ex);
} finally {
conn.closeConnection();
}
LOG.ok("User name: {0} updated", name);
return ret;
}
/**
* Creates a MySQL filter translator.
*
* {@inheritdoc }
*/
public FilterTranslator<FilterWhereBuilder> createFilterTranslator(ObjectClass oclass,
OperationOptions options) {
return new MySQLUserFilterTranslator(oclass, options);
}
/**
* Runs a query generated by the MySQLUserFilterTranslator.
*
* This will be called once for each native query produced by the
* FilterTranslator. {@inheritdoc }
*/
public void executeQuery(ObjectClass oclass, FilterWhereBuilder where, ResultsHandler handler,
OperationOptions options) {
// Get the needed attributes
if (oclass == null || (!oclass.equals(ObjectClass.ACCOUNT))) {
throw new IllegalArgumentException(config.getMessage(MSG_ACCOUNT_OBJECT_CLASS_REQUIRED));
}
// Database query builder will create SQL query.
// if where == null then all users are returned
final DatabaseQueryBuilder query = new DatabaseQueryBuilder(ALL_USER_QUERY);
query.setWhere(where);
ResultSet result = null;
PreparedStatement statement = null;
try {
conn.openConnection();
statement = conn.prepareStatement(query);
result = statement.executeQuery();
while (result.next()) {
ConnectorObjectBuilder bld = new ConnectorObjectBuilder();
// To be sure that uid and name are present for mysql
final String userName = result.getString(1);
// ISSUE, cloud be en empty string for anonymous public user
if (userName == null || userName.length() == 0) {
// Skip this line
continue;
}
bld.setUid(newUid(userName));
bld.setName(userName);
// No other attributes are now supported.
// Password can be encoded and it is not provided as an
// attribute
// only deals w/ accounts..
bld.setObjectClass(ObjectClass.ACCOUNT);
// create the connector object..
ConnectorObject ret = bld.build();
if (!handler.handle(ret)) {
break;
}
}
// Commit finally
conn.commit();
} catch (SQLException e) {
SQLUtil.rollbackQuietly(conn);
throw ConnectorException.wrap(e);
} finally {
SQLUtil.closeQuietly(result);
SQLUtil.closeQuietly(statement);
conn.closeConnection();
}
LOG.ok("executeQuery");
}
/**
* Create the mysqluser schema.
*
* {@inheritDoc}
*/
public Schema schema() {
// The Name is supported attribute
Set<AttributeInfo> attrInfoSet = new HashSet<AttributeInfo>();
attrInfoSet.add(Name.INFO);
// Password is operationalAttribute
attrInfoSet.add(OperationalAttributeInfos.PASSWORD);
// Use SchemaBuilder to build the schema. Currently, only ACCOUNT type
// is supported.
SchemaBuilder schemaBld = new SchemaBuilder(getClass());
schemaBld.defineObjectClass(ObjectClass.ACCOUNT_NAME, attrInfoSet);
LOG.ok("schema");
return schemaBld.build();
}
/**
* Test the configuration and connection.
*
* {@inheritDoc}
*/
public void test() {
try {
conn.openConnection();
conn.test();
if (!findUser(config.getUsermodel())) {
SQLUtil.rollbackQuietly(conn);
throw new IllegalArgumentException(config.getMessage(MSG_USER_MODEL_NOT_FOUND,
config.getUsermodel()));
}
conn.commit();
} catch (SQLException e) {
LOG.error(e, "Error in test");
// No rolback, the error could be just in the openConnection
throw ConnectorException.wrap(e);
} finally {
conn.closeConnection();
}
LOG.ok("test");
}
/**
* Attempts to authenticate the given user/password combination.
*
* {@inheritdoc }
*/
public Uid authenticate(ObjectClass oclass, String user, GuardedString password,
OperationOptions options) {
LOG.info("authenticate user: {0}", user);
// Get the needed attributes
if (oclass == null || (!oclass.equals(ObjectClass.ACCOUNT))) {
throw new IllegalArgumentException(config.getMessage(MSG_ACCOUNT_OBJECT_CLASS_REQUIRED));
}
if (user == null || StringUtil.isBlank(user)) {
throw new IllegalArgumentException(config.getMessage(MSG_NAME_BLANK));
}
if (password == null) {
throw new IllegalArgumentException(config.getMessage(MSG_PWD_BLANK));
}
List<SQLParam> values = new ArrayList<SQLParam>();
values.add(new SQLParam("user", user, Types.VARCHAR));
values.add(new SQLParam("password", password));
PreparedStatement stmt = null;
ResultSet result = null;
try {
conn.openConnection();
stmt = conn.prepareStatement(SQL_AUTH_SELECT, values);
result = stmt.executeQuery();
// No PasswordExpired capability
if (!result.next()) {
throw new InvalidCredentialException(config.getMessage(MSG_AUTH_FAILED, user));
}
final Uid uid = new Uid(result.getString(1));
conn.commit();
LOG.info("user: {0} authenticated ", user);
return uid;
} catch (SQLException e) {
LOG.error(e, "user: {0} authentication failed ", user);
SQLUtil.rollbackQuietly(conn);
throw ConnectorException.wrap(e);
} finally {
SQLUtil.closeQuietly(result);
SQLUtil.closeQuietly(stmt);
conn.closeConnection();
}
}
/**
* {@inheritDoc}
*/
public Uid resolveUsername(ObjectClass oclass, String user, OperationOptions options) {
LOG.info("authenticate user: {0}", user);
// Get the needed attributes
if (oclass == null || (!oclass.equals(ObjectClass.ACCOUNT))) {
throw new IllegalArgumentException(config.getMessage(MSG_ACCOUNT_OBJECT_CLASS_REQUIRED));
}
if (user == null || StringUtil.isBlank(user)) {
throw new IllegalArgumentException(config.getMessage(MSG_NAME_BLANK));
}
List<SQLParam> values = new ArrayList<SQLParam>();
values.add(new SQLParam("user", user, Types.VARCHAR));
PreparedStatement stmt = null;
ResultSet result = null;
try {
conn.openConnection();
stmt = conn.prepareStatement(SQL_RESOLVE_USER, values);
result = stmt.executeQuery();
// No PasswordExpired capability
if (!result.next()) {
throw new InvalidCredentialException(config.getMessage(MSG_AUTH_FAILED, user));
}
final Uid uid = new Uid(result.getString(1));
conn.commit();
LOG.info("user: {0} authenticated ", user);
return uid;
} catch (SQLException e) {
LOG.error(e, "user: {0} authentication failed ", user);
SQLUtil.rollbackQuietly(conn);
throw ConnectorException.wrap(e);
} finally {
SQLUtil.closeQuietly(result);
SQLUtil.closeQuietly(stmt);
conn.closeConnection();
}
}
/**
* Only supported attributes.
*
* @param oclass
* the only one supported class
* @param attrs
* the set of attributes
*/
private void checkAttributes(final ObjectClass oclass, final Set<Attribute> attrs) {
// Get the needed attributes
if (oclass == null || (!oclass.equals(ObjectClass.ACCOUNT))) {
throw new IllegalArgumentException(config.getMessage(MSG_ACCOUNT_OBJECT_CLASS_REQUIRED));
}
if (attrs == null || attrs.size() == 0) {
throw new IllegalArgumentException(config.getMessage(MSG_INVALID_ATTRIBUTE_SET));
}
// Check for known attributes
for (Attribute attribute : attrs) {
if (attribute == null) {
throw new IllegalArgumentException(config.getMessage(MSG_INVALID_ATTRIBUTE_SET));
}
if (attribute.is(Name.NAME) || attribute.is(Uid.NAME)
|| attribute.is(OperationalAttributes.PASSWORD_NAME)) {
continue;
}
throw new IllegalArgumentException(config.getMessage(MSG_INVALID_ATTRIBUTE_SET));
}
}
/**
* creates a new user, also sets the user's password.
*
* @param name
* @param password
*/
private void createUser(String name, GuardedString password) {
PreparedStatement c1 = null;
try {
// Create the user
List<SQLParam> values = new ArrayList<SQLParam>();
values.add(new SQLParam("name", name, Types.VARCHAR));
values.add(new SQLParam("password", password));
c1 = conn.prepareStatement(SQL_CREATE_TEMPLATE, values);
c1.execute();
LOG.ok("User {0} created", name);
} catch (SQLException e) {
if (findUser(name)) {
LOG.error(e, "Already Exists user {0}", name);
SQLUtil.rollbackQuietly(conn);
throw new AlreadyExistsException(e);
} else {
grantUssage(name, password);
}
} finally {
SQLUtil.closeQuietly(c1);
}
}
/**
* creates a new user for MySQL4.1 resource.
*
* @param name
* @param password
*/
private void grantUssage(String name, GuardedString password) {
PreparedStatement c1 = null;
try {
// Create the user
List<SQLParam> values = new ArrayList<SQLParam>();
values.add(new SQLParam("name", name, Types.VARCHAR));
values.add(new SQLParam("password", password));
c1 = conn.prepareStatement(SQL_GRANT_TEMPLATE, values);
c1.execute();
} catch (SQLException e) {
LOG.error(e, "Grant user {0} exception", name);
SQLUtil.rollbackQuietly(conn);
throw new IllegalStateException(e);
} finally {
SQLUtil.closeQuietly(c1);
}
}
/**
* Find the user.
*
* @param userName
*/
private boolean findUser(String userName) {
PreparedStatement ps = null;
ResultSet result = null;
final List<SQLParam> values = new ArrayList<SQLParam>();
values.add(new SQLParam("user", userName, Types.VARCHAR));
LOG.info("find User {0}", userName);
try {
ps = conn.prepareStatement(SQL_DISTINCT_SELECT, values);
result = ps.executeQuery();
if (result.next()) {
return true;
}
} catch (SQLException ex) {
LOG.error(ex, "find User {0} ", userName);
SQLUtil.rollbackQuietly(conn);
throw new IllegalStateException(ex);
} finally {
SQLUtil.closeQuietly(result);
SQLUtil.closeQuietly(ps);
}
return false;
}
/**
* Read the rights for the user model of mysql.
*
* @param modelUser
* name
* @return list of the rights
*/
private List<String> readGrantsForModelUser(String modelUser) {
PreparedStatement c1 = null;
PreparedStatement c2 = null;
ResultSet rs1 = null;
ResultSet rs2 = null;
List<String> grants = new ArrayList<String>();
try {
// created, read the model user grants
c1 = conn.getConnection().prepareStatement(SQL_SHOW_MODEL_USERS);
c1.setString(1, modelUser);
rs1 = c1.executeQuery();
while (rs1.next()) {
final StringBuilder query = new StringBuilder(SQL_SHOW_GRANTS);
String host = rs1.getString(1);
if (StringUtil.isBlank(host)) {
host = "%"; // if host is blank, the all alias is used
}
LOG.ok("readGrantsFor host:{0}, user:{1}", host, modelUser);
c2 = conn.getConnection().prepareStatement(query.toString());
c2.setString(1, modelUser);
c2.setString(2, host);
rs2 = c2.executeQuery();
while (rs2.next()) {
String grant = rs2.getString(1);
grants.add(grant);
}
}
} catch (SQLException e) {
LOG.error(e, "Error read GRANTS for model user {0}", modelUser);
// No error when the modelUser does not exist
SQLUtil.rollbackQuietly(conn);
// throw ConnectorException.wrap(e);
} finally {
SQLUtil.closeQuietly(rs1);
SQLUtil.closeQuietly(rs2);
SQLUtil.closeQuietly(c1);
SQLUtil.closeQuietly(c2);
}
return grants;
}
/**
* Replace Model User in GRANT SQL statements.
*
* @param userName
* the new user
* @param modelUser
* is defined in mysql
* @param grants
* the list of rights to set
* @return the updated GRANT statement
*/
private List<String> replaceModelUserInGrants(String userName, String modelUser,
List<String> grants) {
List<String> newGrants = new ArrayList<String>();
for (String grant : grants) {
String newGrant = grant.replaceAll("'" + modelUser + "'", "'" + userName + "'");
// Remove password key is if present
newGrant = newGrant.replaceAll("IDENTIFIED BY PASSWORD '.*'", "IDENTIFIED BY ?");
newGrants.add(newGrant);
}
return newGrants;
}
/**
* Execute the GRANT statement for the given userName and rights.
*
* @param grants
* rights for the new user
* @param userName
* ID of the new user
*/
private void grantingRights(List<String> grants, String userName, GuardedString password) {
for (String grant : grants) {
final PreparedStatement[] psa = new PreparedStatement[1];
try {
psa[0] = conn.getConnection().prepareStatement(grant);
LOG.info("Granting rights {0} for user: {1}", userName, grant);
if (grant.indexOf("IDENTIFIED BY ?") > 0) {
password.access(new GuardedString.Accessor() {
public void access(char[] clearChars) {
try {
psa[0].setObject(1, new String(clearChars));
} catch (SQLException e) {
// checked exception are not allowed in the
// access method
// Lets use the exception softening pattern
throw new RuntimeException(e);
}
}
});
}
psa[0].execute();
} catch (SQLException e) {
LOG.error(e, "Error granting rights {0} for user: {1}", userName, grant);
SQLUtil.rollbackQuietly(conn);
throw ConnectorException.wrap(e);
} finally {
SQLUtil.closeQuietly(psa[0]);
}
}
}
/**
* Delete The User.
*
* @param uid
* the uid of the user
* @param uid
*/
private void deleteUser(final Uid uid) {
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
ResultSet rs1 = null;
try {
// created, read the model user grants
ps1 = conn.getConnection().prepareStatement(SQL_SHOW_MODEL_USERS);
ps1.setString(1, uid.getUidValue());
rs1 = ps1.executeQuery();
boolean unknown = true;
while (rs1.next()) {
unknown = false;
final StringBuilder query = new StringBuilder(SQL_DELETE_TEMPLATE);
final String host = rs1.getString(1);
// The host specification must be added when defined
if ((host != null) && !host.equals("") && !host.equals("%")) {
query.append("@" + host);
}
// create a prepared call..
ps2 = conn.getConnection().prepareStatement(query.toString());
// set object to delete..
ps2.setString(1, uid.getUidValue());
// uid to delete..
LOG.info("Deleting Uid: {0}, host:{1}", uid.getUidValue(), host);
ps2.execute();
}
if (unknown) {
throw new UnknownUidException(uid, ObjectClass.ACCOUNT);
}
} catch (SQLException e) {
deleteUser41(uid);
} finally {
// clean up..
SQLUtil.closeQuietly(rs1);
SQLUtil.closeQuietly(ps1);
SQLUtil.closeQuietly(ps2);
}
LOG.ok("Deleted Uid: {0}", uid.getUidValue());
}
/**
* Delete The User on 41 resource.
*
* @param uid
* the uid of the user
*/
private void deleteUser41(final Uid uid) {
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
PreparedStatement ps3 = null;
PreparedStatement ps4 = null;
try {
// created, read the model user grants
ps1 = conn.getConnection().prepareStatement(SQL_DELETE_USERS);
ps1.setString(1, uid.getUidValue());
ps1.execute();
ps2 = conn.getConnection().prepareStatement(SQL_DELETE_DB);
ps2.setString(1, uid.getUidValue());
ps2.execute();
ps3 = conn.getConnection().prepareStatement(SQL_DELETE_TABLES);
ps3.setString(1, uid.getUidValue());
ps3.execute();
ps4 = conn.getConnection().prepareStatement(SQL_DELETE_COLUMNS);
ps4.setString(1, uid.getUidValue());
ps4.execute();
flushPriviledges();
} catch (SQLException e) {
SQLUtil.rollbackQuietly(conn);
LOG.error(e, "delete user 41");
throw new IllegalStateException(e);
} finally {
// clean up..
SQLUtil.closeQuietly(ps1);
SQLUtil.closeQuietly(ps2);
SQLUtil.closeQuietly(ps3);
SQLUtil.closeQuietly(ps4);
}
LOG.ok("Deleted Uid: {0}", uid.getUidValue());
}
/**
* Update the user identified by uid using the update string and list of
* bind values.
*
* @param updstr
* the SQL update string
* @param values
* the object values to be bind
*/
private void updateUser(String updstr, List<SQLParam> values) {
// create the sql statement..
PreparedStatement stmt = null;
try {
// create the prepared statement..
stmt = conn.getConnection().prepareStatement(updstr);
SQLUtil.setParams(stmt, values);
stmt.execute();
} catch (SQLException e) {
SQLUtil.rollbackQuietly(conn);
LOG.error(e, "SQL: " + updstr);
throw ConnectorException.wrap(e);
} finally {
// clean up..
SQLUtil.closeQuietly(stmt);
}
flushPriviledges();
}
/**
* Make all privilege changes the most actual on resource.
*/
private void flushPriviledges() {
PreparedStatement cstmt = null;
try {
// create the prepared statement..
cstmt = conn.getConnection().prepareStatement(FLUSH_PRIVILEGES);
cstmt.execute();
} catch (SQLException e) {
SQLUtil.rollbackQuietly(conn);
LOG.error(e, "SQL: " + FLUSH_PRIVILEGES);
throw ConnectorException.wrap(e);
} finally {
// clean up..
SQLUtil.closeQuietly(cstmt);
}
}
/**
* Create new Uid.
*
* @param userName
* @return
*/
private Uid newUid(String userName) {
return new Uid(userName);
}
}