src/main/java/com/enterprisepasswordsafe/database/GroupDAO.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.engine.users.UserClassifier;
import com.enterprisepasswordsafe.engine.utils.IDGenerator;
import org.apache.commons.csv.CSVRecord;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
/**
* Data access object for the group objects.
*/
public class GroupDAO extends GroupStoreManipulator
implements EntityWithAccessRightsDAO<Group, User> {
/**
* The clause for excluding reserved system groups
*/
private static final String EXCLUDE_RESERVED_GROUP_CLAUSE =
"grp.group_id != '0' AND grp.group_id != '1' AND grp.group_id != '2' AND grp.group_id != '3'";
/**
* The SQL to get a particular group by its' ID.
*/
private static final String GET_BY_ID_SQL = "SELECT " + GROUP_FIELDS +" FROM groups grp" + " WHERE grp.group_id = ?";
/**
* The SQL to get a particular group by its' name.
*/
private static final String GET_BY_NAME_SQL = "SELECT " + GROUP_FIELDS
+ " FROM groups grp " + " WHERE grp.group_name = ? AND grp.status = "+Group.STATUS_ENABLED;
/**
* The SQL statement to get all the available groups.
*/
private static final String GET_ALL_GROUPS_SQL = "SELECT " + GROUP_FIELDS
+ " FROM groups grp "
+ " WHERE grp.status < " + Group.STATUS_DELETED
+ " ORDER BY grp.group_name ASC";
/**
* The SQL statement to get all the available groups.
*/
private static final String GET_ALL_GROUP_IDS_SQL = "SELECT group_id FROM groups grp "
+ " WHERE " + EXCLUDE_RESERVED_GROUP_CLAUSE+" AND grp.status < " + Group.STATUS_DELETED
+ " ORDER BY grp.group_name ASC";
/**
* The SQL statement to get all the available groups.
*/
private static final String GET_ALL_NON_SYSTEM_GROUPS_SQL = "SELECT " + GROUP_FIELDS + " FROM groups grp "
+ " WHERE " + EXCLUDE_RESERVED_GROUP_CLAUSE+" AND grp.status < " + Group.STATUS_DELETED
+ " ORDER BY grp.group_name ASC";
/**
* The SQL statement to get all the available enabled groups.
*/
private static final String GET_ALL_ENABLED_GROUPS_SQL = "SELECT " + GROUP_FIELDS+ " FROM groups grp "
+ " WHERE " + EXCLUDE_RESERVED_GROUP_CLAUSE + " AND grp.status = " + Group.STATUS_ENABLED
+ " ORDER BY grp.group_name ASC";
/**
* The SQL write a groups details to the database.
*/
private static final String WRITE_GROUP_SQL =
"INSERT INTO groups(group_id, group_name, status)"
+ " VALUES( ?, ?, "+Group.STATUS_ENABLED+")";
/**
* The SQL to count the number of members in a group.
*/
private static final String COUNT_SQL = "SELECT count(*) FROM membership WHERE group_id = ?";
/**
* The SQL to get the user summary for a search
*/
private static final String GET_SUMMARY_BY_SEARCH =
"SELECT " + GROUP_FIELDS + " FROM groups WHERE group_name like ? ";
private final UserClassifier userClassifier = new UserClassifier();
/**
* Private constructor to prevent instantiation
*/
private GroupDAO() {
super(GET_BY_ID_SQL, GET_BY_NAME_SQL, COUNT_SQL);
}
public void importGroup(final User theImporter, final CSVRecord record)
throws SQLException, GeneralSecurityException, UnsupportedEncodingException {
Iterator<String> valueIterator = record.iterator();
if (!valueIterator.hasNext()) {
throw new GeneralSecurityException("No groupname specified.");
}
String groupName = valueIterator.next().trim();
Group theGroup = create(theImporter, groupName);
Group adminGroup = getAdminGroup(theImporter);
while(valueIterator.hasNext()) {
String memberName = valueIterator.next();
User thisUser = UserDAO.getInstance().getByName(memberName);
if (thisUser == null) {
throw new GeneralSecurityException(memberName + " does not exist");
}
thisUser.decryptAdminAccessKey(adminGroup);
MembershipDAO.getInstance().create(thisUser, theGroup);
}
}
public Group create(final User theCreator, final String id, final String groupName)
throws SQLException, GeneralSecurityException, UnsupportedEncodingException {
Group theGroup = getByName(groupName);
if (theGroup != null) {
throw new GeneralSecurityException("The group already exists");
}
// Create the group and ensure that user 0 is a member.
Group newGroup = new Group(id, groupName, true);
write(newGroup);
MembershipDAO mDAO = MembershipDAO.getInstance();
mDAO.create(theCreator, newGroup);
TamperproofEventLogDAO.getInstance().create(TamperproofEventLog.LOG_LEVEL_GROUP_MANIPULATION,
theCreator, "Created the group {group:" + newGroup.getGroupId() + "}", true);
// Ensure the creating user is part of the group if they are not the
// admin user.
if (!userClassifier.isMasterAdmin(theCreator)) {
mDAO.create(theCreator, newGroup);
}
return newGroup;
}
public Group create(final User theCreator, final String groupName)
throws SQLException, GeneralSecurityException, UnsupportedEncodingException {
return create( theCreator, IDGenerator.getID(), groupName);
}
public Group getAdminGroup(final User theUser)
throws SQLException, GeneralSecurityException {
// Get the admin group either directly (if the user is an admin),
// or indirectly (if the user is only a sub-admin).
Group adminGroup = getById(Group.ADMIN_GROUP_ID);
if (userClassifier.isAdministrator(theUser)) {
Membership adminMembership = MembershipDAO.getInstance().getMembership(theUser, Group.ADMIN_GROUP_ID);
adminGroup.updateAccessKey(adminMembership);
} else if (userClassifier.isSubadministrator(theUser)) {
Membership subAdminMembership =
MembershipDAO.getInstance().getMembership(theUser, Group.SUBADMIN_GROUP_ID);
Group subAdminGroup = getById(Group.SUBADMIN_GROUP_ID);
subAdminGroup.updateAccessKey(subAdminMembership);
adminGroup.setAccessKey(subAdminGroup.getAccessKey());
} else {
return null;
}
return adminGroup;
}
@Override
public Group getByIdDecrypted(final String groupId, final User user)
throws SQLException, GeneralSecurityException {
Group theGroup = userClassifier.isAdministrator(user) ?
UnfilteredGroupDAO.getInstance().getById(groupId) : getById(groupId);
if( theGroup == null || theGroup.getStatus() == Group.STATUS_DELETED) {
return null;
}
Membership mem = MembershipDAO.getInstance().getMembership(user, groupId);
if( mem == null ) {
return null;
}
theGroup.updateAccessKey(mem);
return theGroup;
}
public void write(final Group group)
throws SQLException {
runResultlessParameterisedSQL(WRITE_GROUP_SQL, group.getGroupId(), group.getGroupName());
}
public List<Group> getAll()
throws SQLException {
return getMultiple(GET_ALL_GROUPS_SQL);
}
public List<Group> getNonSystem() throws SQLException {
return getMultiple(GET_ALL_NON_SYSTEM_GROUPS_SQL);
}
public List<Group> getAllEnabled()
throws SQLException {
return getMultiple(GET_ALL_ENABLED_GROUPS_SQL);
}
public List<String> getAllIds()
throws SQLException {
return getFieldValues(GET_ALL_GROUP_IDS_SQL);
}
public List<Group> searchNames(String searchQuery)
throws SQLException {
if(searchQuery == null) {
searchQuery = "%";
} else if(searchQuery.indexOf('%') == -1) {
searchQuery += "%";
}
return getMultiple(GET_SUMMARY_BY_SEARCH, searchQuery);
}
public boolean nameExists(String groupName) throws SQLException {
return (getByName(groupName) != null);
}
public boolean idExists(String groupId) throws SQLException {
return getById(groupId) != null;
}
//------------------------
private static final class InstanceHolder {
static final GroupDAO INSTANCE = new GroupDAO();
}
public static GroupDAO getInstance() {
return InstanceHolder.INSTANCE;
}
}