-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCredentialBinding.java
More file actions
98 lines (87 loc) · 3.26 KB
/
CredentialBinding.java
File metadata and controls
98 lines (87 loc) · 3.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package dev.arcp.runtime.credentials;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.arcp.core.credentials.Credential;
import dev.arcp.core.credentials.CredentialId;
import dev.arcp.core.events.CredentialRotatedBody;
import dev.arcp.core.events.EventBody;
import dev.arcp.core.events.StatusEvent;
import dev.arcp.core.wire.ArcpMapper;
import dev.arcp.runtime.session.JobRecord;
import java.time.Clock;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class CredentialBinding {
private static final Logger log = LoggerFactory.getLogger(CredentialBinding.class);
private static final int MAX_REVOKE_ATTEMPTS = 3;
private final CredentialProvisioner provisioner;
private final CredentialRevocationStore store;
@SuppressWarnings("unused")
private final Clock clock;
private final ObjectMapper mapper;
private final BiConsumer<JobRecord, EventBody> eventSink;
public CredentialBinding(
CredentialProvisioner provisioner, CredentialRevocationStore store, Clock clock) {
this(provisioner, store, clock, (record, body) -> {});
}
public CredentialBinding(
CredentialProvisioner provisioner,
CredentialRevocationStore store,
Clock clock,
BiConsumer<JobRecord, EventBody> eventSink) {
this.provisioner = Objects.requireNonNull(provisioner, "provisioner");
this.store = Objects.requireNonNull(store, "store");
this.clock = Objects.requireNonNull(clock, "clock");
this.mapper = ArcpMapper.shared();
this.eventSink = Objects.requireNonNull(eventSink, "eventSink");
}
public List<Credential> attach(JobRecord record, List<IssuedCredential> issued) {
List<IssuedCredential> copy = List.copyOf(issued);
for (IssuedCredential credential : copy) {
store.record(
credential.wire().id(),
credential.providerHandle() != null
? credential.providerHandle()
: credential.wire().id().value());
}
record.setCredentials(copy);
return copy.stream().map(IssuedCredential::wire).toList();
}
public void rotate(JobRecord record, CredentialId id, IssuedCredential next) {
IssuedCredential prior = record.replaceCredential(id, next);
if (prior != null) {
revoke(prior);
}
store.record(
next.wire().id(),
next.providerHandle() != null ? next.providerHandle() : next.wire().id().value());
eventSink.accept(
record,
new StatusEvent(
"credential_rotated",
null,
mapper.valueToTree(new CredentialRotatedBody(next.wire().id(), next.wire().value()))));
}
public void revokeAll(JobRecord record) {
for (IssuedCredential credential : record.drainCredentials()) {
revoke(credential);
}
}
private void revoke(IssuedCredential credential) {
CredentialId id = credential.wire().id();
for (int attempt = 1; attempt <= MAX_REVOKE_ATTEMPTS; attempt++) {
try {
provisioner.revoke(id).join();
store.markRevoked(id);
return;
} catch (RuntimeException e) {
if (attempt == MAX_REVOKE_ATTEMPTS) {
log.warn("credential revoke failed after {} attempts for {}", attempt, id);
return;
}
}
}
}
}