SecretDAO { @VisibleForTesting public long createSecret(String name, String encryptedSecret, String hmac, String creator, Map<String, String> metadata, long expiry, String description, @Nullable String type, @Nullable Map<String, String> generationOptions) { return dslContext.transactionResult(configuration -> { if (name.startsWith(".")) { throw new BadRequestException(format("secret cannot be created with name `%s` - secret " + "names cannot begin with a period", name)); } if (name.length() > SECRET_NAME_MAX_LENGTH) { throw new BadRequestException(format("secret cannot be created with name `%s` - secret " + "names must be %d characters or less", name, SECRET_NAME_MAX_LENGTH)); } long now = OffsetDateTime.now().toEpochSecond(); SecretContentDAO secretContentDAO = secretContentDAOFactory.using(configuration); SecretSeriesDAO secretSeriesDAO = secretSeriesDAOFactory.using(configuration); Optional<SecretSeries> secretSeries = secretSeriesDAO.getSecretSeriesByName(name); long secretId; if (secretSeries.isPresent()) { SecretSeries secretSeries1 = secretSeries.get(); if (secretSeries1.currentVersion().isPresent()) { throw new DataAccessException(format("secret already present: %s", name)); } else { throw new IllegalStateException( format("secret %s retrieved without current version set", name)); } } else { secretId = secretSeriesDAO.createSecretSeries(name, creator, description, type, generationOptions, now); } long secretContentId = secretContentDAO.createSecretContent(secretId, encryptedSecret, hmac, creator, metadata, expiry, now); secretSeriesDAO.setCurrentVersion(secretId, secretContentId, creator, now); return secretId; }); } SecretDAO(DSLContext dslContext, SecretContentDAOFactory secretContentDAOFactory,
      SecretSeriesDAOFactory secretSeriesDAOFactory, ContentCryptographer cryptographer); @VisibleForTesting long createSecret(String name, String encryptedSecret, String hmac,
      String creator, Map<String, String> metadata, long expiry, String description,
      @Nullable String type, @Nullable Map<String, String> generationOptions); @VisibleForTesting long createOrUpdateSecret(String name, String encryptedSecret, String hmac, String creator,
      Map<String, String> metadata, long expiry, String description, @Nullable String type,
      @Nullable Map<String, String> generationOptions); @VisibleForTesting long partialUpdateSecret(String name, String creator,
      PartialUpdateSecretRequestV2 request); boolean setExpiration(String name, Instant expiration); Optional<SecretSeriesAndContent> getSecretById(long secretId); Optional<SecretSeriesAndContent> getSecretByName(String name); List<SecretSeriesAndContent> getSecretsByName(List<String> names); ImmutableList<SecretSeriesAndContent> getSecrets(@Nullable Long expireMaxTime,
      @Nullable Group group, @Nullable Long expireMinTime, @Nullable String minName,
      @Nullable Integer limit); ImmutableList<SimpleEntry<Long, String>> getSecretsNameOnly(); ImmutableList<SecretSeriesAndContent> getSecretsBatched(int idx, int num,
      boolean newestFirst); Optional<ImmutableList<SanitizedSecret>> getSecretVersionsByName(String name,
      int versionIdx, int numVersions); void setCurrentSecretVersionByName(String name, long versionId, String updater); void deleteSecretsByName(String name); int countDeletedSecrets(); int countSecretsDeletedBeforeDate(DateTime deleteBefore); void dangerPermanentlyRemoveSecretsDeletedBeforeDate(DateTime deletedBefore,
      int sleepMillis);  }
@Test public void createSecret() { int secretsBefore = tableSize(SECRETS); int secretContentsBefore = tableSize(SECRETS_CONTENT); String name = "newSecret"; String content = "c2VjcmV0MQ=="; String hmac = cryptographer.computeHmac(content.getBytes(UTF_8), "hmackey"); String encryptedContent = cryptographer.encryptionKeyDerivedFrom(name).encrypt(content); long newId = secretDAO.createSecret(name, encryptedContent, hmac, "creator", ImmutableMap.of(), 0, "", null, ImmutableMap.of()); SecretSeriesAndContent newSecret = secretDAO.getSecretById(newId).get(); assertThat(tableSize(SECRETS)).isEqualTo(secretsBefore + 1); assertThat(tableSize(SECRETS_CONTENT)).isEqualTo(secretContentsBefore + 1); newSecret = secretDAO.getSecretByName(newSecret.series().name()).get(); assertThat(secretDAO.getSecrets(null, null, null,null, null)).containsOnly(secret1, secret2b, newSecret); }
@Test(expected = DataAccessException.class) public void createSecretFailsIfSecretExists() { String name = "newSecret"; secretDAO.createSecret(name, "some secret", "checksum", "creator", ImmutableMap.of(), 0, "", null, ImmutableMap.of()); secretDAO.createSecret(name, "some secret", "checksum", "creator", ImmutableMap.of(), 0, "", null, ImmutableMap.of()); }
@Test(expected = BadRequestException.class) public void createSecretFailsIfNameHasLeadingPeriod() { String name = ".newSecret"; secretDAO.createSecret(name, "some secret", "checksum", "creator", ImmutableMap.of(), 0, "", null, ImmutableMap.of()); }
@Test(expected = BadRequestException.class) public void createSecretFailsIfNameIsTooLong() { String name = "newSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecretnewSecret"; secretDAO.createSecret(name, "some secret", "checksum", "creator", ImmutableMap.of(), 0, "", null, ImmutableMap.of()); }
@Test(expected = DataAccessException.class) public void createSecretFailsIfNameMatchesDeletedSecret() { String name = "newSecret"; long firstId = secretDAO.createSecret(name, "content1", cryptographer.computeHmac("content1".getBytes(UTF_8), "hmackey"), "creator1", ImmutableMap.of("foo", "bar"), 1000, "description1", "type1", ImmutableMap.of()); jooqContext.update(SECRETS) .set(SECRETS.CURRENT, (Long) null) .where(SECRETS.ID.eq(firstId)) .execute(); secretDAO.createSecret(name, "content2", cryptographer.computeHmac("content2".getBytes(UTF_8), "hmackey"), "creator2", ImmutableMap.of("foo2", "bar2"), 2000, "description2", "type2", ImmutableMap.of()); }