RotatePatKeysJob.java

1
package edu.ucsb.cs.scaffold.jobs;
2
3
import edu.ucsb.cs.scaffold.entity.PatCredential;
4
import edu.ucsb.cs.scaffold.errors.NoSuchKeyVersionException;
5
import edu.ucsb.cs.scaffold.repository.PatCredentialRepository;
6
import edu.ucsb.cs.scaffold.services.PatEncryptionService;
7
import edu.ucsb.cs156.jobs.services.JobContext;
8
import edu.ucsb.cs156.jobs.services.JobContextConsumer;
9
import lombok.Builder;
10
11
/**
12
 * Re-encrypts every stored PAT under the current PAT_ENCRYPTION_KEY. Launched by an admin (POST
13
 * /api/jobs/launch/rotatePatKeys) after the key rotation script has moved the retiring key to
14
 * PREVIOUS_PAT_ENCRYPTION_KEY and installed a new PAT_ENCRYPTION_KEY; see
15
 * docs/PAT_ENCRYPTION_KEY_instructions.md.
16
 *
17
 * <p>Credentials already on the current key version are skipped, so the job is idempotent and can
18
 * simply be re-launched after a partial failure. A credential whose key version has no configured
19
 * key (e.g. PREVIOUS_PAT_ENCRYPTION_KEY was removed too early) is logged and skipped rather than
20
 * failing the whole job; that user must re-enter their PAT.
21
 */
22
@Builder
23
public class RotatePatKeysJob implements JobContextConsumer {
24
25
  private PatEncryptionService patEncryptionService;
26
  private PatCredentialRepository patCredentialRepository;
27
28
  @Override
29
  public void accept(JobContext ctx) throws Exception {
30
    int currentVersion = patEncryptionService.currentKeyVersion();
31 1 1. accept : removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED
    ctx.log("Rotating stored PATs to key version %d".formatted(currentVersion));
32
    int rotated = 0;
33
    int alreadyCurrent = 0;
34
    int undecryptable = 0;
35
    for (PatCredential credential : patCredentialRepository.findAll()) {
36
      int oldVersion = credential.getKeyVersion();
37 1 1. accept : negated conditional → KILLED
      if (oldVersion == currentVersion) {
38 1 1. accept : Changed increment from 1 to -1 → KILLED
        alreadyCurrent++;
39
        continue;
40
      }
41
      try {
42
        String plaintext = patEncryptionService.decrypt(credential.getCiphertext(), oldVersion);
43
        PatEncryptionService.EncryptedPat encrypted = patEncryptionService.encrypt(plaintext);
44 1 1. accept : removed call to edu/ucsb/cs/scaffold/entity/PatCredential::setCiphertext → KILLED
        credential.setCiphertext(encrypted.ciphertext());
45 1 1. accept : removed call to edu/ucsb/cs/scaffold/entity/PatCredential::setKeyVersion → KILLED
        credential.setKeyVersion(encrypted.keyVersion());
46
        patCredentialRepository.save(credential);
47 1 1. accept : Changed increment from 1 to -1 → KILLED
        rotated++;
48 1 1. accept : removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED
        ctx.log(
49
            "Rotated credential id %d from key version %d to %d"
50
                .formatted(credential.getId(), oldVersion, currentVersion));
51
      } catch (NoSuchKeyVersionException e) {
52 1 1. accept : Changed increment from 1 to -1 → KILLED
        undecryptable++;
53 1 1. accept : removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED
        ctx.log("Cannot rotate credential id %d: %s".formatted(credential.getId(), e.getMessage()));
54
      }
55
    }
56 1 1. accept : removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED
    ctx.log(
57
        "Done: %d rotated, %d already on the current key version, %d could not be decrypted"
58
            .formatted(rotated, alreadyCurrent, undecryptable));
59
  }
60
}

Mutations

31

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:logs_and_skips_credentials_whose_key_version_is_no_longer_available()]
removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED

37

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:logs_and_skips_credentials_whose_key_version_is_no_longer_available()]
negated conditional → KILLED

38

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:rotates_old_rows_and_skips_rows_already_on_the_current_version()]
Changed increment from 1 to -1 → KILLED

44

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:rotates_old_rows_and_skips_rows_already_on_the_current_version()]
removed call to edu/ucsb/cs/scaffold/entity/PatCredential::setCiphertext → KILLED

45

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:rotates_old_rows_and_skips_rows_already_on_the_current_version()]
removed call to edu/ucsb/cs/scaffold/entity/PatCredential::setKeyVersion → KILLED

47

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:rotates_old_rows_and_skips_rows_already_on_the_current_version()]
Changed increment from 1 to -1 → KILLED

48

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:rotates_old_rows_and_skips_rows_already_on_the_current_version()]
removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED

52

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:logs_and_skips_credentials_whose_key_version_is_no_longer_available()]
Changed increment from 1 to -1 → KILLED

53

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:logs_and_skips_credentials_whose_key_version_is_no_longer_available()]
removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED

56

1.1
Location : accept
Killed by : edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests.[engine:junit-jupiter]/[class:edu.ucsb.cs.scaffold.jobs.RotatePatKeysJobTests]/[method:logs_and_skips_credentials_whose_key_version_is_no_longer_available()]
removed call to edu/ucsb/cs156/jobs/services/JobContext::log → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0