src/main/java/com/enterprisepasswordsafe/database/UserAccessControlDAO.java
/*
* Copyright (c) 2017 Carbon Security Ltd. <opensource@carbonsecurity.co.uk>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.enterprisepasswordsafe.database;
import com.enterprisepasswordsafe.database.schema.AccessControlDAOInterface;
import com.enterprisepasswordsafe.engine.AccessControlDecryptor;
import com.enterprisepasswordsafe.engine.accesscontrol.PasswordPermission;
import com.enterprisepasswordsafe.engine.accesscontrol.UserAccessControl;
import com.enterprisepasswordsafe.engine.utils.KeyUtils;
import javax.crypto.BadPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class UserAccessControlDAO
extends AbstractAccessControlDAO
implements AccessControlDAOInterface<User, UserAccessControl> {
public static final String UAC_FIELDS = " uac.item_id, uac.mkey, uac.rkey, uac.user_id ";
private static final String WRITE_UAC_SQL =
"INSERT INTO user_access_control(user_id, item_id, rkey, mkey) VALUES( ?, ?, ?, ?)";
private static final String GET_UAC_SQL =
"SELECT " + UAC_FIELDS + " FROM user_access_control uac WHERE uac.user_id = ? AND uac.item_id = ? "
+ " AND uac.rkey is not null";
private static final String GET_ALL_UAC_FOR_USER_SQL =
"SELECT " + UAC_FIELDS + " FROM user_access_control uac WHERE uac.user_id = ? ";
private static final String DELETE_SQL =
"DELETE FROM user_access_control WHERE user_id = ? AND item_id = ?";
private static final String DELETE_ALL_FOR_ITEM_SQL =
"DELETE FROM user_access_control WHERE item_id = ?";
private static final String GET_UAC_SUMMARIES_UAC_SQL =
"SELECT uac.rkey, uac.mkey FROM user_access_control uac "
+ " WHERE uac.item_id = ? AND uac.user_id = ? AND uac.rkey is not null ";
private static final String GET_UAC_SUMMARIES_UAR_SQL =
"SELECT uar.role FROM user_access_roles uar WHERE uar.item_id = ? AND uar.actor_id = ?";
private UserAccessControlDAO( ) {
super();
}
@Override
public UserAccessControl create(final User theUser, final AccessControledObject item, PasswordPermission permission)
throws SQLException, UnsupportedEncodingException, GeneralSecurityException {
return create(theUser, item, permission, true);
}
public UserAccessControl create(final User theUser, final AccessControledObject item,
final PasswordPermission permission, final boolean writeToDB)
throws SQLException, GeneralSecurityException {
if( !permission.allowsRead ) {
UserAccessControl existingUac = get(theUser, item);
if( existingUac != null ) {
delete(existingUac);
}
return null;
}
PrivateKey modifyKey = null;
if( permission.allowsModification ) {
modifyKey = item.getModifyKey();
}
UserAccessControl newUac = new UserAccessControl(theUser.getId(), item.getId(), modifyKey, item.getReadKey());
if( writeToDB ) {
write( newUac, theUser.getKeyEncrypter() );
}
return newUac;
}
@Override
public void write(final User user, final UserAccessControl uac)
throws SQLException, GeneralSecurityException
{
write(uac, user.getKeyEncrypter());
}
public void write(final UserAccessControl uac, final Encrypter encrypter )
throws SQLException, GeneralSecurityException
{
try(PreparedStatement ps = BOMFactory.getCurrentConntection().prepareStatement(WRITE_UAC_SQL)) {
ps.setString(1, uac.getUserId());
ps.setString(2, uac.getItemId());
ps.setBytes (3, KeyUtils.encryptKey(uac.getReadKey(), encrypter));
ps.setBytes (4, KeyUtils.encryptKey(uac.getModifyKey(), encrypter));
ps.executeUpdate();
}
}
@Override
public UserAccessControl get(final User user, final AccessControledObject item)
throws SQLException, GeneralSecurityException {
if (item == null) {
return null;
}
return get(user, item.getId());
}
public UserAccessControl get(final User user, final String itemId)
throws SQLException, GeneralSecurityException {
if (user == null || itemId == null) {
return null;
}
try(PreparedStatement ps = BOMFactory.getCurrentConntection().prepareStatement(GET_UAC_SQL)) {
ps.setString(1, user.getId());
ps.setString(2, itemId);
ps.setMaxRows(1);
try(ResultSet rs = ps.executeQuery()) {
return rs.next() ? buildFromResultSet(rs, user) : null;
}
}
}
public void delete(UserAccessControl uac)
throws SQLException {
try(PreparedStatement ps = BOMFactory.getCurrentConntection().prepareStatement(DELETE_SQL)) {
ps.setString(1, uac.getUserId());
ps.setString(2, uac.getItemId());
ps.executeUpdate();
}
}
public void deleteAllForItem(AccessControledObject aco)
throws SQLException {
try(PreparedStatement ps = BOMFactory.getCurrentConntection().prepareStatement(DELETE_ALL_FOR_ITEM_SQL)) {
ps.setString(1, aco.getId());
ps.executeUpdate();
}
}
public void updateEncryptionOnKeys(final User user, final Encrypter encrypter)
throws SQLException, GeneralSecurityException {
if (user == null) {
return;
}
List<UserAccessControl> encryptionList = new ArrayList<>();
try(PreparedStatement ps = BOMFactory.getCurrentConntection().prepareStatement(GET_ALL_UAC_FOR_USER_SQL)) {
ps.setString(1, user.getId());
try(ResultSet rs = ps.executeQuery()) {
while(rs.next()) {
try {
final UserAccessControl ac = buildFromResultSet(rs, user);
if(ac.getReadKey() != null || ac.getModifyKey() != null) {
encryptionList.add(ac);
}
} catch(BadPaddingException e) {
Logger.getAnonymousLogger().log(Level.SEVERE, "User "+user.getUserName()+" encountered a problem on key update.", e);
}
}
}
}
for(UserAccessControl ac : encryptionList ) {
update(ac, encrypter);
}
}
public Set<AccessSummary> getSummaries(final AccessControledObject item)
throws SQLException {
Set<AccessSummary> summaries = new TreeSet<>();
try(PreparedStatement uacPS = BOMFactory.getCurrentConntection().prepareStatement(GET_UAC_SUMMARIES_UAC_SQL)) {
try(PreparedStatement uarPS = BOMFactory.getCurrentConntection().prepareStatement(GET_UAC_SUMMARIES_UAR_SQL)) {
uacPS.setString(1, item.getId());
uarPS.setString(1, item.getId());
for(User thisUser : UserDAO.getInstance().getAll()) {
boolean canRead = false;
boolean canModify = false;
uacPS.setString(2, thisUser.getId());
uacPS.setMaxRows(1);
try(ResultSet rs = uacPS.executeQuery()) {
if( rs.next() ) {
rs.getBytes(1); // Read the read key
canRead = !rs.wasNull();
rs.getBytes(2); // Read the modify key
canModify = !rs.wasNull();
}
}
uarPS.setString(2, thisUser.getId());
AbstractAccessControlDAO.Permissions permissions = getPermissions(uarPS);
AccessSummary gas = new AccessSummary(thisUser.getId(), thisUser.getUserName(),
canRead, canModify, permissions.canApproveRARequest, permissions.canViewHistory);
summaries.add(gas);
}
return summaries;
}
}
}
public void update(final User user, final UserAccessControl uac)
throws SQLException, GeneralSecurityException {
update(uac, user.getKeyEncrypter());
}
public void update(final UserAccessControl uac, final Encrypter encrypter)
throws SQLException, GeneralSecurityException {
// TODO: Look at improved update
delete(uac);
write(uac, encrypter);
}
static UserAccessControl buildFromResultSet(final ResultSet rs,
final AccessControlDecryptor decryptor)
throws SQLException, GeneralSecurityException {
return UserAccessControl.builder()
.withItemId(rs.getString(1))
.withModifyKey(
KeyUtils.decryptPrivateKey(rs.getBytes(1 +1), decryptor.getKeyDecrypter()))
.withReadKey(
KeyUtils.decryptPublicKey(rs.getBytes(1 +2), decryptor.getKeyDecrypter()))
.withAccessorId(rs.getString(1 +3))
.build();
}
//------------------------
private static final class InstanceHolder {
static final UserAccessControlDAO INSTANCE = new UserAccessControlDAO();
}
public static UserAccessControlDAO getInstance() {
return InstanceHolder.INSTANCE;
}
}