</service:DocHandlerParams>
<service:validatorHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.report.nuxeo.ReportValidatorHandler
</service:validatorHandler>
- <!-- Commented out per http://issues.collectionspace.org/browse/CSPACE-6346 -->
- <!--
<service:initHandler xmlns:service="http://collectionspace.org/services/config/service">
<service:classname>org.collectionspace.services.report.nuxeo.ReportPostInitHandler</service:classname>
<service:params>
- <service:property>
- <service:key>readerRoleName</service:key>
- <service:value>reader</service:value>
- </service:property>
+ <!-- The name of a database account (role) that will provide remote access for building and running reports -->
+ <service:property>
+ <service:key>reporterRoleName</service:key>
+ <service:value>reporter</service:value>
+ </service:property>
+ <!-- Currently retained for backward compatibility -->
+ <service:property>
+ <service:key>readerRoleName</service:key>
+ <service:value>reader</service:value>
+ </service:property>
</service:params>
</service:initHandler>
- -->
<service:object xmlns:service="http://collectionspace.org/services/config/service" name="Report"
version="1.0">
<service:part id="0" control_group="Managed" versionable="true" auditable="false" label="reports-system"
<service:validatorHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.report.nuxeo.ReportValidatorHandler</service:validatorHandler>
<service:initHandler xmlns:service="http://collectionspace.org/services/config/service">
<service:classname>org.collectionspace.services.report.nuxeo.ReportPostInitHandler</service:classname>
- <!-- The name of a database account (role) that will provide remote access for building and running reports -->
- <service:params>
- <service:property>
- <service:key>reporterRoleName</service:key>
- <service:value>reporter</service:value>
- </service:property>
- </service:params>
<service:params>
<service:property>
<service:key>readerRoleName</service:key>
import java.io.File;\r
import java.io.FileInputStream;\r
import java.io.FileNotFoundException;\r
-import java.io.FileWriter;\r
import java.io.InputStream;\r
import java.sql.Connection;\r
import java.sql.PreparedStatement;\r
if (list != null && list.size() > 0) {\r
org.collectionspace.services.config.service.InitHandler handlerType = list.get(0); // REM - 12/2012: We might want to think about supporting multiple post-init handlers\r
String initHandlerClassname = handlerType.getClassname();\r
+ if (Tools.isEmpty(initHandlerClassname)) {\r
+ continue;\r
+ }\r
+ if (logger.isInfoEnabled()) {\r
+ logger.info(String.format("Firing post-init handler %s ...", initHandlerClassname));\r
+ }\r
\r
List<org.collectionspace.services.config.service.InitHandler.Params.Field>\r
fields = handlerType.getParams().getField();\r
List<RepositoryDomainType> repoDomainList = tenantBinding.getRepositoryDomain();\r
for (RepositoryDomainType repoDomain : repoDomainList) {\r
String repoDomainName = repoDomain.getName();\r
- String dbName = JDBCTools.getDatabaseName(repoDomain.getRepositoryName(), getCspaceInstanceId());\r
+ String repositoryName = repoDomain.getRepositoryName();\r
+ String cspaceInstanceId = getCspaceInstanceId();\r
+ String dbName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);\r
if (nuxeoDBsChecked.contains(dbName)) {\r
if (logger.isDebugEnabled()) {\r
logger.debug("Another user of db: " + dbName + ": Repo: " + repoDomainName\r
}\r
} else {\r
// Create the user as needed\r
- createUserIfNotExists(conn, dbType, nuxeoUser, nuxeoPW);\r
+ JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, nuxeoUser, nuxeoPW);\r
if (readerUser != null) {\r
- createUserIfNotExists(conn, dbType, readerUser, readerPW);\r
+ JDBCTools.createNewDatabaseUser(JDBCTools.CSADMIN_DATASOURCE_NAME, repositoryName, cspaceInstanceId, dbType, readerUser, readerPW);\r
}\r
// Create the database\r
createDatabaseWithRights(conn, dbType, dbName, nuxeoUser, nuxeoPW, readerUser, readerPW);\r
\r
}\r
\r
- private void createUserIfNotExists(Connection conn, DatabaseProductType dbType, String username, String userPW)\r
- throws Exception {\r
- PreparedStatement pstmt = null;\r
- Statement stmt = null;\r
- final String USER_EXISTS_QUERY_PSQL = "SELECT 1 AS result FROM pg_roles WHERE rolname=?";\r
- String userExistsQuery;\r
- \r
- if (dbType == DatabaseProductType.POSTGRESQL) {\r
- userExistsQuery = USER_EXISTS_QUERY_PSQL;\r
- } else {\r
- throw new UnsupportedOperationException("CreateUserIfNotExists only supports PSQL - MySQL NYI!");\r
- }\r
- \r
- try {\r
- pstmt = conn.prepareStatement(userExistsQuery); // create a\r
- // statement\r
- pstmt.setString(1, username); // set dbName param\r
- ResultSet rs = pstmt.executeQuery();\r
- // extract data from the ResultSet\r
- boolean userExists = rs.next();\r
- rs.close();\r
- if (userExists) {\r
- if (logger.isDebugEnabled()) {\r
- logger.debug("User: " + username + " already exists.");\r
- }\r
- } else {\r
- stmt = conn.createStatement();\r
- String sql = "CREATE ROLE " + username + " WITH PASSWORD '" + userPW + "' LOGIN";\r
- stmt.executeUpdate(sql);\r
- // Really should do the grants as well.\r
- if (logger.isDebugEnabled()) {\r
- logger.debug("Created Users: '" + username + "' and 'reader'");\r
- }\r
- }\r
- } catch (Exception e) {\r
- logger.error("createUserIfNotExists failed on exception: " + e.getLocalizedMessage());\r
- throw e; // propagate\r
- } finally { // close resources\r
- try {\r
- if (pstmt != null) {\r
- pstmt.close();\r
- }\r
- if (stmt != null) {\r
- stmt.close();\r
- }\r
- } catch (SQLException se) {\r
- // nothing we can do\r
- }\r
- }\r
- }\r
- \r
private void createDatabaseWithRights(Connection conn, DatabaseProductType dbType, String dbName, String ownerName,\r
String ownerPW, String readerName, String readerPW) throws Exception {\r
Statement stmt = null;\r
*/\r
package org.collectionspace.services.common.storage;\r
\r
-import org.collectionspace.services.common.ServiceMain;\r
import org.collectionspace.services.common.api.Tools;\r
import org.collectionspace.services.common.config.ConfigUtils;\r
import org.slf4j.Logger;\r
import javax.sql.rowset.RowSetProvider;\r
\r
import org.apache.tomcat.dbcp.dbcp.BasicDataSource;\r
+import org.collectionspace.services.common.ServiceMain;\r
\r
/**\r
* User: laramie\r
public static String CSPACE_DATASOURCE_NAME = "CspaceDS";\r
public static String NUXEO_DATASOURCE_NAME = "NuxeoDS";\r
// Default database names\r
-// public static String DEFAULT_CSPACE_DATABASE_NAME = ConfigUtils.DEFAULT_CSPACE_DATABASE_NAME;\r
+ // public static String DEFAULT_CSPACE_DATABASE_NAME = ConfigUtils.DEFAULT_CSPACE_DATABASE_NAME;\r
public static String DEFAULT_NUXEO_REPOSITORY_NAME = ConfigUtils.DEFAULT_NUXEO_REPOSITORY_NAME;\r
public static String DEFAULT_NUXEO_DATABASE_NAME = ConfigUtils.DEFAULT_NUXEO_DATABASE_NAME;\r
public static String CSADMIN_DATASOURCE_NAME = "CsadminDS";\r
public static String NUXEO_READER_DATASOURCE_NAME = "NuxeoReaderDS";\r
public static String NUXEO_USER_NAME = "nuxeo";\r
public static String SQL_WILDCARD = "%";\r
+ public static String DATABASE_SELECT_PRIVILEGE_NAME = "SELECT";\r
+\r
\r
//\r
// Private constants\r
\r
return databaseName;\r
}\r
+ \r
+ /**\r
+ * Grant a specified privilege to a database user. This privilege will\r
+ * be applied to all 'public' schema tables within the specified repository.\r
+ * \r
+ * @param dataSourceName a JDBC datasource name.\r
+ * @param repositoryName a repository (e.g. RDBMS database) name.\r
+ * @param cspaceInstanceId a CollectionSpace instance identifier.\r
+ * @param privilegeName a database privilege (e.g. SELECT) to be granted.\r
+ * @param databaseUserName a database user to receive the privilege grant.\r
+ */\r
+ public static void grantPrivilegeToDatabaseUser(String dataSourceName, String repositoryName,\r
+ String cspaceInstanceId, String privilegeName, String databaseUserName) {\r
+ Statement stmt = null;\r
+ Connection conn = null;\r
+ String sql = String.format("GRANT %s ON ALL TABLES IN SCHEMA public TO %s", privilegeName, databaseUserName);\r
+ try {\r
+ DatabaseProductType databaseProductType = JDBCTools.getDatabaseProductType(dataSourceName, repositoryName,\r
+ cspaceInstanceId);\r
+ if (databaseProductType == DatabaseProductType.MYSQL) {\r
+ // Nothing to do here: MYSQL already does wildcard grants in init_db.sql\r
+ } else if(databaseProductType != DatabaseProductType.POSTGRESQL) {\r
+ throw new Exception("Unrecognized database system " + databaseProductType);\r
+ } else {\r
+ String databaseName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);\r
+ // Verify that the database user exists before executing the grant\r
+ if (hasDatabaseUser(dataSourceName, repositoryName, cspaceInstanceId,\r
+ databaseProductType, databaseUserName)) {\r
+ conn = getConnection(dataSourceName, repositoryName, cspaceInstanceId);\r
+ stmt = conn.createStatement(); \r
+ stmt.execute(sql);\r
+ }\r
+ }\r
+ \r
+ } catch (SQLException sqle) {\r
+ SQLException tempException = sqle;\r
+ // SQLExceptions can be chained. Loop to log all.\r
+ while (null != tempException) {\r
+ logger.debug("SQL Exception: " + sqle.getLocalizedMessage());\r
+ tempException = tempException.getNextException();\r
+ }\r
+ logger.debug("SQL problem in executeQuery: ", sqle);\r
+ } catch (Throwable e) {\r
+ logger.debug(String.format("Problem granting privileges to database user: %s SQL: %s ERROR: %s",\r
+ databaseUserName, sql, e));\r
+ } finally {\r
+ try {\r
+ if (stmt != null) {\r
+ stmt.close();\r
+ }\r
+ if (conn != null) {\r
+ conn.close();\r
+ }\r
+ } catch (SQLException sqle) {\r
+ // nothing we can do here except log\r
+ logger.warn("SQL Exception when closing statement/connection: " + sqle.getLocalizedMessage());\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Create a database user, if that user doesn't already exist.\r
+ * \r
+ * @param conn a database connection.\r
+ * @param dbType a database product type.\r
+ * @param username the name of the database user to create.\r
+ * @param userPW the initial password for that database user.\r
+ */\r
+ public static void createNewDatabaseUser(String dataSourceName, String repositoryName,\r
+ String cspaceInstanceId, DatabaseProductType dbType, String username, String userPW) throws Exception {\r
+ Statement stmt = null;\r
+ Connection conn = null;\r
+ if (dbType != DatabaseProductType.POSTGRESQL) {\r
+ throw new UnsupportedOperationException("createNewDatabaseUser only supports PostgreSQL");\r
+ }\r
+ try {\r
+ if (hasDatabaseUser(dataSourceName, repositoryName, cspaceInstanceId, dbType, username)) {\r
+ if (logger.isDebugEnabled()) {\r
+ logger.debug("User: " + username + " already exists.");\r
+ }\r
+ } else {\r
+ conn = getConnection(dataSourceName, repositoryName, cspaceInstanceId);\r
+ stmt = conn.createStatement();\r
+ String sql = "CREATE ROLE " + username + " WITH PASSWORD '" + userPW + "' LOGIN";\r
+ stmt.executeUpdate(sql);\r
+ if (logger.isDebugEnabled()) {\r
+ logger.debug("Created User: " + username);\r
+ }\r
+ }\r
+ } catch (Exception e) {\r
+ logger.error("createNewDatabaseUser failed on exception: " + e.getLocalizedMessage());\r
+ throw e;\r
+ } finally {\r
+ try {\r
+ if (stmt != null) {\r
+ stmt.close();\r
+ }\r
+ if (conn != null) {\r
+ conn.close();\r
+ }\r
+ } catch (SQLException sqle) {\r
+ // nothing we can do here except log\r
+ logger.warn("SQL Exception when closing statement/connection: " + sqle.getLocalizedMessage());\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Identify whether a database user already exists.\r
+ * \r
+ * @param dataSourceName a JDBC datasource name.\r
+ * @param repositoryName a repository (e.g. RDBMS database) name.\r
+ * @param cspaceInstanceId a CollectionSpace instance identifier.\r
+ * @param dbType a database product type.\r
+ * @param username the name of the database user to create.\r
+ * @param userPW the initial password for that database user.\r
+ */\r
+ public static boolean hasDatabaseUser(String dataSourceName, String repositoryName,\r
+ String cspaceInstanceId, DatabaseProductType dbType, String username) throws Exception {\r
+ PreparedStatement pstmt = null;\r
+ Statement stmt = null;\r
+ Connection conn = null;\r
+ final String USER_EXISTS_QUERY_POSTGRESQL = "SELECT 1 AS result FROM pg_roles WHERE rolname=?";\r
+ if (dbType != DatabaseProductType.POSTGRESQL) {\r
+ throw new UnsupportedOperationException("hasDatabaseUser only supports PostgreSQL");\r
+ }\r
+ try {\r
+ conn = getConnection(dataSourceName, repositoryName, cspaceInstanceId);\r
+ pstmt = conn.prepareStatement(USER_EXISTS_QUERY_POSTGRESQL);\r
+ pstmt.setString(1, username);\r
+ ResultSet rs = pstmt.executeQuery();\r
+ boolean userExists = rs.next(); // Will return a value of 1 if user exists\r
+ rs.close();\r
+ return userExists;\r
+ } catch (Exception e) {\r
+ logger.error("hasDatabaseUser failed on exception: " + e.getLocalizedMessage());\r
+ throw e;\r
+ } finally {\r
+ try {\r
+ if (pstmt != null) {\r
+ pstmt.close();\r
+ }\r
+ if (stmt != null) {\r
+ stmt.close();\r
+ }\r
+ if (conn != null) {\r
+ conn.close();\r
+ }\r
+ } catch (SQLException sqle) {\r
+ // nothing we can do here except log\r
+ logger.warn("SQL Exception when closing statement/connection: " + sqle.getLocalizedMessage());\r
+ }\r
+ }\r
+ }\r
+ \r
+ \r
+ // -----------------------------\r
+ // Utility methods for debugging\r
+ // -----------------------------\r
\r
/**\r
* Prints metadata, such as database username and connection URL,\r
* @param rs a ResultSet.\r
* @throws SQLException \r
*/\r
- public void printResultSetMetaData(ResultSet rs) throws SQLException {\r
+ public static void printResultSetMetaData(ResultSet rs) throws SQLException {\r
if (rs == null) {\r
return;\r
}\r
public class ReportPostInitHandler extends InitHandler implements IInitHandler {\r
\r
final Logger logger = LoggerFactory.getLogger(ReportPostInitHandler.class);\r
- public static final String DATABASE_SELECT_PRIVILEGE_NAME = "SELECT";\r
\r
+// public static final String REPORTER_ROLE_NAME_KEY = "reporterRoleName";\r
+// public static final String DEFAULT_REPORTER_ROLE_NAME = "reporter" + ServiceMain.getInstance().getCspaceInstanceId();\r
+// private String reporterRoleName = DEFAULT_REPORTER_ROLE_NAME;\r
+ \r
// Currently retained for backward compatibility\r
public static final String READER_ROLE_NAME_KEY = "readerRoleName";\r
public static final String DEFAULT_READER_ROLE_NAME = "reader" + ServiceMain.getInstance().getCspaceInstanceId();\r
private String readerRoleName = DEFAULT_READER_ROLE_NAME;\r
- \r
- public static final String REPORTER_ROLE_NAME_KEY = "reporterRoleName";\r
- public static final String DEFAULT_REPORTER_ROLE_NAME = "reporter" + ServiceMain.getInstance().getCspaceInstanceId();\r
- private String reporterRoleName = DEFAULT_REPORTER_ROLE_NAME;\r
\r
/** See the class javadoc for this class: it shows the syntax supported in the configuration params.\r
*/\r
List<Property> propertyList) throws Exception {\r
//Check for existing privileges, and if not there, grant them\r
for(Property prop : propertyList) {\r
- if(REPORTER_ROLE_NAME_KEY.equals(prop.getKey())) {\r
- String value = prop.getValue();\r
- if(Tools.notEmpty(value) && !DEFAULT_REPORTER_ROLE_NAME.equals(value)){\r
- reporterRoleName = value + ServiceMain.getInstance().getCspaceInstanceId();\r
- logger.debug("ReportPostInitHandler: overriding reporterRoleName default value to use: "\r
- + value);\r
- }\r
- }\r
+// if(REPORTER_ROLE_NAME_KEY.equals(prop.getKey())) {\r
+// String value = prop.getValue();\r
+// if(Tools.notEmpty(value) && !DEFAULT_REPORTER_ROLE_NAME.equals(value)){\r
+// reporterRoleName = value + ServiceMain.getInstance().getCspaceInstanceId();\r
+// logger.debug("ReportPostInitHandler: overriding reporterRoleName default value to use: "\r
+// + value);\r
+// }\r
+// }\r
// FIXME: Currently retained for backward compatibility; remove this block when appropriate\r
if(READER_ROLE_NAME_KEY.equals(prop.getKey())) {\r
String value = prop.getValue();\r
}\r
}\r
}\r
- String privilegeName = DATABASE_SELECT_PRIVILEGE_NAME;\r
- grantPrivilegeToDatabaseRole(dataSourceName, repositoryName, cspaceInstanceId, privilegeName, reporterRoleName);\r
+ String privilegeName = JDBCTools.DATABASE_SELECT_PRIVILEGE_NAME;\r
+// JDBCTools.grantPrivilegeToDatabaseUser(dataSourceName, repositoryName, cspaceInstanceId, privilegeName, reporterRoleName);\r
// FIXME: Currently retained for backward compatibility; remove the following line when appropriate\r
- grantPrivilegeToDatabaseRole(dataSourceName, repositoryName, cspaceInstanceId, privilegeName, readerRoleName);\r
- }\r
-\r
- // FIXME: This method might be refactorable / movable to the\r
- // org.collectionspace.services.common.storage.JDBCTools class.\r
- // If so, any database privilege constants here should be moved with it.\r
- private void grantPrivilegeToDatabaseRole(String dataSourceName, String repositoryName, String cspaceInstanceId,\r
- String privilegeName, String roleName) {\r
- Connection conn = null;\r
- Statement stmt = null;\r
- String sql = "";\r
- try {\r
- DatabaseProductType databaseProductType = JDBCTools.getDatabaseProductType(dataSourceName, repositoryName,\r
- cspaceInstanceId);\r
- if (databaseProductType == DatabaseProductType.MYSQL) {\r
- // Nothing to do: MYSQL already does wildcard grants in init_db.sql\r
- } else if(databaseProductType != DatabaseProductType.POSTGRESQL) {\r
- throw new Exception("Unrecognized database system " + databaseProductType);\r
- } else {\r
- String databaseName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);\r
- conn = JDBCTools.getConnection(dataSourceName, databaseName);\r
- stmt = conn.createStatement(); \r
- // FIXME: Check first that role exists before executing the grant\r
- sql = String.format("GRANT %s ON ALL TABLES IN SCHEMA public TO %s", privilegeName, roleName);\r
- stmt.execute(sql);\r
- }\r
- \r
- } catch (SQLException sqle) {\r
- SQLException tempException = sqle;\r
- while (null != tempException) { // SQLExceptions can be chained. Loop to log all.\r
- logger.debug("SQL Exception: " + sqle.getLocalizedMessage());\r
- tempException = tempException.getNextException();\r
- }\r
- logger.debug("ReportPostInitHandler: SQL problem in executeQuery: ", sqle);\r
- } catch (Throwable e) {\r
- logger.debug("ReportPostInitHandler: problem checking/adding grant for reader: "+readerRoleName+") SQL: "+sql+" ERROR: "+e);\r
- } finally {\r
- try {\r
- if (conn != null) {\r
- conn.close();\r
- }\r
- if (stmt != null) {\r
- stmt.close();\r
- }\r
- } catch (SQLException sqle) {\r
- logger.debug("SQL Exception closing statement/connection in executeQuery: " + sqle.getLocalizedMessage());\r
- }\r
- }\r
+ JDBCTools.grantPrivilegeToDatabaseUser(dataSourceName, repositoryName, cspaceInstanceId, privilegeName, readerRoleName);\r
}\r
\r
\r