/*******************************************************************************
* Copyright (c) 2013, 2014 Lectorius, Inc.
* Authors:
* Vijay Pandurangan (vijayp@mitro.co)
* Evan Jones (ej@mitro.co)
* Adam Hilss (ahilss@mitro.co)
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can contact the authors at inbound@mitro.co.
*******************************************************************************/
package co.mitro.core.servlets;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import co.mitro.core.exceptions.MitroServletException;
import co.mitro.core.server.data.DBAcl;
import co.mitro.core.server.data.DBAcl.AccessLevelType;
import co.mitro.core.server.data.DBAcl.CyclicGroupError;
import co.mitro.core.server.data.DBGroup;
import co.mitro.core.server.data.DBGroupSecret;
import co.mitro.core.server.data.DBIdentity;
import co.mitro.core.server.data.DBServerVisibleSecret;
import co.mitro.core.server.data.RPC;
import co.mitro.core.server.data.RPC.AddGroupRequest.ACL;
import co.mitro.core.server.data.RPC.AddGroupResponse;
import co.mitro.core.server.data.RPC.EditGroupRequest;
import co.mitro.core.servlets.MitroServlet.MitroRequestContext;
public class EditGroupTest extends OrganizationsFixture {
private EditGroupRequest request;
private EditGroup servlet;
@Before
public void setUp() throws SQLException {
request = createEditGroupRequest(testGroup);
servlet = new EditGroup();
}
@Test
public void testGetOrgAdminsForGroup() throws SQLException, CyclicGroupError {
DBGroup groupForTest = createGroupContainingIdentity(testIdentity);
DBAcl acl = new DBAcl();
acl.setGroup(testGroup);
acl.setMemberGroup(groupForTest);
acl.setLevel(DBAcl.AccessLevelType.ADMIN);
acl.setGroupKeyEncryptedForMe("encrypted group key");
manager.aclDao.create(acl);
assertEquals(1, manager.groupDao.refresh(testGroup));
assertEquals(2, testGroup.getAcls().size());
Set<Integer> orgAdmins = EditGroup.getOrgAdminsForGroup(manager, testGroup);
assertEquals(1, orgAdmins.size());
assertEquals(testIdentity.getId(), orgAdmins.iterator().next().intValue());
}
/** Returns a valid "do nothing" EditGroupRequest for group. */
private EditGroupRequest createEditGroupRequest(DBGroup group) throws SQLException {
EditGroupRequest r = new EditGroupRequest();
r.groupId = group.getId();
r.name = group.getName();
r.autoDelete = group.isAutoDelete();
r.publicKey = group.getPublicKeyString();
r.acls = new ArrayList<>();
for (DBAcl acl : group.getAcls()) {
RPC.AddGroupRequest.ACL rpcAcl = new RPC.AddGroupRequest.ACL();
rpcAcl.level = acl.getLevel();
rpcAcl.groupKeyEncryptedForMe = acl.getGroupKeyEncryptedForMe();
rpcAcl.memberIdentity = acl.loadMemberIdentity(manager.identityDao).getName();
rpcAcl.myPublicKey = testIdentity.getPublicKeyString();
assert rpcAcl.memberIdentity != null;
r.acls.add(rpcAcl);
}
r.secrets = new ArrayList<>();
for (DBGroupSecret groupSecret : group.getGroupSecrets()) {
RPC.Secret rpcSecret = new RPC.Secret();
rpcSecret.encryptedClientData = "client";
rpcSecret.encryptedCriticalData = "critical";
rpcSecret.secretId = groupSecret.getServerVisibleSecret().getId();
r.secrets.add(rpcSecret);
}
return r;
}
private AddGroupResponse processCommand(DBIdentity identity)
throws IOException, SQLException, MitroServletException {
return (AddGroupResponse) servlet.processCommand(
new MitroRequestContext(identity, gson.toJson(request), manager, null));
}
@Test
public void testEditNoOpWithAcl() throws SQLException, IOException, MitroServletException, CyclicGroupError {
DBGroup newGroup = createGroupContainingIdentity(testIdentity);
request.groupId = newGroup.getId();
request.secrets = null;
AddGroupResponse response = processCommand(testIdentity);
assertEquals(newGroup.getId(), response.groupId);
request = createEditGroupRequest(newGroup);
// Same thing should work with empty secrets
request.secrets = new ArrayList<>();
response = processCommand(testIdentity);
assertEquals(newGroup.getId(), response.groupId);
// Add a secret to the group: empty secrets now is an incorrect request
createSecret(newGroup, "client", "critical", null);
try {
processCommand(testIdentity);
fail("expected exception");
} catch (MitroServletException e) {
assertThat(e.getMessage(), containsString("cannot change the number of secrets"));
}
}
@Test
public void testEditNoOpWithoutAcl() throws SQLException, IOException, MitroServletException {
request.acls = null;
AddGroupResponse response = processCommand(testIdentity2);
assertEquals(testGroup.getId(), response.groupId);
request.acls = Collections.emptyList();
response = processCommand(testIdentity2);
assertEquals(testGroup.getId(), response.groupId);
}
@Test
public void testRemoveUserFromGroup() throws Exception {
// only contains testIdentity
DBGroup privateGroup = createGroupContainingIdentity(testIdentity);
// Add testIdentity2 to testGroup
addToGroup(testIdentity2, testGroup, AccessLevelType.ADMIN);
// Add a secret to the group
DBServerVisibleSecret svs = createSecret(testGroup, "client", "critical", null);
addSecretToGroup(svs, privateGroup, "client", "critical");
// create the edit request then remove testIdentity from testGroup
request = createEditGroupRequest(testGroup);
int testIdentityIndex = -1;
for (int i = 0; i < request.acls.size(); i++) {
if (request.acls.get(i).memberIdentity.equals(testIdentity.getName())) {
testIdentityIndex = i;
break;
}
}
request.acls.remove(testIdentityIndex);
// request should succeed (at one point failed due to "orphaned" secret:
// testIdentity can only edit privateGroup, testIdentity2 can only edit testGroup
AddGroupResponse response = processCommand(testIdentity2);
assertEquals(testGroup.getId(), response.groupId);
}
@Test
public void testAddSecretToMultipleOrgs()
throws IOException, SQLException, MitroServletException, CyclicGroupError {
// share the secret with a user.
DBIdentity org2Admin = outsiders.iterator().next();
DBGroup group = createGroupContainingIdentity(org2Admin);
addSecretToGroup(orgSecret, group, "1", "2");
// create a new org
DBGroup org2 = createOrganization(org2Admin, "org2", outsiders, outsiders);
manager.groupDao.refresh(group);
// turn group into an org2 group.
request = createEditGroupRequest(group);
ACL orgAcl = new ACL();
orgAcl.memberGroup = org2.getId();
orgAcl.groupKeyEncryptedForMe = "asdf";
orgAcl.memberIdentity = null;
orgAcl.level = AccessLevelType.ADMIN;
request.acls.add(orgAcl);
try {
processCommand(org2Admin);
fail("expected exception");
} catch (MitroServletException e) {
if (!e.getMessage().contains("secret to more than one organization")) throw e;
}
}
}