/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package keywhiz.service.daos;
import com.google.common.collect.ImmutableMap;
import java.time.OffsetDateTime;
import javax.inject.Inject;
import keywhiz.KeywhizTestRunner;
import keywhiz.api.ApiDate;
import keywhiz.api.model.SecretContent;
import keywhiz.service.daos.SecretContentDAO.SecretContentDAOFactory;
import org.jooq.DSLContext;
import org.jooq.tools.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static keywhiz.jooq.tables.Secrets.SECRETS;
import static keywhiz.jooq.tables.SecretsContent.SECRETS_CONTENT;
import static keywhiz.service.daos.SecretContentDAO.PRUNE_CUTOFF_ITEMS;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(KeywhizTestRunner.class)
public class SecretContentDAOTest {
@Inject DSLContext jooqContext;
@Inject SecretContentDAOFactory secretContentDAOFactory;
final static ApiDate date = ApiDate.now();
ImmutableMap<String, String> metadata = ImmutableMap.of("foo", "bar");
SecretContent secretContent1 = SecretContent.of(11, 22, "[crypted]", "checksum", date, "creator", date,
"creator", metadata, 1136214245);
SecretContentDAO secretContentDAO;
@Before
public void setUp() throws Exception {
secretContentDAO = secretContentDAOFactory.readwrite();
long now = OffsetDateTime.now().toEpochSecond();
jooqContext.insertInto(SECRETS, SECRETS.ID, SECRETS.NAME, SECRETS.CREATEDAT, SECRETS.UPDATEDAT)
.values(secretContent1.secretSeriesId(), "secretName", now, now)
.execute();
jooqContext.insertInto(SECRETS_CONTENT)
.set(SECRETS_CONTENT.ID, secretContent1.id())
.set(SECRETS_CONTENT.SECRETID, secretContent1.secretSeriesId())
.set(SECRETS_CONTENT.ENCRYPTED_CONTENT, secretContent1.encryptedContent())
.set(SECRETS_CONTENT.CONTENT_HMAC, "checksum")
.set(SECRETS_CONTENT.CREATEDAT, secretContent1.createdAt().toEpochSecond())
.set(SECRETS_CONTENT.CREATEDBY, secretContent1.createdBy())
.set(SECRETS_CONTENT.UPDATEDAT, secretContent1.updatedAt().toEpochSecond())
.set(SECRETS_CONTENT.UPDATEDBY, secretContent1.updatedBy())
.set(SECRETS_CONTENT.METADATA, JSONObject.toJSONString(secretContent1.metadata()))
.set(SECRETS_CONTENT.EXPIRY, 1136214245L)
.execute();
}
@Test public void createSecretContent() {
int before = tableSize();
secretContentDAO.createSecretContent(secretContent1.secretSeriesId()+1, "encrypted", "checksum", "creator",
metadata, 1136214245);
assertThat(tableSize()).isEqualTo(before + 1);
}
@Test public void pruneOldContents() throws Exception {
long now = OffsetDateTime.now().toEpochSecond();
long secretSeriesId = 666;
jooqContext.insertInto(SECRETS, SECRETS.ID, SECRETS.NAME, SECRETS.CREATEDAT, SECRETS.UPDATEDAT)
.values(secretSeriesId, "secretForPruneTest1", now, now)
.execute();
int before = tableSize();
// Create contents
long[] ids = new long[15];
for (int i = 0; i < ids.length; i++) {
long id = secretContentDAO.createSecretContent(
secretSeriesId, "encrypted", "checksum", "creator", metadata, 1136214245);
ids[i] = id;
}
assertThat(tableSize()).isEqualTo(before + ids.length);
// Update created_at to make all secrets older than cutoff
jooqContext.update(SECRETS_CONTENT)
.set(SECRETS_CONTENT.CREATEDAT, 1L)
.execute();
// Make most recent id be the current version for the secret series and prune
jooqContext.update(SECRETS)
.set(SECRETS.CURRENT, ids[ids.length-1])
.where(SECRETS.ID.eq(secretSeriesId))
.execute();
secretContentDAO.pruneOldContents(secretSeriesId);
// Last ten secrets in series should have survived (plus the current one)
assertThat(tableSize()).isEqualTo(before + PRUNE_CUTOFF_ITEMS + 1);
for (int i = 0; i < (ids.length - PRUNE_CUTOFF_ITEMS - 1); i++) {
assertThat(secretContentDAO.getSecretContentById(ids[i]).isPresent()).isFalse();
}
for (int i = (ids.length - PRUNE_CUTOFF_ITEMS - 1); i < ids.length; i++) {
assertThat(secretContentDAO.getSecretContentById(ids[i]).isPresent()).isTrue();
}
// Other secrets contents left intact
assertThat(secretContentDAO.getSecretContentById(secretContent1.id()).isPresent()).isTrue();
}
@Test public void pruneIgnores45DaysOrLess() throws Exception {
long now = OffsetDateTime.now().toEpochSecond();
long secretSeriesId = 666;
jooqContext.insertInto(SECRETS, SECRETS.ID, SECRETS.NAME, SECRETS.CREATEDAT, SECRETS.UPDATEDAT)
.values(secretSeriesId, "secretForPruneTest2", now, now)
.execute();
int before = tableSize();
// Create contents
long[] ids = new long[15];
for (int i = 0; i < ids.length; i++) {
long id = secretContentDAO.createSecretContent(
secretSeriesId, "encrypted", "checksum", "creator", metadata, 1136214245);
ids[i] = id;
}
assertThat(tableSize()).isEqualTo(before + ids.length);
// Make most recent id be the current version for the secret series and prune
jooqContext.update(SECRETS)
.set(SECRETS.CURRENT, ids[ids.length-1])
.where(SECRETS.ID.eq(secretSeriesId))
.execute();
secretContentDAO.pruneOldContents(secretSeriesId);
// Nothing pruned
for (int i = 0; i < ids.length; i++) {
assertThat(secretContentDAO.getSecretContentById(ids[i]).isPresent()).isTrue();
}
}
@Test public void getSecretContentById() {
assertThat(secretContentDAO.getSecretContentById(secretContent1.id())).contains(secretContent1);
}
private int tableSize() {
return jooqContext.fetchCount(SECRETS_CONTENT);
}
}