/******************************************************************************* * 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.assertTrue; 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; import co.mitro.core.server.data.DBAcl.AccessLevelType; import co.mitro.core.server.data.DBAcl.CyclicGroupError; import co.mitro.core.server.data.DBAudit; 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.AddSecretResponse; import co.mitro.core.servlets.MitroServlet.MitroRequestContext; public class AddSecretTest extends OrganizationsFixture { private AddSecret servlet; private RPC.AddSecretRequest request; @Before public void setUp() throws SQLException, CyclicGroupError { servlet = new AddSecret(); request = new RPC.AddSecretRequest(); request.ownerGroupId = testGroup.getId(); request.encryptedClientData = "client"; request.encryptedCriticalData = "critical"; } @Test public void testAddSecret() throws IOException, SQLException, MitroServletException, CyclicGroupError { manager.setRequestor(testIdentity, null); RPC.AddSecretResponse response = processCommand(testIdentity); DBServerVisibleSecret secret = manager.svsDao.queryForId(response.secretId); DBGroupSecret groupSecret = manager.getGroupSecret(testGroup, secret); assertEquals(request.encryptedClientData, groupSecret.getClientVisibleDataEncrypted()); assertEquals(request.encryptedCriticalData, groupSecret.getCriticalDataEncrypted()); // Add the secret to a new group DBGroup second = createGroupContainingIdentity(testIdentity); request.secretId = response.secretId; request.ownerGroupId = second.getId(); request.encryptedClientData = "fooclient"; request.encryptedCriticalData = "foocritical"; response = processCommand(testIdentity); assertEquals((int) request.secretId, response.secretId); List<DBAudit> audits = DBAudit.getAllActionsByUser(manager, testIdentity); assertEquals(audits.size(), 2); assertEquals(audits.get(0).getAction(), DBAudit.ACTION.ADD_SECRET); assertEquals(audits.get(0).getTargetGroup(), testGroup); assertEquals(audits.get(1).getAction(), DBAudit.ACTION.ADD_SECRET); assertEquals(audits.get(1).getTargetGroup(), second); DBGroupSecret s2 = manager.getGroupSecret(second, secret); assertTrue(s2.getId() != groupSecret.getId()); assertEquals("fooclient", s2.getClientVisibleDataEncrypted()); assertEquals("foocritical", s2.getCriticalDataEncrypted()); // Repeating the request fails processCommandWithException(testIdentity, "Secret is already in group"); } @SuppressWarnings("deprecation") @Test public void deprecatedMyUserId() throws IOException, SQLException, MitroServletException { // wrong user id fails request.myUserId = testIdentity2.getName(); processCommandWithException(testIdentity, "User ID does not match rpc requestor"); // correct user id works request.myUserId = testIdentity.getName(); RPC.AddSecretResponse response = processCommand(testIdentity); DBServerVisibleSecret svs = manager.svsDao.queryForId(response.secretId); assertEquals(svs.getId(), response.secretId); } @Test public void addWithoutAdminAccessSecret() throws Exception { // testIdentity can't access this secret! DBGroup id2Group = createGroupContainingIdentity(testIdentity2); DBServerVisibleSecret svs = createSecret(id2Group, "client", "critical", null); // Add id2 to a new group as admin, and testIdentity as READONLY DBGroup bothIdentities = createGroupContainingIdentity(testIdentity2); DBAcl testIdentityAcl = addToGroup(testIdentity, bothIdentities, AccessLevelType.READONLY); // testIdentity doesn't have access to this secret (only part of id2Group) request.ownerGroupId = bothIdentities.getId(); request.secretId = svs.getId(); processCommandWithException(testIdentity, "User is not an admin"); // testIdentity can't add new secrets to this group (not admin) request.secretId = null; processCommandWithException(testIdentity, "User is not an admin"); // Set testIdentity's access level to ADMIN: works testIdentityAcl.setLevel(AccessLevelType.ADMIN); manager.aclDao.update(testIdentityAcl); AddSecretResponse resp = processCommand(testIdentity); manager.setRequestor(testIdentity, "device"); hasAudit(manager, AuditLogProcessor.ActionType.GRANTED_ACCESS_TO, testIdentity, testIdentity2, resp.secretId); } @Test public void addWithMultipleGroups() throws Exception { // Groups: testGroup (testIdentity); id2Group (testIdentity2) // Create a secret that belongs to both id2Group and testGroup (both identities are admins) // had a null bug access control checking groups when the requestor is not a member DBGroup id2Group = createGroupContainingIdentity(testIdentity2); DBServerVisibleSecret svs = createSecret(id2Group, "client", "critical", null); addSecretToGroup(svs, testGroup, "client", "critical"); // testIdentity successfully adds this secret to a new group DBGroup testIdentityGroup2 = createGroupContainingIdentity(testIdentity); request.ownerGroupId = testIdentityGroup2.getId(); request.secretId = svs.getId(); AddSecretResponse resp = processCommand(testIdentity); assertEquals(svs.getId(), resp.secretId); manager.setRequestor(testIdentity, "device"); // both users already had access to this secret before, so no changes should have been written assertEquals(false, hasAudit(manager, AuditLogProcessor.ActionType.GRANTED_ACCESS_TO, testIdentity, testIdentity, resp.secretId)); assertEquals(false, hasAudit(manager, AuditLogProcessor.ActionType.GRANTED_ACCESS_TO, testIdentity, testIdentity2, resp.secretId)); // testIdentity2 successfully adds this secret to a new group DBGroup testIdentity2Group2 = createGroupContainingIdentity(testIdentity2); request.ownerGroupId = testIdentity2Group2.getId(); request.secretId = svs.getId(); assertEquals(svs.getId(), processCommand(testIdentity2).secretId); } @Test public void testAddSecretBadSecretId() throws IOException, SQLException, MitroServletException { request.secretId = 99; processCommandWithException(testIdentity, "Secret does not exist"); } @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); // try to share the secret (in org1) with org2 user DBGroup userGroup = getPrivateOrgGroup(org2, org2Admin); // try to add the org secret to this group; should fail. request.secretId = orgSecret.getId(); request.ownerGroupId = userGroup.getId(); request.encryptedClientData = "1"; request.encryptedCriticalData = "2"; processCommandWithException(org2Admin, "You cannot add a secret to more than one organization."); } @Test public void testNotInGroup() throws IOException, SQLException, MitroServletException { DBGroup g = new DBGroup(); g.setName("name"); g.setPublicKeyString("public key"); manager.groupDao.create(g); request.ownerGroupId = g.getId(); processCommandWithException(testIdentity, "ser should not be able to see group"); } private void processCommandWithException(DBIdentity requestor, String exceptionMessage) throws IOException, SQLException { try { processCommand(requestor); fail("expected exception"); } catch (MitroServletException e) { assertThat(e.getMessage(), containsString(exceptionMessage)); } } private RPC.AddSecretResponse processCommand(DBIdentity identity) throws IOException, SQLException, MitroServletException { return (RPC.AddSecretResponse) servlet.processCommand(new MitroRequestContext( identity, gson.toJson(request), manager, null)); } }