/*
* Copyright © 2016 Red Hat, Inc. and others.
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
*/
package org.opendaylight.aaa.datastore.h2;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.sql.Connection;
-import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Base class for H2 stores.
*/
-// "Nonconstant string passed to execute or addBatch method on an SQL statement...Consider using a prepared statement
-// instead. It is more efficient and less vulnerable to SQL injection attacks.". Possible TODO - is it worth it here to
-// use prepared statements?
-@SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
abstract class AbstractStore<T> {
-
private static final Logger LOG = LoggerFactory.getLogger(AbstractStore.class);
/**
* The name of the table used to represent this store.
*/
- private final String tableName;
-
+ private final @NonNull String tableName;
/**
* Database connection factory.
*/
- private final ConnectionProvider dbConnectionFactory;
-
+ private final @NonNull ConnectionProvider dbConnectionFactory;
/**
* Table types we're interested in (when checking tables' existence).
*/
- public static final String[] TABLE_TYPES = new String[] { "TABLE" };
+ @VisibleForTesting
+ static final String[] TABLE_TYPES = new String[] { "TABLE" };
/**
* Creates an instance.
* @param dbConnectionFactory factory to obtain JDBC Connections from
* @param tableName The name of the table being managed.
*/
- protected AbstractStore(ConnectionProvider dbConnectionFactory, String tableName) {
- this.dbConnectionFactory = dbConnectionFactory;
- this.tableName = tableName;
+ AbstractStore(final ConnectionProvider dbConnectionFactory, final String tableName) {
+ this.dbConnectionFactory = requireNonNull(dbConnectionFactory);
+ this.tableName = requireNonNull(tableName);
}
/**
* exist, it will be created (using {@link #getTableCreationStatement()}).
*
* @return A database connection.
- *
* @throws StoreException if an error occurs.
*/
- protected Connection dbConnect() throws StoreException {
- Connection conn = dbConnectionFactory.getConnection();
- try {
- // Ensure table check/creation is atomic
- synchronized (this) {
- DatabaseMetaData dbm = conn.getMetaData();
- try (ResultSet rs = dbm.getTables(null, null, tableName, TABLE_TYPES)) {
- if (rs.next()) {
- LOG.debug("Table {} already exists", tableName);
- } else {
+ final Connection dbConnect() throws StoreException {
+ final var conn = dbConnectionFactory.getConnection();
+ // Ensure table check/creation is atomic
+ synchronized (this) {
+ try {
+ final var dbm = conn.getMetaData();
+ try (var rs = dbm.getTables(null, null, tableName, TABLE_TYPES)) {
+ if (!rs.next()) {
LOG.info("Table {} does not exist, creating it", tableName);
- try (Statement stmt = conn.createStatement()) {
- stmt.executeUpdate(getTableCreationStatement());
+ try (var stmt = conn.createStatement()) {
+ createTable(stmt);
}
+ } else {
+ LOG.debug("Table {} already exists", tableName);
}
}
+ } catch (SQLException e) {
+ LOG.error("Error connecting to the H2 database", e);
+ throw new StoreException("Cannot connect to database server", e);
}
- } catch (SQLException e) {
- LOG.error("Error connecting to the H2 database", e);
- throw new StoreException("Cannot connect to database server", e);
}
return conn;
}
+ /**
+ * Create a managed table for on a particular connection..
+ *
+ * @param stmt A pre-allocated SQL statement
+ * @throws SQLException If table creation fails
+ */
+ abstract void createTable(Statement stmt) throws SQLException;
+
/**
* Empties the store.
*
* @throws StoreException if a connection error occurs.
*/
- public void dbClean() throws StoreException {
- try (Connection c = dbConnect()) {
- // The table name can't be a parameter in a prepared statement
- String sql = "DELETE FROM " + tableName;
- try (Statement statement = c.createStatement()) {
- statement.execute(sql);
- }
+ @VisibleForTesting
+ @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE",
+ justification = "table name cannot be a parameter in a prepared statement")
+ final void dbClean() throws StoreException {
+ try (var c = dbConnect();
+ var statement = c.createStatement()) {
+ // FIXME: can we somehow make this a constant?
+ statement.execute("DELETE FROM " + tableName);
} catch (SQLException e) {
LOG.error("Error clearing table {}", tableName, e);
throw new StoreException("Error clearing table " + tableName, e);
}
}
- /**
- * Returns the SQL code required to create the managed table.
- *
- * @return The SQL table creation statement.
- */
- protected abstract String getTableCreationStatement();
+ abstract void cleanTable(Statement stmt) throws SQLException;
/**
* Lists all the stored items.
*
* @return The stored item.
- *
* @throws StoreException if an error occurs.
*/
- protected List<T> listAll() throws StoreException {
+ @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE",
+ justification = "table name cannot be a parameter in a prepared statement")
+ final List<T> listAll() throws StoreException {
List<T> result = new ArrayList<>();
- String query = "SELECT * FROM " + tableName;
- try (Connection conn = dbConnect();
- Statement stmt = conn.createStatement();
- ResultSet rs = stmt.executeQuery(query)) {
+ try (var conn = dbConnect();
+ var stmt = conn.createStatement();
+ var rs = stmt.executeQuery("SELECT * FROM " + tableName)) {
while (rs.next()) {
result.add(fromResultSet(rs));
}
* Lists the stored items returned by the given statement.
*
* @param ps The statement (which must be ready for execution). It is the caller's responsibility to close this.
- *
* @return The stored items.
- *
* @throws StoreException if an error occurs.
*/
- protected List<T> listFromStatement(PreparedStatement ps) throws StoreException {
- List<T> result = new ArrayList<>();
- try (ResultSet rs = ps.executeQuery()) {
+ final List<T> listFromStatement(final PreparedStatement ps) throws StoreException {
+ final var result = new ArrayList<T>();
+ try (var rs = ps.executeQuery()) {
while (rs.next()) {
result.add(fromResultSet(rs));
}
* Extracts the first item returned by the given statement, if any.
*
* @param ps The statement (which must be ready for execution). It is the caller's responsibility to close this.
- *
* @return The first item, or {@code null} if none.
- *
* @throws StoreException if an error occurs.
*/
- protected T firstFromStatement(PreparedStatement ps) throws StoreException {
- try (ResultSet rs = ps.executeQuery()) {
- if (rs.next()) {
- return fromResultSet(rs);
- } else {
- return null;
- }
+ final T firstFromStatement(final PreparedStatement ps) throws StoreException {
+ try (var rs = ps.executeQuery()) {
+ return rs.next() ? fromResultSet(rs) : null;
} catch (SQLException e) {
LOG.error("Error listing first matching item from {}", tableName, e);
throw new StoreException(e);
* Converts a single row in a result set to an instance of the managed type.
*
* @param rs The result set (which is ready for extraction; {@link ResultSet#next()} must <b>not</b> be called).
- *
* @return The corresponding instance.
- *
* @throws SQLException if an error occurs.
*/
- protected abstract T fromResultSet(ResultSet rs) throws SQLException;
+ abstract T fromResultSet(ResultSet rs) throws SQLException;
}
package org.opendaylight.aaa.datastore.h2;
import java.sql.Connection;
+import javax.sql.CommonDataSource;
import javax.sql.DataSource;
/**
- * Provider of JDBC Connections.
- * Essentially a much simplified {@link DataSource}.
+ * Provider of JDBC Connections. Essentially a much simplified {@link DataSource} -- sans the {@link CommonDataSource}
+ * bits, which are a hassle.
*
* @author Michael Vorburger
*/
public interface ConnectionProvider {
-
/**
- * Get a Connection.
+ * Get an SQL {@link Connection}.
*
- * @return a connection from this Factory; it may be a brand new one
- * obtained from the JDBC Driver, or an existing open one, if it
- * hasn't previously been closed
- * @throws StoreException
- * if no Connection could be obtained
+ * @return a connection from this Factory; it may be a brand new one obtained from the JDBC Driver, or an existing
+ * open one, if it has not previously been closed
+ * @throws StoreException if no Connection could be obtained
*/
+ // FIXME: what is the blocking behaviour?
Connection getConnection() throws StoreException;
-
}
/*
* Copyright (c) 2014, 2017 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.text.StringEscapeUtils;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.aaa.api.model.Domain;
import org.opendaylight.aaa.api.model.Domains;
import org.slf4j.Logger;
/**
* Domain store.
- *
- * @author peter.mellquist@hp.com
- *
*/
-public class DomainStore extends AbstractStore<Domain> {
+final class DomainStore extends AbstractStore<Domain> {
private static final Logger LOG = LoggerFactory.getLogger(DomainStore.class);
- public static final String SQL_ID = "domainid";
- public static final String SQL_NAME = "name";
- public static final String SQL_DESCR = "description";
- public static final String SQL_ENABLED = "enabled";
- private static final String TABLE_NAME = "DOMAINS";
+ /**
+ * Name of our SQL table. This constant lives here rather than in {@link SQLTable} for brevity.
+ */
+ // FIXME: AAA-221: this is a system table
+ static final @NonNull String TABLE = "DOMAINS";
+
+ static {
+ SQLTable.DOMAIN.verifyTable(TABLE);
+ }
- public DomainStore(final ConnectionProvider dbConnectionFactory) {
- super(dbConnectionFactory, TABLE_NAME);
+ /**
+ * Column storing {@link Domain#getDomainid()}, which is a flat namespace.
+ */
+ // FIXME: rename to "id"
+ @VisibleForTesting
+ static final String COL_ID = "domainid";
+ /**
+ * Column storing {@link Domain#getName()}, which is a short name.
+ */
+ @VisibleForTesting
+ static final String COL_NAME = "name";
+ /**
+ * Column storing {@link Domain#getDescription()}, which is a detailed description.
+ */
+ @VisibleForTesting
+ static final String COL_DESC = "description";
+ /**
+ * Column storing {@link Domain#isEnabled()}, which is ... not used anywhere.
+ */
+ // FIXME: remove or audit for potential callers of isEnabled()
+ @VisibleForTesting
+ static final String COL_ENABLED = "enabled";
+
+ DomainStore(final ConnectionProvider dbConnectionFactory) {
+ super(dbConnectionFactory, TABLE);
+ }
+
+ @Override
+ void createTable(final Statement stmt) throws SQLException {
+ stmt.executeUpdate("CREATE TABLE " + TABLE + " ("
+ // FIXME: on delete cascade? RoleStore.COL_DOMAIN_ID seems to reference this
+ + COL_ID + " VARCHAR(128) PRIMARY KEY, "
+ + COL_NAME + " VARCHAR(128) UNIQUE NOT NULL, "
+ + COL_DESC + " VARCHAR(128), "
+ // FIXME: change boolean
+ + COL_ENABLED + " INTEGER NOT NULL)");
}
@Override
- protected String getTableCreationStatement() {
- return "CREATE TABLE DOMAINS "
- + "(domainid VARCHAR(128) PRIMARY KEY,"
- + "name VARCHAR(128) UNIQUE NOT NULL, "
- + "description VARCHAR(128) , "
- + "enabled INTEGER NOT NULL)";
+ void cleanTable(final Statement stmt) throws SQLException {
+ stmt.execute("DELETE FROM " + TABLE);
}
@Override
protected Domain fromResultSet(final ResultSet rs) throws SQLException {
Domain domain = new Domain();
- domain.setDomainid(rs.getString(SQL_ID));
- domain.setName(rs.getString(SQL_NAME));
- domain.setDescription(rs.getString(SQL_DESCR));
- domain.setEnabled(rs.getInt(SQL_ENABLED) == 1);
+ domain.setDomainid(rs.getString(COL_ID));
+ domain.setName(rs.getString(COL_NAME));
+ domain.setDescription(rs.getString(COL_DESC));
+ domain.setEnabled(rs.getInt(COL_ENABLED) == 1);
return domain;
}
- public Domains getDomains() throws StoreException {
+ Domains getDomains() throws StoreException {
Domains domains = new Domains();
domains.setDomains(listAll());
return domains;
}
- protected Domains getDomains(final String domainName) throws StoreException {
- LOG.debug("getDomains for: {}", domainName);
- Domains domains = new Domains();
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM DOMAINS WHERE name = ?")) {
- pstmt.setString(1, domainName);
- LOG.debug("query string: {}", pstmt);
- domains.setDomains(listFromStatement(pstmt));
+ // FIXME: seems to be unused
+ Domains getDomains(final String domainName) throws StoreException {
+ final Domains domains;
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_NAME + " = ?")) {
+ stmt.setString(1, domainName);
+
+ domains = new Domains();
+ LOG.debug("getDomains() request: {}", stmt);
+ domains.setDomains(listFromStatement(stmt));
} catch (SQLException e) {
LOG.error("Error listing domains matching {}", domainName, e);
throw new StoreException("Error listing domains", e);
return domains;
}
- protected Domain getDomain(final String id) throws StoreException {
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM DOMAINS WHERE domainid = ? ")) {
- pstmt.setString(1, id);
- LOG.debug("query string: {}", pstmt);
- return firstFromStatement(pstmt);
+ Domain getDomain(final String id) throws StoreException {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, id);
+
+ LOG.debug("getDomain() request: {}", stmt);
+ return firstFromStatement(stmt);
} catch (SQLException e) {
LOG.error("Error retrieving domain {}", id, e);
throw new StoreException("Error loading domain", e);
}
}
- public Domain createDomain(final Domain domain) throws StoreException {
+ Domain createDomain(final Domain domain) throws StoreException {
requireNonNull(domain);
requireNonNull(domain.getName());
requireNonNull(domain.isEnabled());
- String query = "insert into DOMAINS (domainid,name,description,enabled) values(?, ?, ?, ?)";
- try (Connection conn = dbConnect();
- PreparedStatement statement = conn.prepareStatement(query)) {
- statement.setString(1, domain.getName());
- statement.setString(2, domain.getName());
- statement.setString(3, domain.getDescription());
- statement.setInt(4, domain.isEnabled() ? 1 : 0);
- int affectedRows = statement.executeUpdate();
- if (affectedRows == 0) {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("INSERT INTO " + TABLE + " ("
+ + COL_ID + ", " + COL_NAME + ", " + COL_DESC + ", " + COL_ENABLED + ") VALUES (?, ?, ?, ?)")) {
+ stmt.setString(1, domain.getName());
+ stmt.setString(2, domain.getName());
+ stmt.setString(3, domain.getDescription());
+ stmt.setInt(4, domain.isEnabled() ? 1 : 0);
+
+ LOG.debug("createDomain() request: {}", stmt);
+ if (stmt.executeUpdate() == 0) {
throw new StoreException("Creating domain failed, no rows affected.");
}
domain.setDomainid(domain.getName());
}
}
- protected Domain putDomain(final Domain domain) throws StoreException {
- Domain savedDomain = this.getDomain(domain.getDomainid());
+ Domain putDomain(final Domain domain) throws StoreException {
+ final var savedDomain = getDomain(domain.getDomainid());
if (savedDomain == null) {
return null;
}
-
if (domain.getDescription() != null) {
savedDomain.setDescription(domain.getDescription());
}
savedDomain.setEnabled(domain.isEnabled());
}
- String query = "UPDATE domains SET description = ?, enabled = ?, name = ? WHERE domainid = ?";
- try (Connection conn = dbConnect();
- PreparedStatement statement = conn.prepareStatement(query)) {
- statement.setString(1, savedDomain.getDescription());
- statement.setInt(2, savedDomain.isEnabled() ? 1 : 0);
- statement.setString(3, savedDomain.getName());
- statement.setString(4, savedDomain.getDomainid());
- statement.executeUpdate();
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("UPDATE " + TABLE + " SET "
+ + COL_NAME + " = ?, " + COL_DESC + " = ?, " + COL_ENABLED + " = ? WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, savedDomain.getName());
+ stmt.setString(2, savedDomain.getDescription());
+ stmt.setInt(3, savedDomain.isEnabled() ? 1 : 0);
+ stmt.setString(4, savedDomain.getDomainid());
+
+ LOG.debug("putDomain() request: {}", stmt);
+ stmt.executeUpdate();
} catch (SQLException e) {
LOG.error("Error updating domain {}", domain.getDomainid(), e);
throw new StoreException("Error updating domain", e);
return savedDomain;
}
- @SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
- protected Domain deleteDomain(String domainid) throws StoreException {
- domainid = StringEscapeUtils.escapeHtml4(domainid);
- Domain deletedDomain = this.getDomain(domainid);
+ @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", justification = "Weird original code")
+ Domain deleteDomain(final String domainid) throws StoreException {
+ // FIXME: remove this once we have a more modern H2
+ final String escaped = StringEscapeUtils.escapeHtml4(domainid);
+ final var deletedDomain = getDomain(escaped);
if (deletedDomain == null) {
return null;
}
- String query = String.format("DELETE FROM DOMAINS WHERE domainid = '%s'", domainid);
- try (Connection conn = dbConnect();
- Statement statement = conn.createStatement()) {
- int deleteCount = statement.executeUpdate(query);
+
+ try (var conn = dbConnect();
+ var stmt = conn.createStatement()) {
+ // FIXME: prepare statement instead
+ final String query = String.format("DELETE FROM " + TABLE + " WHERE " + COL_ID + " = '%s'", escaped);
+ LOG.debug("deleteDomain() request: {}", query);
+
+ int deleteCount = stmt.executeUpdate(query);
LOG.debug("deleted {} records", deleteCount);
return deletedDomain;
} catch (SQLException e) {
package org.opendaylight.aaa.datastore.h2;
+import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
* Grant store.
*
* @author peter.mellquist@hp.com
- *
*/
-public class GrantStore extends AbstractStore<Grant> {
+final class GrantStore extends AbstractStore<Grant> {
private static final Logger LOG = LoggerFactory.getLogger(GrantStore.class);
- public static final String SQL_ID = "grantid";
- public static final String SQL_TENANTID = "domainid";
- public static final String SQL_USERID = "userid";
- public static final String SQL_ROLEID = "roleid";
- private static final String TABLE_NAME = "GRANTS";
+ static final String TABLE = "GRANTS";
+
+ static {
+ SQLTable.GRANT.verifyTable(TABLE);
+ }
+
+ // FIXME: javadoc
+ static final String COL_ID = "grantid";
- public GrantStore(ConnectionProvider dbConnectionFactory) {
- super(dbConnectionFactory, TABLE_NAME);
+ // FIXME: javadoc
+ // FIXME: 'tenant' vs 'domain' ?
+ @VisibleForTesting
+ static final String COL_TENANTID = "domainid";
+
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_USERID = "userid";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_ROLEID = "roleid";
+
+ GrantStore(final ConnectionProvider dbConnectionFactory) {
+ super(dbConnectionFactory, TABLE);
}
@Override
- protected String getTableCreationStatement() {
- return "CREATE TABLE GRANTS "
- + "(grantid VARCHAR(128) PRIMARY KEY,"
- + "domainid VARCHAR(128) NOT NULL, "
- + "userid VARCHAR(128) NOT NULL, "
- + "roleid VARCHAR(128) NOT NULL)";
+ void createTable(final Statement stmt) throws SQLException {
+ stmt.executeUpdate("CREATE TABLE " + TABLE + " ("
+ + COL_ID + " VARCHAR(128) PRIMARY KEY, "
+ // FIXME: foreign key to DomainStore.COL_ID?
+ + COL_TENANTID + " VARCHAR(128) NOT NULL, "
+ // FIXME: foreign key to UserStore.COL_ID?
+ + COL_USERID + " VARCHAR(128) NOT NULL, "
+ // FIXME: foreign key to RoleStore.COL_ID?
+ + COL_ROLEID + " VARCHAR(128) NOT NULL)");
}
@Override
- protected Grant fromResultSet(ResultSet rs) throws SQLException {
+ void cleanTable(final Statement stmt) throws SQLException {
+ stmt.execute("DELETE FROM " + TABLE);
+ }
+
+ @Override
+ protected Grant fromResultSet(final ResultSet rs) throws SQLException {
Grant grant = new Grant();
try {
- grant.setGrantid(rs.getString(SQL_ID));
- grant.setDomainid(rs.getString(SQL_TENANTID));
- grant.setUserid(rs.getString(SQL_USERID));
- grant.setRoleid(rs.getString(SQL_ROLEID));
+ grant.setGrantid(rs.getString(COL_ID));
+ grant.setDomainid(rs.getString(COL_TENANTID));
+ grant.setUserid(rs.getString(COL_USERID));
+ grant.setRoleid(rs.getString(COL_ROLEID));
} catch (SQLException sqle) {
LOG.error("SQL Exception: ", sqle);
throw sqle;
return grant;
}
- public Grants getGrants(String did, String uid) throws StoreException {
- Grants grants = new Grants();
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn
- .prepareStatement("SELECT * FROM grants WHERE domainid = ? AND userid = ?")) {
- pstmt.setString(1, did);
- pstmt.setString(2, uid);
- LOG.debug("query string: {}", pstmt);
- grants.setGrants(listFromStatement(pstmt));
+ Grants getGrants(final String domainId, final String userId) throws StoreException {
+ final Grants grants;
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE
+ + " WHERE " + COL_TENANTID + " = ? AND " + COL_USERID + " = ?")) {
+ stmt.setString(1, domainId);
+ stmt.setString(2, userId);
+ LOG.debug("getGrants() request: {}", stmt);
+
+ grants = new Grants();
+ grants.setGrants(listFromStatement(stmt));
} catch (SQLException e) {
throw new StoreException("SQL Exception", e);
}
return grants;
}
- protected Grants getGrants(String userid) throws StoreException {
- Grants grants = new Grants();
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM GRANTS WHERE userid = ? ")) {
- pstmt.setString(1, userid);
- LOG.debug("query string: {}", pstmt);
- grants.setGrants(listFromStatement(pstmt));
+ Grants getGrants(final String userid) throws StoreException {
+ final Grants grants;
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_USERID + " = ?")) {
+ stmt.setString(1, userid);
+ LOG.debug("getGrants() request: {}", stmt);
+
+ grants = new Grants();
+ grants.setGrants(listFromStatement(stmt));
} catch (SQLException e) {
throw new StoreException("SQL Exception", e);
}
return grants;
}
- protected Grant getGrant(String id) throws StoreException {
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM GRANTS WHERE grantid = ? ")) {
- pstmt.setString(1, id);
- LOG.debug("query string: {}", pstmt);
- return firstFromStatement(pstmt);
+ Grant getGrant(final String id) throws StoreException {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, id);
+ LOG.debug("getGrant() request: {}", stmt);
+
+ return firstFromStatement(stmt);
} catch (SQLException e) {
throw new StoreException("SQL Exception", e);
}
}
- protected Grant getGrant(String did, String uid, String rid) throws StoreException {
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn
- .prepareStatement("SELECT * FROM GRANTS WHERE domainid = ? AND userid = ? AND roleid = ? ")) {
- pstmt.setString(1, did);
- pstmt.setString(2, uid);
- pstmt.setString(3, rid);
- LOG.debug("query string: {}", pstmt);
- return firstFromStatement(pstmt);
+ // FIXME: seems to be unused
+ Grant getGrant(final String did, final String uid, final String rid) throws StoreException {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE
+ + " WHERE " + COL_TENANTID + " = ? AND " + COL_USERID + " = ? AND " + COL_ROLEID + " = ?")) {
+ stmt.setString(1, did);
+ stmt.setString(2, uid);
+ stmt.setString(3, rid);
+ LOG.debug("getGrant() request: {}", stmt);
+
+ return firstFromStatement(stmt);
} catch (SQLException e) {
throw new StoreException("SQL Exception", e);
}
}
- protected Grant createGrant(Grant grant) throws StoreException {
- String query = "insert into grants (grantid,domainid,userid,roleid) values(?,?,?,?)";
- try (Connection conn = dbConnect();
- PreparedStatement statement = conn.prepareStatement(query)) {
- statement.setString(
- 1,
- IDMStoreUtil.createGrantid(grant.getUserid(), grant.getDomainid(),
- grant.getRoleid()));
- statement.setString(2, grant.getDomainid());
- statement.setString(3, grant.getUserid());
- statement.setString(4, grant.getRoleid());
- int affectedRows = statement.executeUpdate();
- if (affectedRows == 0) {
+ Grant createGrant(final Grant grant) throws StoreException {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("INSERT INTO " + TABLE + " ("
+ + COL_ID + ", " + COL_TENANTID + ", " + COL_USERID + ", " + COL_ROLEID + ") VALUES (?, ?, ?, ?)")) {
+ stmt.setString(1, IDMStoreUtil.createGrantid(grant.getUserid(), grant.getDomainid(), grant.getRoleid()));
+ stmt.setString(2, grant.getDomainid());
+ stmt.setString(3, grant.getUserid());
+ stmt.setString(4, grant.getRoleid());
+ LOG.debug("createGrant() request: {}", stmt);
+
+ if (stmt.executeUpdate() == 0) {
throw new StoreException("Creating grant failed, no rows affected.");
}
- grant.setGrantid(IDMStoreUtil.createGrantid(grant.getUserid(), grant.getDomainid(),
- grant.getRoleid()));
+ grant.setGrantid(IDMStoreUtil.createGrantid(grant.getUserid(), grant.getDomainid(), grant.getRoleid()));
return grant;
} catch (SQLException e) {
throw new StoreException("SQL Exception", e);
}
}
- @SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
- protected Grant deleteGrant(String grantid) throws StoreException {
- grantid = StringEscapeUtils.escapeHtml4(grantid);
- Grant savedGrant = this.getGrant(grantid);
+ @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", justification = "Weird original code")
+ Grant deleteGrant(final String grantid) throws StoreException {
+ final String escaped = StringEscapeUtils.escapeHtml4(grantid);
+ final var savedGrant = getGrant(escaped);
if (savedGrant == null) {
return null;
}
- String query = String.format("DELETE FROM GRANTS WHERE grantid = '%s'", grantid);
- try (Connection conn = dbConnect();
- Statement statement = conn.createStatement()) {
- int deleteCount = statement.executeUpdate(query);
+ try (var conn = dbConnect();
+ var stmt = conn.createStatement()) {
+ // FIXME: prepare statement instead
+ final String query = String.format("DELETE FROM " + TABLE + " WHERE " + COL_ID + " = '%s'", escaped);
+ LOG.debug("deleteGrant() request: {}", query);
+
+ int deleteCount = stmt.executeUpdate(query);
LOG.debug("deleted {} records", deleteCount);
return savedGrant;
} catch (SQLException e) {
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
+import org.h2.jdbcx.JdbcConnectionPool;
/**
- * Simple Provider of JDBC Connections, based on an {@link IdmLightConfig} and
- * {@link DriverManager}.
+ * Simple Provider of JDBC Connections, based on an {@link IdmLightConfig} and {@link DriverManager}.
*
* @author Michael Vorburger
*/
this.config = config;
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec This implemenation always opens a new connection.
+ *
+ * FIXME: Integrate a {@link JdbcConnectionPool}, as {@link #config} is guaranteed to be constant. This is
+ * needlessly heavy, as we are locating the driver.
+ */
@Override
public Connection getConnection() throws StoreException {
try {
import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.text.StringEscapeUtils;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.aaa.api.IDMStoreUtil;
import org.opendaylight.aaa.api.model.Role;
import org.opendaylight.aaa.api.model.Roles;
import org.slf4j.LoggerFactory;
/**
- * Store for roles.
+ * An {@link AbstractStore} of {@link Role}s.
*
* @author peter.mellquist@hp.com
- *
*/
-public class RoleStore extends AbstractStore<Role> {
+final class RoleStore extends AbstractStore<Role> {
private static final Logger LOG = LoggerFactory.getLogger(RoleStore.class);
- public static final String SQL_ID = "roleid";
- protected static final String SQL_DOMAIN_ID = "domainid";
- public static final String SQL_NAME = "name";
- public static final String SQL_DESCR = "description";
- private static final String TABLE_NAME = "ROLES";
+ /**
+ * Name of our SQL table. This constant lives here rather than in {@link SQLTable} for brevity.
+ */
+ // FIXME: AAA-221: this is a system table
+ static final @NonNull String TABLE = "ROLES";
+
+ static {
+ SQLTable.ROLE.verifyTable(TABLE);
+ }
+
+ /**
+ * Column storing {@link Role#getRoleid()}, which is a flat namespace.
+ */
+ // FIXME: rename to "id"
+ @VisibleForTesting
+ static final String COL_ID = "roleid";
+ /**
+ * Column storing {@link Role#getName()}, which is a short name.
+ */
+ @VisibleForTesting
+ static final String COL_NAME = "name";
+ // FIXME: document this column
+ // FIXME: rename to "domain_id"
+ // FIXME: cross-reference DomainStore?
+ @VisibleForTesting
+ static final String COL_DOMAIN_ID = "domainid";
+ /**
+ * Column storing {@link Role#getDescription()}, which is a detailed description.
+ * FIXME: this should be optional, justlike {@link DomainStore#COL_DESC}
+ */
+ @VisibleForTesting
+ static final String COL_DESC = "description";
+
+ RoleStore(final ConnectionProvider dbConnectionFactory) {
+ super(dbConnectionFactory, TABLE);
+ }
- public RoleStore(final ConnectionProvider dbConnectionFactory) {
- super(dbConnectionFactory, TABLE_NAME);
+ @Override
+ void createTable(final Statement stmt) throws SQLException {
+ stmt.executeUpdate("CREATE TABLE " + TABLE + " ("
+ + COL_ID + " VARCHAR(128) PRIMARY KEY, "
+ + COL_NAME + " VARCHAR(128) NOT NULL, "
+ // FIXME: FOREIGN_KEY to DomainStore?
+ + COL_DOMAIN_ID + " VARCHAR(128) NOT NULL, "
+ + COL_DESC + " VARCHAR(128) NOT NULL)");
}
@Override
- protected String getTableCreationStatement() {
- return "CREATE TABLE ROLES " + "(roleid VARCHAR(128) PRIMARY KEY,"
- + "name VARCHAR(128) NOT NULL, " + "domainid VARCHAR(128) NOT NULL, "
- + "description VARCHAR(128) NOT NULL)";
+ void cleanTable(final Statement stmt) throws SQLException {
+ stmt.execute("DELETE FROM " + TABLE);
}
@Override
protected Role fromResultSet(final ResultSet rs) throws SQLException {
Role role = new Role();
try {
- role.setRoleid(rs.getString(SQL_ID));
- role.setDomainid(rs.getString(SQL_DOMAIN_ID));
- role.setName(rs.getString(SQL_NAME));
- role.setDescription(rs.getString(SQL_DESCR));
+ role.setRoleid(rs.getString(COL_ID));
+ role.setDomainid(rs.getString(COL_DOMAIN_ID));
+ role.setName(rs.getString(COL_NAME));
+ role.setDescription(rs.getString(COL_DESC));
} catch (SQLException sqle) {
LOG.error("SQL Exception: ", sqle);
throw sqle;
return role;
}
- public Roles getRoles() throws StoreException {
+ Roles getRoles() throws StoreException {
Roles roles = new Roles();
roles.setRoles(listAll());
return roles;
}
- protected Role getRole(final String id) throws StoreException {
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM ROLES WHERE roleid = ? ")) {
- pstmt.setString(1, id);
- LOG.debug("query string: {}", pstmt);
- return firstFromStatement(pstmt);
+ Role getRole(final String id) throws StoreException {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, id);
+
+ LOG.debug("getRole() request: {}", stmt);
+ return firstFromStatement(stmt);
} catch (SQLException s) {
throw new StoreException("SQL Exception: " + s);
}
}
- protected Role createRole(final Role role) throws StoreException {
+ Role createRole(final Role role) throws StoreException {
requireNonNull(role);
requireNonNull(role.getName());
requireNonNull(role.getDomainid());
- String query = "insert into roles (roleid,domainid,name,description) values(?,?,?,?)";
- try (Connection conn = dbConnect(); PreparedStatement statement = conn.prepareStatement(query)) {
+
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("INSERT INTO " + TABLE + " ("
+ + COL_ID + ", " + COL_DOMAIN_ID + ", " + COL_NAME + ", " + COL_DESC + ") VALUES (?, ?, ?, ?)")) {
role.setRoleid(IDMStoreUtil.createRoleid(role.getName(), role.getDomainid()));
- statement.setString(1, role.getRoleid());
- statement.setString(2, role.getDomainid());
- statement.setString(3, role.getName());
- statement.setString(4, role.getDescription());
- int affectedRows = statement.executeUpdate();
- if (affectedRows == 0) {
+ stmt.setString(1, role.getRoleid());
+ stmt.setString(2, role.getDomainid());
+ stmt.setString(3, role.getName());
+ stmt.setString(4, role.getDescription());
+
+ LOG.debug("createRole() request: {}", stmt);
+ if (stmt.executeUpdate() == 0) {
throw new StoreException("Creating role failed, no rows affected.");
}
return role;
}
}
- protected Role putRole(final Role role) throws StoreException {
-
- Role savedRole = this.getRole(role.getRoleid());
+ Role putRole(final Role role) throws StoreException {
+ Role savedRole = getRole(role.getRoleid());
if (savedRole == null) {
return null;
}
savedRole.setName(role.getName());
}
- String query = "UPDATE roles SET description = ? WHERE roleid = ?";
- try (Connection conn = dbConnect(); PreparedStatement statement = conn.prepareStatement(query)) {
- statement.setString(1, savedRole.getDescription());
- statement.setString(2, savedRole.getRoleid());
- statement.executeUpdate();
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement(
+ "UPDATE " + TABLE + " SET " + COL_DESC + " = ? WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, savedRole.getDescription());
+ stmt.setString(2, savedRole.getRoleid());
+
+ LOG.debug("putRole() request: {}", stmt);
+ stmt.executeUpdate();
} catch (SQLException s) {
throw new StoreException("SQL Exception : " + s);
}
}
@SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
- protected Role deleteRole(String roleid) throws StoreException {
- roleid = StringEscapeUtils.escapeHtml4(roleid);
- Role savedRole = this.getRole(roleid);
+ Role deleteRole(final String roleid) throws StoreException {
+ // FIXME: remove this once we have a more modern H2
+ final String escaped = StringEscapeUtils.escapeHtml4(roleid);
+ Role savedRole = getRole(escaped);
if (savedRole == null) {
return null;
}
- String query = String.format("DELETE FROM ROLES WHERE roleid = '%s'", roleid);
- try (Connection conn = dbConnect(); Statement statement = conn.createStatement()) {
- int deleteCount = statement.executeUpdate(query);
+ try (var conn = dbConnect();
+ var stmt = conn.createStatement()) {
+ // FIXME: prepare statement instead
+ final String query = String.format("DELETE FROM " + TABLE + " WHERE " + COL_ID + " = '%s'", escaped);
+ LOG.debug("deleteRole() request: {}", query);
+
+ int deleteCount = stmt.executeUpdate(query);
LOG.debug("deleted {} records", deleteCount);
return savedRole;
} catch (SQLException s) {
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.datastore.h2;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Enumeration of tables in our schema.
+ */
+@NonNullByDefault
+enum SQLTable {
+ /**
+ * Domains.
+ */
+ // FIXME: Yeah, say more in documentation:
+ // - what is a domain?
+ // - how does it relate to others?
+ DOMAIN(DomainStore.TABLE),
+ /**
+ * Users.
+ */
+ // FIXME: Yeah, say more in documentation
+ USER(UserStore.TABLE),
+ /**
+ * Roles.
+ */
+ // FIXME: Yeah, say more in documentation
+ ROLE(RoleStore.TABLE),
+ /**
+ * Grants. Probably just a (domain, user, role) tuple, but do not take my word for it.
+ */
+ // FIXME: Yeah, say more in documentation
+ GRANT(GrantStore.TABLE);
+
+ private final String tableName;
+
+ SQLTable(final String name) {
+ tableName = name;
+ }
+
+ void verifyTable(final String storeTableName) {
+ verify(tableName.equals(storeTableName), "Mismatched table name '%s' with allocation %s", storeTableName, this);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(name()).add("tableName", tableName).toString();
+ }
+}
import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
-import java.util.Objects;
import org.apache.commons.text.StringEscapeUtils;
import org.opendaylight.aaa.api.IDMStoreUtil;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
-import org.opendaylight.aaa.api.password.service.PasswordHash;
import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* Store for users.
*
* @author peter.mellquist@hp.com
- *
*/
-public class UserStore extends AbstractStore<User> {
+final class UserStore extends AbstractStore<User> {
private static final Logger LOG = LoggerFactory.getLogger(UserStore.class);
- public static final String SQL_ID = "userid";
- public static final String SQL_DOMAIN_ID = "domainid";
- public static final String SQL_NAME = "name";
- public static final String SQL_EMAIL = "email";
- public static final String SQL_PASSWORD = "password";
- public static final String SQL_DESCR = "description";
- public static final String SQL_ENABLED = "enabled";
- public static final String SQL_SALT = "salt";
- private static final String TABLE_NAME = "USERS";
+ static final String TABLE = "USERS";
+
+ static {
+ SQLTable.USER.verifyTable(TABLE);
+ }
+
+ /**
+ * Column storing {@link User#getUserid()}, which is a flat namespace.
+ */
+ // FIXME: rename to "id"
+ @VisibleForTesting
+ static final String COL_ID = "userid";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_DOMAIN_ID = "domainid";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_NAME = "name";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_EMAIL = "email";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_PASSWORD = "password";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_DESC = "description";
+ // FIXME: javadoc
+ @VisibleForTesting
+ static final String COL_ENABLED = "enabled";
+ // FIXME: javadoc
+ private static final String COL_SALT = "salt";
private final PasswordHashService passwordService;
- public UserStore(final ConnectionProvider dbConnectionFactory, final PasswordHashService passwordService) {
- super(dbConnectionFactory, TABLE_NAME);
- this.passwordService = Objects.requireNonNull(passwordService);
+ UserStore(final ConnectionProvider dbConnectionFactory, final PasswordHashService passwordService) {
+ super(dbConnectionFactory, TABLE);
+ this.passwordService = requireNonNull(passwordService);
+ }
+
+ @Override
+ void createTable(final Statement stmt) throws SQLException {
+ stmt.executeUpdate("CREATE TABLE " + TABLE + " ("
+ + COL_ID + " VARCHAR(128) PRIMARY KEY, "
+ + COL_NAME + " VARCHAR(128) NOT NULL, "
+ // FIXME: foreign key to DomainStore.COL_ID?
+ + COL_DOMAIN_ID + " VARCHAR(128) NOT NULL, "
+ + COL_EMAIL + " VARCHAR(128) NOT NULL, "
+ + COL_DESC + " VARCHAR(128) NOT NULL, "
+ // FIXME: is 'salt' even used? Some comparators are not storing hashes, either
+ + COL_PASSWORD + " VARCHAR(128) NOT NULL, "
+ + COL_SALT + " VARCHAR(128) NOT NULL, "
+ // FIXME: boolean
+ + COL_ENABLED + " INTEGER NOT NULL)");
}
@Override
- protected String getTableCreationStatement() {
- return "CREATE TABLE users " + "(userid VARCHAR(128) PRIMARY KEY,"
- + "name VARCHAR(128) NOT NULL, " + "domainid VARCHAR(128) NOT NULL, "
- + "email VARCHAR(128) NOT NULL, " + "password VARCHAR(128) NOT NULL, "
- + "description VARCHAR(128) NOT NULL, " + "salt VARCHAR(128) NOT NULL, "
- + "enabled INTEGER NOT NULL)";
+ void cleanTable(final Statement stmt) throws SQLException {
+ stmt.execute("DELETE FROM " + TABLE);
}
@Override
protected User fromResultSet(final ResultSet rs) throws SQLException {
User user = new User();
try {
- user.setUserid(rs.getString(SQL_ID));
- user.setDomainid(rs.getString(SQL_DOMAIN_ID));
- user.setName(rs.getString(SQL_NAME));
- user.setEmail(rs.getString(SQL_EMAIL));
- user.setPassword(rs.getString(SQL_PASSWORD));
- user.setDescription(rs.getString(SQL_DESCR));
- user.setEnabled(rs.getInt(SQL_ENABLED) == 1);
- user.setSalt(rs.getString(SQL_SALT));
- } catch (SQLException sqle) {
- LOG.error("SQL Exception: ", sqle);
- throw sqle;
+ user.setUserid(rs.getString(COL_ID));
+ user.setDomainid(rs.getString(COL_DOMAIN_ID));
+ user.setName(rs.getString(COL_NAME));
+ user.setEmail(rs.getString(COL_EMAIL));
+ user.setPassword(rs.getString(COL_PASSWORD));
+ user.setDescription(rs.getString(COL_DESC));
+ user.setEnabled(rs.getInt(COL_ENABLED) == 1);
+ user.setSalt(rs.getString(COL_SALT));
+ } catch (SQLException e) {
+ LOG.error("SQL Exception: ", e);
+ throw e;
}
return user;
}
- public Users getUsers() throws StoreException {
+ Users getUsers() throws StoreException {
Users users = new Users();
users.setUsers(listAll());
return users;
}
- protected Users getUsers(final String username, final String domain) throws StoreException {
- LOG.debug("getUsers for: {} in domain {}", username, domain);
+ Users getUsers(final String username, final String domain) throws StoreException {
+ final Users users;
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " USERS WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, IDMStoreUtil.createUserid(username, domain));
+ LOG.debug("getUsers() request: {}", stmt);
- Users users = new Users();
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM USERS WHERE userid = ? ")) {
- pstmt.setString(1, IDMStoreUtil.createUserid(username, domain));
- LOG.debug("query string: {}", pstmt);
- users.setUsers(listFromStatement(pstmt));
+ users = new Users();
+ users.setUsers(listFromStatement(stmt));
} catch (SQLException s) {
throw new StoreException("SQL Exception : " + s);
}
return users;
}
- public User getUser(final String id) throws StoreException {
- try (Connection conn = dbConnect();
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM USERS WHERE userid = ? ")) {
- pstmt.setString(1, id);
- LOG.debug("query string: {}", pstmt);
- return firstFromStatement(pstmt);
+ User getUser(final String id) throws StoreException {
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("SELECT * FROM " + TABLE + " WHERE " + COL_ID + " = ? ")) {
+ stmt.setString(1, id);
+ LOG.debug("getUser() request: {}", stmt);
+
+ return firstFromStatement(stmt);
} catch (SQLException s) {
throw new StoreException("SQL Exception : " + s);
}
}
- protected User createUser(final User user) throws StoreException {
+ User createUser(final User user) throws StoreException {
requireNonNull(user);
requireNonNull(user.getName());
requireNonNull(user.getDomainid());
- final PasswordHash passwordHash = passwordService.getPasswordHash(user.getPassword());
+ final var passwordHash = passwordService.getPasswordHash(user.getPassword());
user.setSalt(passwordHash.getSalt());
- String query =
- "insert into users"
- + " (userid,domainid,name,email,password,description,enabled,salt) values(?,?,?,?,?,?,?,?)";
- try (Connection conn = dbConnect(); PreparedStatement statement = conn.prepareStatement(query)) {
+
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("INSERT INTO " + TABLE + " ("
+ + COL_ID + ", " + COL_DOMAIN_ID + ", " + COL_NAME + ", " + COL_EMAIL + ", " + COL_PASSWORD + ", "
+ + COL_DESC + ", " + COL_ENABLED + ", " + COL_SALT + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?)")) {
user.setUserid(IDMStoreUtil.createUserid(user.getName(), user.getDomainid()));
- statement.setString(1, user.getUserid());
- statement.setString(2, user.getDomainid());
- statement.setString(3, user.getName());
- statement.setString(4, user.getEmail());
- statement.setString(5, passwordHash.getHashedPassword());
- statement.setString(6, user.getDescription());
- statement.setInt(7, user.isEnabled() ? 1 : 0);
- statement.setString(8, user.getSalt());
- int affectedRows = statement.executeUpdate();
- if (affectedRows == 0) {
+ stmt.setString(1, user.getUserid());
+ stmt.setString(2, user.getDomainid());
+ stmt.setString(3, user.getName());
+ stmt.setString(4, user.getEmail());
+ stmt.setString(5, passwordHash.getHashedPassword());
+ stmt.setString(6, user.getDescription());
+ stmt.setInt(7, user.isEnabled() ? 1 : 0);
+ stmt.setString(8, user.getSalt());
+ LOG.debug("createUser() request: {}", stmt);
+
+ if (stmt.executeUpdate() == 0) {
throw new StoreException("Creating user failed, no rows affected.");
}
return user;
}
}
- public User putUser(final User user) throws StoreException {
-
- User savedUser = this.getUser(user.getUserid());
+ User putUser(final User user) throws StoreException {
+ final var savedUser = getUser(user.getUserid());
if (savedUser == null) {
return null;
}
savedUser.setEmail(user.getEmail());
}
if (user.getPassword() != null) {
- // If a new salt is provided, use it. Otherwise, derive salt from
- // existing.
+ // If a new salt is provided, use it. Otherwise, derive salt from existing.
String salt = user.getSalt();
if (salt == null) {
salt = savedUser.getSalt();
}
- final PasswordHash passwordHash = passwordService.getPasswordHash(user.getPassword(), salt);
+ final var passwordHash = passwordService.getPasswordHash(user.getPassword(), salt);
savedUser.setPassword(passwordHash.getHashedPassword());
}
- String query = "UPDATE users SET email = ?, password = ?, description = ?, enabled = ? WHERE userid = ?";
- try (Connection conn = dbConnect(); PreparedStatement statement = conn.prepareStatement(query)) {
- statement.setString(1, savedUser.getEmail());
- statement.setString(2, savedUser.getPassword());
- statement.setString(3, savedUser.getDescription());
- statement.setInt(4, savedUser.isEnabled() ? 1 : 0);
- statement.setString(5, savedUser.getUserid());
- statement.executeUpdate();
+ try (var conn = dbConnect();
+ var stmt = conn.prepareStatement("UPDATE " + TABLE + " SET " + COL_EMAIL + " = ?, "
+ + COL_PASSWORD + " = ?," + COL_DESC + " = ?, " + COL_ENABLED + "= ? WHERE " + COL_ID + " = ?")) {
+ stmt.setString(1, savedUser.getEmail());
+ stmt.setString(2, savedUser.getPassword());
+ stmt.setString(3, savedUser.getDescription());
+ stmt.setInt(4, savedUser.isEnabled() ? 1 : 0);
+ stmt.setString(5, savedUser.getUserid());
+ LOG.debug("putUser() request: {}", stmt);
+
+ stmt.executeUpdate();
} catch (SQLException s) {
throw new StoreException("SQL Exception : " + s);
}
return savedUser;
}
- @SuppressFBWarnings("SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE")
- protected User deleteUser(String userid) throws StoreException {
- userid = StringEscapeUtils.escapeHtml4(userid);
- User savedUser = this.getUser(userid);
+ @SuppressFBWarnings(value = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", justification = "Weird original code")
+ User deleteUser(final String userid) throws StoreException {
+ // FIXME: remove this once we have a more modern H2
+ final var escaped = StringEscapeUtils.escapeHtml4(userid);
+ final var savedUser = getUser(escaped);
if (savedUser == null) {
return null;
}
- String query = String.format("DELETE FROM USERS WHERE userid = '%s'", userid);
- try (Connection conn = dbConnect(); Statement statement = conn.createStatement()) {
- int deleteCount = statement.executeUpdate(query);
+ try (var conn = dbConnect();
+ var stmt = conn.createStatement()) {
+ // FIXME: prepare statement instead
+ final var query = String.format("DELETE FROM " + TABLE + " WHERE " + COL_ID + " = '%s'", escaped);
+ LOG.debug("deleteUser() request: {}", query);
+
+ int deleteCount = stmt.executeUpdate(query);
LOG.debug("deleted {} records", deleteCount);
return savedUser;
} catch (SQLException s) {
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.aaa.api.model.Domain;
import org.opendaylight.aaa.api.model.Domains;
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class DomainStoreTest {
-
private final Connection connectionMock = mock(Connection.class);
-
- private final ConnectionProvider connectionFactoryMock = () -> connectionMock;
-
- private final DomainStore domainStoreUnderTest = new DomainStore(connectionFactoryMock);
+ private final DomainStore domainStoreUnderTest = new DomainStore(() -> connectionMock);
@Test
- public void getDomainsTest() throws SQLException, Exception {
+ public void getDomainsTest() throws Exception {
// Setup Mock Behavior
- String[] tableTypes = { "TABLE" };
- when(connectionMock.isClosed()).thenReturn(false);
DatabaseMetaData dbmMock = mock(DatabaseMetaData.class);
when(connectionMock.getMetaData()).thenReturn(dbmMock);
ResultSet rsUserMock = mock(ResultSet.class);
- when(dbmMock.getTables(null, null, "DOMAINS", tableTypes)).thenReturn(rsUserMock);
+ when(dbmMock.getTables(null, null, DomainStore.TABLE, AbstractStore.TABLE_TYPES)).thenReturn(rsUserMock);
when(rsUserMock.next()).thenReturn(true);
Statement stmtMock = mock(Statement.class);
assertNull(ds.getDomain(domainId));
}
- public ResultSet getMockedResultSet() throws SQLException {
+ private static ResultSet getMockedResultSet() throws SQLException {
ResultSet rsMock = mock(ResultSet.class);
when(rsMock.next()).thenReturn(true).thenReturn(false);
- when(rsMock.getInt(DomainStore.SQL_ID)).thenReturn(1);
- when(rsMock.getString(DomainStore.SQL_NAME)).thenReturn("DomainName_1");
- when(rsMock.getString(DomainStore.SQL_DESCR)).thenReturn("Desc_1");
- when(rsMock.getInt(DomainStore.SQL_ENABLED)).thenReturn(1);
+ when(rsMock.getString(DomainStore.COL_ID)).thenReturn("1");
+ when(rsMock.getString(DomainStore.COL_NAME)).thenReturn("DomainName_1");
+ when(rsMock.getString(DomainStore.COL_DESC)).thenReturn("Desc_1");
+ when(rsMock.getInt(DomainStore.COL_ENABLED)).thenReturn(1);
return rsMock;
}
}
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.aaa.api.model.Grants;
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class GrantStoreTest {
+ private static final String DOMAIN_ID = "5";
+ private static final String USER_ID = "5";
private final Connection connectionMock = mock(Connection.class);
-
- private final ConnectionProvider connectionFactoryMock = () -> connectionMock;
-
- private final GrantStore grantStoreUnderTest = new GrantStore(connectionFactoryMock);
-
- private final String did = "5";
- private final String uid = "5";
+ private final GrantStore grantStoreUnderTest = new GrantStore(() -> connectionMock);
@Test
public void getGrantsTest() throws Exception {
// Setup Mock Behavior
- String[] tableTypes = { "TABLE" };
- when(connectionMock.isClosed()).thenReturn(false);
DatabaseMetaData dbmMock = mock(DatabaseMetaData.class);
when(connectionMock.getMetaData()).thenReturn(dbmMock);
ResultSet rsUserMock = mock(ResultSet.class);
- when(dbmMock.getTables(null, null, "GRANTS", tableTypes)).thenReturn(rsUserMock);
+ when(dbmMock.getTables(null, null, GrantStore.TABLE, AbstractStore.TABLE_TYPES)).thenReturn(rsUserMock);
when(rsUserMock.next()).thenReturn(true);
PreparedStatement pstmtMock = mock(PreparedStatement.class);
when(pstmtMock.executeQuery()).thenReturn(rsMock);
// Run Test
- Grants grants = grantStoreUnderTest.getGrants(did, uid);
+ Grants grants = grantStoreUnderTest.getGrants(DOMAIN_ID, USER_ID);
// Verify
assertEquals(1, grants.getGrants().size());
verify(pstmtMock).close();
}
- public ResultSet getMockedResultSet() throws SQLException {
+ private static ResultSet getMockedResultSet() throws SQLException {
ResultSet rsMock = mock(ResultSet.class);
when(rsMock.next()).thenReturn(true).thenReturn(false);
- when(rsMock.getInt(GrantStore.SQL_ID)).thenReturn(1);
- when(rsMock.getString(GrantStore.SQL_TENANTID)).thenReturn(did);
- when(rsMock.getString(GrantStore.SQL_USERID)).thenReturn(uid);
- when(rsMock.getString(GrantStore.SQL_ROLEID)).thenReturn("Role_1");
-
+ when(rsMock.getString(GrantStore.COL_ID)).thenReturn("1");
+ when(rsMock.getString(GrantStore.COL_TENANTID)).thenReturn(DOMAIN_ID);
+ when(rsMock.getString(GrantStore.COL_USERID)).thenReturn(USER_ID);
+ when(rsMock.getString(GrantStore.COL_ROLEID)).thenReturn("Role_1");
return rsMock;
}
-
}
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.aaa.api.model.Roles;
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class RoleStoreTest {
-
private final Connection connectionMock = mock(Connection.class);
-
- private final ConnectionProvider connectionFactoryMock = () -> connectionMock;
-
- private final RoleStore roleStoreUnderTest = new RoleStore(connectionFactoryMock);
+ private final RoleStore roleStoreUnderTest = new RoleStore(() -> connectionMock);
@Test
- public void getRolesTest() throws SQLException, Exception {
+ public void getRolesTest() throws Exception {
// Setup Mock Behavior
- String[] tableTypes = { "TABLE" };
- when(connectionMock.isClosed()).thenReturn(false);
DatabaseMetaData dbmMock = mock(DatabaseMetaData.class);
when(connectionMock.getMetaData()).thenReturn(dbmMock);
ResultSet rsUserMock = mock(ResultSet.class);
- when(dbmMock.getTables(null, null, "ROLES", tableTypes)).thenReturn(rsUserMock);
+ when(dbmMock.getTables(null, null, RoleStore.TABLE, AbstractStore.TABLE_TYPES)).thenReturn(rsUserMock);
when(rsUserMock.next()).thenReturn(true);
Statement stmtMock = mock(Statement.class);
verify(stmtMock).close();
}
- public ResultSet getMockedResultSet() throws SQLException {
+ private static ResultSet getMockedResultSet() throws SQLException {
ResultSet rsMock = mock(ResultSet.class);
when(rsMock.next()).thenReturn(true).thenReturn(false);
- when(rsMock.getInt(RoleStore.SQL_ID)).thenReturn(1);
- when(rsMock.getString(RoleStore.SQL_NAME)).thenReturn("RoleName_1");
- when(rsMock.getString(RoleStore.SQL_DESCR)).thenReturn("Desc_1");
+ when(rsMock.getString(RoleStore.COL_ID)).thenReturn("1");
+ when(rsMock.getString(RoleStore.COL_NAME)).thenReturn("RoleName_1");
+ when(rsMock.getString(RoleStore.COL_DOMAIN_ID)).thenReturn("Domain_1");
+ when(rsMock.getString(RoleStore.COL_DESC)).thenReturn("Desc_1");
return rsMock;
}
}
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.aaa.api.model.Users;
import org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService;
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class UserStoreTest {
-
private final Connection connectionMock = mock(Connection.class);
-
- private final ConnectionProvider connectionFactoryMock = () -> connectionMock;
-
- private final UserStore userStoreUnderTest = new UserStore(connectionFactoryMock,
+ private final UserStore userStoreUnderTest = new UserStore(() -> connectionMock,
new DefaultPasswordHashService());
@Test
- public void getUsersTest() throws SQLException, Exception {
+ public void getUsersTest() throws Exception {
// Setup Mock Behavior
- String[] tableTypes = { "TABLE" };
- when(connectionMock.isClosed()).thenReturn(false);
DatabaseMetaData dbmMock = mock(DatabaseMetaData.class);
when(connectionMock.getMetaData()).thenReturn(dbmMock);
ResultSet rsUserMock = mock(ResultSet.class);
- when(dbmMock.getTables(null, null, "USERS", tableTypes)).thenReturn(rsUserMock);
+ when(dbmMock.getTables(null, null, UserStore.TABLE, AbstractStore.TABLE_TYPES)).thenReturn(rsUserMock);
when(rsUserMock.next()).thenReturn(true);
Statement stmtMock = mock(Statement.class);
verify(stmtMock).close();
}
- public ResultSet getMockedResultSet() throws SQLException {
+ private static ResultSet getMockedResultSet() throws SQLException {
ResultSet rsMock = mock(ResultSet.class);
when(rsMock.next()).thenReturn(true).thenReturn(false);
- when(rsMock.getInt(UserStore.SQL_ID)).thenReturn(1);
- when(rsMock.getString(UserStore.SQL_NAME)).thenReturn("Name_1");
- when(rsMock.getString(UserStore.SQL_EMAIL)).thenReturn("Name_1@company.com");
- when(rsMock.getString(UserStore.SQL_PASSWORD)).thenReturn("Pswd_1");
- when(rsMock.getString(UserStore.SQL_DESCR)).thenReturn("Desc_1");
- when(rsMock.getInt(UserStore.SQL_ENABLED)).thenReturn(1);
+ when(rsMock.getString(UserStore.COL_ID)).thenReturn("1");
+ when(rsMock.getString(UserStore.COL_NAME)).thenReturn("Name_1");
+ when(rsMock.getString(UserStore.COL_EMAIL)).thenReturn("Name_1@company.com");
+ when(rsMock.getString(UserStore.COL_PASSWORD)).thenReturn("Pswd_1");
+ when(rsMock.getString(UserStore.COL_DESC)).thenReturn("Desc_1");
+ when(rsMock.getString(UserStore.COL_DOMAIN_ID)).thenReturn("Domain_1");
+ when(rsMock.getInt(UserStore.COL_ENABLED)).thenReturn(1);
return rsMock;
}
}