/*******************************************************************************
* 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.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import co.mitro.analysis.AuditLogProcessor;
import co.mitro.core.exceptions.MitroServletException;
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.MitroRPC;
import co.mitro.core.servlets.MitroServlet.MitroRequestContext;
import com.google.common.collect.ImmutableList;
public class RemoveSecretTest extends MemoryDBFixture {
private DBServerVisibleSecret secret;
private RPC.RemoveSecretRequest request;
private RemoveSecret servlet;
@Before
public void setUp() throws SQLException {
secret = createSecret(testGroup, "client", "critical", null);
request = new RPC.RemoveSecretRequest();
request.secretId = secret.getId();
request.groupId = testGroup.getId();
servlet = new RemoveSecret();
}
@Test
public void testRemoveFromLastGroup() throws Exception {
// remove secret from testGroup: deleted!
removeSecret(testIdentity);
assertNull(manager.svsDao.queryForId(secret.getId()));
manager.setRequestor(testIdentity, "device");
assertEquals(true, hasAudit(manager, AuditLogProcessor.ActionType.REVOKED_ACCESS_TO, testIdentity, testIdentity, secret.getId()));
}
@Test
public void testRemoveFromSingleGroup() throws Exception {
// add secret to testGroup and group2
DBGroup g2 = createGroupContainingIdentity(testIdentity);
addSecretToGroup(secret, g2, "client", "critical");
// remove secret from testGroup: still in g2
removeSecret(testIdentity);
assertNotNull(manager.getGroupSecret(g2, secret));
assertNotNull(manager.svsDao.queryForId(secret.getId()));
}
@Test
public void deleteSecretAsOrgAdmin() throws IOException, SQLException, MitroServletException, CyclicGroupError {
List<DBIdentity> admins = ImmutableList.of(testIdentity2);
List<DBIdentity> members = ImmutableList.of();
DBGroup topLevelOrganization = createOrganization(
testIdentity2, "Organization", admins, members);
DBGroup g2 = createGroupContainingIdentity(testIdentity);
addOrgToGroup(manager, topLevelOrganization, g2, AccessLevelType.ADMIN);
DBServerVisibleSecret newSecret = createSecret(g2, "client", "critical", null);
request.secretId = newSecret.getId();
request.groupId = g2.getId();
// we should be able to delete the secret as the admin.
removeSecret(testIdentity2);
}
@Test
public void testRemoveFromAllGroups() throws Exception {
// add secret to testGroup and group2
DBGroup g2 = createGroupContainingIdentity(testIdentity);
addSecretToGroup(secret, g2, "client", "critical");
DBGroupSecret matchObject = new DBGroupSecret();
matchObject.setServerVisibleSecret(secret);
assertEquals(2, manager.groupSecretDao.queryForMatchingArgs(matchObject).size());
// remove secret from all groups: deletes the secret
request.groupId = null;
removeSecret(testIdentity);
assertEquals(0, manager.groupSecretDao.queryForMatchingArgs(matchObject).size());
assertNull(manager.svsDao.queryForId(secret.getId()));
}
@Test
public void testNoSecret() throws Exception {
request.secretId += 1;
assertRemoveException("does not exist", testIdentity);
}
@Test
public void testWrongGroup() throws Exception {
request.groupId += 1;
assertRemoveException("not in group", testIdentity);
}
@Test
public void testRemoveFromNonMemberGroup() throws Exception {
// testIdentity2: not permitted to remove secret from testGroup (does not have access)
assertRemoveException("does not have access", testIdentity2);
// Create group for testIdentity2, adding secret
DBGroup id2Group = createGroupContainingIdentity(testIdentity2);
addSecretToGroup(secret, id2Group, "client", "critical");
// remove secret from testGroup, as testIdentity2
// happens when user1 shares a secret with id2Group, and user2 in id2Group shares with user3
// this causes user2 to remove user1, creating a group with (user1, user3)
removeSecret(testIdentity2);
}
@SuppressWarnings("deprecation")
@Test
public void testDeprecatedUserId() throws IOException, SQLException, MitroServletException {
request.myUserId = testIdentity.getName();
removeSecret(testIdentity);
// sign the request as testIdentity2, but the request itself specifies testIdentity
// protects against spoofing (signing with one key, but pretending to be someone else)
request.myUserId = testIdentity2.getName();
assertRemoveException("does not match rpc requestor", testIdentity);
}
@Test
public void removeFromAutodeleteGroup() throws Exception {
// make testGroup an autodelete group.
testGroup.setAutoDelete(true);
manager.groupDao.update(testGroup);
// share secret with another group and give testIdentity2 access
DBGroup group2 = createGroupContainingIdentity(testIdentity2);
addSecretToGroup(secret, group2, "client", "critical");
// testIdentity2 removes the secret from testGroup
removeSecret(testIdentity2);
// testGroup should be deleted
assertNull(manager.groupDao.queryForId(testGroup.getId()));
// the secret still exists: it was only removed from the one group
assertNotNull(manager.svsDao.queryForId(secret.getId()));
}
@Test
public void removeFromOrganization() throws Exception {
DBGroup organization = createOrganization(
testIdentity, "org", ImmutableList.of(testIdentity), ImmutableList.of(testIdentity2));
// put a secret in a private organization group
DBGroup privateMemberGroup = getPrivateOrgGroup(organization, testIdentity2);
secret = createSecret(privateMemberGroup, "client", "critical", organization);
// remove the secret from the org only: not permitted
request.secretId = secret.getId();
request.groupId = organization.getId();
assertRemoveException("cannot remove secrets from organizations", testIdentity);
assertRemoveException("cannot remove secrets from organizations", testIdentity2);
// delete the secret completely
request.secretId = secret.getId();
request.groupId = null;
// This is now permitted, but we should because testIdentity has admin access,
// but we should require a special flag in the future
// assertRemoveException("does not have access", testIdentity);
// permitted as testIdentity2
removeSecret(testIdentity2);
// the secret still exists, but only as part of the organization
manager.svsDao.refresh(secret);
assertEquals(1, secret.getGroupSecrets().size());
assertEquals(organization, secret.getGroupSecrets().iterator().next().getGroup());
// now fails as testIdentity2
assertRemoveException("does not have access", testIdentity2);
}
private void assertRemoveException(String expectedSubstring, DBIdentity identity)
throws IOException, SQLException {
try {
removeSecret(identity);
fail("expected exception");
} catch (MitroServletException e) {
assertThat(e.getMessage(), containsString(expectedSubstring));
}
}
private void removeSecret(DBIdentity identity)
throws IOException, SQLException, MitroServletException {
MitroRPC response = servlet.processCommand(
new MitroRequestContext(identity, gson.toJson(request), manager, null));
assertNotNull(response);
}
}