SA: Implement schema and methods for (account, hostname) pausing (#7490)
Add the storage implementation for our new (account, hostname) pair pausing feature. - Add schema and model for for the new paused table - Add SA service methods for interacting with the paused table Part of #7406 Part of #7475
This commit is contained in:
parent
063db40db2
commit
594cb1332f
22
mocks/sa.go
22
mocks/sa.go
|
|
@ -244,6 +244,26 @@ func (sa *StorageAuthority) SerialsForIncident(ctx context.Context, _ *sapb.Seri
|
||||||
return &ServerStreamClient[sapb.IncidentSerial]{}, nil
|
return &ServerStreamClient[sapb.IncidentSerial]{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIdentifiersPaused is a mock
|
||||||
|
func (sa *StorageAuthorityReadOnly) CheckIdentifiersPaused(_ context.Context, _ *sapb.PauseRequest, _ ...grpc.CallOption) (*sapb.Identifiers, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckIdentifiersPaused is a mock
|
||||||
|
func (sa *StorageAuthority) CheckIdentifiersPaused(_ context.Context, _ *sapb.PauseRequest, _ ...grpc.CallOption) (*sapb.Identifiers, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPausedIdentifiers is a mock
|
||||||
|
func (sa *StorageAuthorityReadOnly) GetPausedIdentifiers(_ context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (*sapb.Identifiers, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPausedIdentifiers is a mock
|
||||||
|
func (sa *StorageAuthority) GetPausedIdentifiers(_ context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (*sapb.Identifiers, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetRevokedCerts is a mock
|
// GetRevokedCerts is a mock
|
||||||
func (sa *StorageAuthorityReadOnly) GetRevokedCerts(ctx context.Context, _ *sapb.GetRevokedCertsRequest, _ ...grpc.CallOption) (sapb.StorageAuthorityReadOnly_GetRevokedCertsClient, error) {
|
func (sa *StorageAuthorityReadOnly) GetRevokedCerts(ctx context.Context, _ *sapb.GetRevokedCertsRequest, _ ...grpc.CallOption) (sapb.StorageAuthorityReadOnly_GetRevokedCertsClient, error) {
|
||||||
return &ServerStreamClient[corepb.CRLEntry]{}, nil
|
return &ServerStreamClient[corepb.CRLEntry]{}, nil
|
||||||
|
|
@ -457,7 +477,7 @@ func (sa *StorageAuthorityReadOnly) GetValidAuthorizations2(ctx context.Context,
|
||||||
RegistrationID: req.RegistrationID,
|
RegistrationID: req.RegistrationID,
|
||||||
Expires: &exp,
|
Expires: &exp,
|
||||||
Identifier: identifier.ACMEIdentifier{
|
Identifier: identifier.ACMEIdentifier{
|
||||||
Type: "dns",
|
Type: identifier.DNS,
|
||||||
Value: name,
|
Value: name,
|
||||||
},
|
},
|
||||||
Challenges: []core.Challenge{
|
Challenges: []core.Challenge{
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,7 @@ func initTables(dbMap *borp.DbMap) {
|
||||||
dbMap.AddTableWithName(crlShardModel{}, "crlShards").SetKeys(true, "ID")
|
dbMap.AddTableWithName(crlShardModel{}, "crlShards").SetKeys(true, "ID")
|
||||||
dbMap.AddTableWithName(revokedCertModel{}, "revokedCertificates").SetKeys(true, "ID")
|
dbMap.AddTableWithName(revokedCertModel{}, "revokedCertificates").SetKeys(true, "ID")
|
||||||
dbMap.AddTableWithName(replacementOrderModel{}, "replacementOrders").SetKeys(true, "ID")
|
dbMap.AddTableWithName(replacementOrderModel{}, "replacementOrders").SetKeys(true, "ID")
|
||||||
|
dbMap.AddTableWithName(pausedModel{}, "paused")
|
||||||
|
|
||||||
// Read-only maps used for selecting subsets of columns.
|
// Read-only maps used for selecting subsets of columns.
|
||||||
dbMap.AddTableWithName(CertStatusMetadata{}, "certificateStatus")
|
dbMap.AddTableWithName(CertStatusMetadata{}, "certificateStatus")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
-- +migrate Up
|
||||||
|
-- SQL in section 'Up' is executed when this migration is applied
|
||||||
|
|
||||||
|
-- This table has no auto-incrementing primary key because we don't plan to
|
||||||
|
-- partition it. This table expected to be < 800K rows initially and grow at a
|
||||||
|
-- rate of ~18% per year.
|
||||||
|
|
||||||
|
CREATE TABLE `paused` (
|
||||||
|
`registrationID` bigint(20) NOT NULL,
|
||||||
|
`identifierType` tinyint(4) NOT NULL,
|
||||||
|
`identifierValue` varchar(255) NOT NULL,
|
||||||
|
`pausedAt` datetime NOT NULL,
|
||||||
|
`unpausedAt` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`registrationID`, `identifierType`, `identifierValue`)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- +migrate Down
|
||||||
|
-- SQL section 'Down' is executed when this migration is rolled back
|
||||||
|
|
||||||
|
DROP TABLE `paused`;
|
||||||
|
|
@ -34,6 +34,8 @@ GRANT SELECT ON incidents TO 'sa'@'localhost';
|
||||||
GRANT SELECT,INSERT,UPDATE ON crlShards TO 'sa'@'localhost';
|
GRANT SELECT,INSERT,UPDATE ON crlShards TO 'sa'@'localhost';
|
||||||
GRANT SELECT,INSERT,UPDATE ON revokedCertificates TO 'sa'@'localhost';
|
GRANT SELECT,INSERT,UPDATE ON revokedCertificates TO 'sa'@'localhost';
|
||||||
GRANT SELECT,INSERT,UPDATE ON replacementOrders TO 'sa'@'localhost';
|
GRANT SELECT,INSERT,UPDATE ON replacementOrders TO 'sa'@'localhost';
|
||||||
|
-- Tests need to be able to TRUNCATE this table, so DROP is necessary.
|
||||||
|
GRANT SELECT,INSERT,UPDATE,DROP ON paused TO 'sa'@'localhost';
|
||||||
|
|
||||||
GRANT SELECT ON certificates TO 'sa_ro'@'localhost';
|
GRANT SELECT ON certificates TO 'sa_ro'@'localhost';
|
||||||
GRANT SELECT ON certificateStatus TO 'sa_ro'@'localhost';
|
GRANT SELECT ON certificateStatus TO 'sa_ro'@'localhost';
|
||||||
|
|
@ -54,6 +56,7 @@ GRANT SELECT ON incidents TO 'sa_ro'@'localhost';
|
||||||
GRANT SELECT ON crlShards TO 'sa_ro'@'localhost';
|
GRANT SELECT ON crlShards TO 'sa_ro'@'localhost';
|
||||||
GRANT SELECT ON revokedCertificates TO 'sa_ro'@'localhost';
|
GRANT SELECT ON revokedCertificates TO 'sa_ro'@'localhost';
|
||||||
GRANT SELECT ON replacementOrders TO 'sa_ro'@'localhost';
|
GRANT SELECT ON replacementOrders TO 'sa_ro'@'localhost';
|
||||||
|
GRANT SELECT ON paused TO 'sa_ro'@'localhost';
|
||||||
|
|
||||||
-- OCSP Responder
|
-- OCSP Responder
|
||||||
GRANT SELECT ON certificateStatus TO 'ocsp_resp'@'localhost';
|
GRANT SELECT ON certificateStatus TO 'ocsp_resp'@'localhost';
|
||||||
|
|
|
||||||
64
sa/model.go
64
sa/model.go
|
|
@ -1294,3 +1294,67 @@ func setReplacementOrderFinalized(ctx context.Context, db db.Execer, orderID int
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type identifierModel struct {
|
||||||
|
Type uint8 `db:"identifierType"`
|
||||||
|
Value string `db:"identifierValue"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIdentifierModelFromPB(pb *sapb.Identifier) (identifierModel, error) {
|
||||||
|
idType, ok := identifierTypeToUint[pb.Type]
|
||||||
|
if !ok {
|
||||||
|
return identifierModel{}, fmt.Errorf("unsupported identifier type %q", pb.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifierModel{
|
||||||
|
Type: idType,
|
||||||
|
Value: pb.Value,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPBFromIdentifierModel(id identifierModel) (*sapb.Identifier, error) {
|
||||||
|
idType, ok := uintToIdentifierType[id.Type]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported identifier type %d", id.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sapb.Identifier{
|
||||||
|
Type: idType,
|
||||||
|
Value: id.Value,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIdentifierModelsFromPB(pb []*sapb.Identifier) ([]identifierModel, error) {
|
||||||
|
var ids []identifierModel
|
||||||
|
for _, p := range pb {
|
||||||
|
id, err := newIdentifierModelFromPB(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPBFromIdentifierModels(ids []identifierModel) (*sapb.Identifiers, error) {
|
||||||
|
var pb []*sapb.Identifier
|
||||||
|
for _, id := range ids {
|
||||||
|
p, err := newPBFromIdentifierModel(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pb = append(pb, p)
|
||||||
|
}
|
||||||
|
return &sapb.Identifiers{Identifiers: pb}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pausedModel represents a row in the paused table. The pausedAt and unpausedAt
|
||||||
|
// fields are pointers because they are NULL-able columns. Valid states are:
|
||||||
|
// - Identifier paused: pausedAt is non-NULL, unpausedAt is NULL
|
||||||
|
// - Identifier unpaused: pausedAt is non-NULL, unpausedAt is non-NULL
|
||||||
|
type pausedModel struct {
|
||||||
|
identifierModel
|
||||||
|
RegistrationID int64 `db:"registrationID"`
|
||||||
|
PausedAt *time.Time `db:"pausedAt"`
|
||||||
|
UnpausedAt *time.Time `db:"unpausedAt"`
|
||||||
|
}
|
||||||
|
|
|
||||||
1422
sa/proto/sa.pb.go
1422
sa/proto/sa.pb.go
File diff suppressed because it is too large
Load Diff
|
|
@ -41,6 +41,8 @@ service StorageAuthorityReadOnly {
|
||||||
rpc KeyBlocked(SPKIHash) returns (Exists) {}
|
rpc KeyBlocked(SPKIHash) returns (Exists) {}
|
||||||
rpc ReplacementOrderExists(Serial) returns (Exists) {}
|
rpc ReplacementOrderExists(Serial) returns (Exists) {}
|
||||||
rpc SerialsForIncident (SerialsForIncidentRequest) returns (stream IncidentSerial) {}
|
rpc SerialsForIncident (SerialsForIncidentRequest) returns (stream IncidentSerial) {}
|
||||||
|
rpc CheckIdentifiersPaused (PauseRequest) returns (Identifiers) {}
|
||||||
|
rpc GetPausedIdentifiers (RegistrationID) returns (Identifiers) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageAuthority provides full read/write access to the database.
|
// StorageAuthority provides full read/write access to the database.
|
||||||
|
|
@ -77,6 +79,8 @@ service StorageAuthority {
|
||||||
rpc KeyBlocked(SPKIHash) returns (Exists) {}
|
rpc KeyBlocked(SPKIHash) returns (Exists) {}
|
||||||
rpc ReplacementOrderExists(Serial) returns (Exists) {}
|
rpc ReplacementOrderExists(Serial) returns (Exists) {}
|
||||||
rpc SerialsForIncident (SerialsForIncidentRequest) returns (stream IncidentSerial) {}
|
rpc SerialsForIncident (SerialsForIncidentRequest) returns (stream IncidentSerial) {}
|
||||||
|
rpc CheckIdentifiersPaused (PauseRequest) returns (Identifiers) {}
|
||||||
|
rpc GetPausedIdentifiers (RegistrationID) returns (Identifiers) {}
|
||||||
// Adders
|
// Adders
|
||||||
rpc AddBlockedKey(AddBlockedKeyRequest) returns (google.protobuf.Empty) {}
|
rpc AddBlockedKey(AddBlockedKeyRequest) returns (google.protobuf.Empty) {}
|
||||||
rpc AddCertificate(AddCertificateRequest) returns (google.protobuf.Empty) {}
|
rpc AddCertificate(AddCertificateRequest) returns (google.protobuf.Empty) {}
|
||||||
|
|
@ -96,6 +100,8 @@ service StorageAuthority {
|
||||||
rpc UpdateRevokedCertificate(RevokeCertificateRequest) returns (google.protobuf.Empty) {}
|
rpc UpdateRevokedCertificate(RevokeCertificateRequest) returns (google.protobuf.Empty) {}
|
||||||
rpc LeaseCRLShard(LeaseCRLShardRequest) returns (LeaseCRLShardResponse) {}
|
rpc LeaseCRLShard(LeaseCRLShardRequest) returns (LeaseCRLShardResponse) {}
|
||||||
rpc UpdateCRLShard(UpdateCRLShardRequest) returns (google.protobuf.Empty) {}
|
rpc UpdateCRLShard(UpdateCRLShardRequest) returns (google.protobuf.Empty) {}
|
||||||
|
rpc PauseIdentifiers(PauseRequest) returns (PauseIdentifiersResponse) {}
|
||||||
|
rpc UnpauseAccount(RegistrationID) returns (google.protobuf.Empty) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message RegistrationID {
|
message RegistrationID {
|
||||||
|
|
@ -414,3 +420,22 @@ message UpdateCRLShardRequest {
|
||||||
google.protobuf.Timestamp thisUpdate = 3;
|
google.protobuf.Timestamp thisUpdate = 3;
|
||||||
google.protobuf.Timestamp nextUpdate = 4;
|
google.protobuf.Timestamp nextUpdate = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Identifier {
|
||||||
|
string type = 1;
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Identifiers {
|
||||||
|
repeated Identifier identifiers = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PauseRequest {
|
||||||
|
int64 registrationID = 1;
|
||||||
|
repeated Identifier identifiers = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PauseIdentifiersResponse {
|
||||||
|
int64 paused = 1;
|
||||||
|
int64 repaused = 2;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ const (
|
||||||
StorageAuthorityReadOnly_KeyBlocked_FullMethodName = "/sa.StorageAuthorityReadOnly/KeyBlocked"
|
StorageAuthorityReadOnly_KeyBlocked_FullMethodName = "/sa.StorageAuthorityReadOnly/KeyBlocked"
|
||||||
StorageAuthorityReadOnly_ReplacementOrderExists_FullMethodName = "/sa.StorageAuthorityReadOnly/ReplacementOrderExists"
|
StorageAuthorityReadOnly_ReplacementOrderExists_FullMethodName = "/sa.StorageAuthorityReadOnly/ReplacementOrderExists"
|
||||||
StorageAuthorityReadOnly_SerialsForIncident_FullMethodName = "/sa.StorageAuthorityReadOnly/SerialsForIncident"
|
StorageAuthorityReadOnly_SerialsForIncident_FullMethodName = "/sa.StorageAuthorityReadOnly/SerialsForIncident"
|
||||||
|
StorageAuthorityReadOnly_CheckIdentifiersPaused_FullMethodName = "/sa.StorageAuthorityReadOnly/CheckIdentifiersPaused"
|
||||||
|
StorageAuthorityReadOnly_GetPausedIdentifiers_FullMethodName = "/sa.StorageAuthorityReadOnly/GetPausedIdentifiers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StorageAuthorityReadOnlyClient is the client API for StorageAuthorityReadOnly service.
|
// StorageAuthorityReadOnlyClient is the client API for StorageAuthorityReadOnly service.
|
||||||
|
|
@ -90,6 +92,8 @@ type StorageAuthorityReadOnlyClient interface {
|
||||||
KeyBlocked(ctx context.Context, in *SPKIHash, opts ...grpc.CallOption) (*Exists, error)
|
KeyBlocked(ctx context.Context, in *SPKIHash, opts ...grpc.CallOption) (*Exists, error)
|
||||||
ReplacementOrderExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error)
|
ReplacementOrderExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error)
|
||||||
SerialsForIncident(ctx context.Context, in *SerialsForIncidentRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[IncidentSerial], error)
|
SerialsForIncident(ctx context.Context, in *SerialsForIncidentRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[IncidentSerial], error)
|
||||||
|
CheckIdentifiersPaused(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*Identifiers, error)
|
||||||
|
GetPausedIdentifiers(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*Identifiers, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type storageAuthorityReadOnlyClient struct {
|
type storageAuthorityReadOnlyClient struct {
|
||||||
|
|
@ -446,6 +450,26 @@ func (c *storageAuthorityReadOnlyClient) SerialsForIncident(ctx context.Context,
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type StorageAuthorityReadOnly_SerialsForIncidentClient = grpc.ServerStreamingClient[IncidentSerial]
|
type StorageAuthorityReadOnly_SerialsForIncidentClient = grpc.ServerStreamingClient[IncidentSerial]
|
||||||
|
|
||||||
|
func (c *storageAuthorityReadOnlyClient) CheckIdentifiersPaused(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*Identifiers, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(Identifiers)
|
||||||
|
err := c.cc.Invoke(ctx, StorageAuthorityReadOnly_CheckIdentifiersPaused_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *storageAuthorityReadOnlyClient) GetPausedIdentifiers(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*Identifiers, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(Identifiers)
|
||||||
|
err := c.cc.Invoke(ctx, StorageAuthorityReadOnly_GetPausedIdentifiers_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StorageAuthorityReadOnlyServer is the server API for StorageAuthorityReadOnly service.
|
// StorageAuthorityReadOnlyServer is the server API for StorageAuthorityReadOnly service.
|
||||||
// All implementations must embed UnimplementedStorageAuthorityReadOnlyServer
|
// All implementations must embed UnimplementedStorageAuthorityReadOnlyServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
|
|
@ -481,6 +505,8 @@ type StorageAuthorityReadOnlyServer interface {
|
||||||
KeyBlocked(context.Context, *SPKIHash) (*Exists, error)
|
KeyBlocked(context.Context, *SPKIHash) (*Exists, error)
|
||||||
ReplacementOrderExists(context.Context, *Serial) (*Exists, error)
|
ReplacementOrderExists(context.Context, *Serial) (*Exists, error)
|
||||||
SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error
|
SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error
|
||||||
|
CheckIdentifiersPaused(context.Context, *PauseRequest) (*Identifiers, error)
|
||||||
|
GetPausedIdentifiers(context.Context, *RegistrationID) (*Identifiers, error)
|
||||||
mustEmbedUnimplementedStorageAuthorityReadOnlyServer()
|
mustEmbedUnimplementedStorageAuthorityReadOnlyServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -581,6 +607,12 @@ func (UnimplementedStorageAuthorityReadOnlyServer) ReplacementOrderExists(contex
|
||||||
func (UnimplementedStorageAuthorityReadOnlyServer) SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error {
|
func (UnimplementedStorageAuthorityReadOnlyServer) SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SerialsForIncident not implemented")
|
return status.Errorf(codes.Unimplemented, "method SerialsForIncident not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedStorageAuthorityReadOnlyServer) CheckIdentifiersPaused(context.Context, *PauseRequest) (*Identifiers, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CheckIdentifiersPaused not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedStorageAuthorityReadOnlyServer) GetPausedIdentifiers(context.Context, *RegistrationID) (*Identifiers, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetPausedIdentifiers not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedStorageAuthorityReadOnlyServer) mustEmbedUnimplementedStorageAuthorityReadOnlyServer() {
|
func (UnimplementedStorageAuthorityReadOnlyServer) mustEmbedUnimplementedStorageAuthorityReadOnlyServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1125,6 +1157,42 @@ func _StorageAuthorityReadOnly_SerialsForIncident_Handler(srv interface{}, strea
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type StorageAuthorityReadOnly_SerialsForIncidentServer = grpc.ServerStreamingServer[IncidentSerial]
|
type StorageAuthorityReadOnly_SerialsForIncidentServer = grpc.ServerStreamingServer[IncidentSerial]
|
||||||
|
|
||||||
|
func _StorageAuthorityReadOnly_CheckIdentifiersPaused_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PauseRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StorageAuthorityReadOnlyServer).CheckIdentifiersPaused(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StorageAuthorityReadOnly_CheckIdentifiersPaused_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StorageAuthorityReadOnlyServer).CheckIdentifiersPaused(ctx, req.(*PauseRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StorageAuthorityReadOnly_GetPausedIdentifiers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(RegistrationID)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StorageAuthorityReadOnlyServer).GetPausedIdentifiers(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StorageAuthorityReadOnly_GetPausedIdentifiers_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StorageAuthorityReadOnlyServer).GetPausedIdentifiers(ctx, req.(*RegistrationID))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// StorageAuthorityReadOnly_ServiceDesc is the grpc.ServiceDesc for StorageAuthorityReadOnly service.
|
// StorageAuthorityReadOnly_ServiceDesc is the grpc.ServiceDesc for StorageAuthorityReadOnly service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
|
@ -1240,6 +1308,14 @@ var StorageAuthorityReadOnly_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "ReplacementOrderExists",
|
MethodName: "ReplacementOrderExists",
|
||||||
Handler: _StorageAuthorityReadOnly_ReplacementOrderExists_Handler,
|
Handler: _StorageAuthorityReadOnly_ReplacementOrderExists_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CheckIdentifiersPaused",
|
||||||
|
Handler: _StorageAuthorityReadOnly_CheckIdentifiersPaused_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetPausedIdentifiers",
|
||||||
|
Handler: _StorageAuthorityReadOnly_GetPausedIdentifiers_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|
@ -1298,6 +1374,8 @@ const (
|
||||||
StorageAuthority_KeyBlocked_FullMethodName = "/sa.StorageAuthority/KeyBlocked"
|
StorageAuthority_KeyBlocked_FullMethodName = "/sa.StorageAuthority/KeyBlocked"
|
||||||
StorageAuthority_ReplacementOrderExists_FullMethodName = "/sa.StorageAuthority/ReplacementOrderExists"
|
StorageAuthority_ReplacementOrderExists_FullMethodName = "/sa.StorageAuthority/ReplacementOrderExists"
|
||||||
StorageAuthority_SerialsForIncident_FullMethodName = "/sa.StorageAuthority/SerialsForIncident"
|
StorageAuthority_SerialsForIncident_FullMethodName = "/sa.StorageAuthority/SerialsForIncident"
|
||||||
|
StorageAuthority_CheckIdentifiersPaused_FullMethodName = "/sa.StorageAuthority/CheckIdentifiersPaused"
|
||||||
|
StorageAuthority_GetPausedIdentifiers_FullMethodName = "/sa.StorageAuthority/GetPausedIdentifiers"
|
||||||
StorageAuthority_AddBlockedKey_FullMethodName = "/sa.StorageAuthority/AddBlockedKey"
|
StorageAuthority_AddBlockedKey_FullMethodName = "/sa.StorageAuthority/AddBlockedKey"
|
||||||
StorageAuthority_AddCertificate_FullMethodName = "/sa.StorageAuthority/AddCertificate"
|
StorageAuthority_AddCertificate_FullMethodName = "/sa.StorageAuthority/AddCertificate"
|
||||||
StorageAuthority_AddPrecertificate_FullMethodName = "/sa.StorageAuthority/AddPrecertificate"
|
StorageAuthority_AddPrecertificate_FullMethodName = "/sa.StorageAuthority/AddPrecertificate"
|
||||||
|
|
@ -1316,6 +1394,8 @@ const (
|
||||||
StorageAuthority_UpdateRevokedCertificate_FullMethodName = "/sa.StorageAuthority/UpdateRevokedCertificate"
|
StorageAuthority_UpdateRevokedCertificate_FullMethodName = "/sa.StorageAuthority/UpdateRevokedCertificate"
|
||||||
StorageAuthority_LeaseCRLShard_FullMethodName = "/sa.StorageAuthority/LeaseCRLShard"
|
StorageAuthority_LeaseCRLShard_FullMethodName = "/sa.StorageAuthority/LeaseCRLShard"
|
||||||
StorageAuthority_UpdateCRLShard_FullMethodName = "/sa.StorageAuthority/UpdateCRLShard"
|
StorageAuthority_UpdateCRLShard_FullMethodName = "/sa.StorageAuthority/UpdateCRLShard"
|
||||||
|
StorageAuthority_PauseIdentifiers_FullMethodName = "/sa.StorageAuthority/PauseIdentifiers"
|
||||||
|
StorageAuthority_UnpauseAccount_FullMethodName = "/sa.StorageAuthority/UnpauseAccount"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StorageAuthorityClient is the client API for StorageAuthority service.
|
// StorageAuthorityClient is the client API for StorageAuthority service.
|
||||||
|
|
@ -1354,6 +1434,8 @@ type StorageAuthorityClient interface {
|
||||||
KeyBlocked(ctx context.Context, in *SPKIHash, opts ...grpc.CallOption) (*Exists, error)
|
KeyBlocked(ctx context.Context, in *SPKIHash, opts ...grpc.CallOption) (*Exists, error)
|
||||||
ReplacementOrderExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error)
|
ReplacementOrderExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error)
|
||||||
SerialsForIncident(ctx context.Context, in *SerialsForIncidentRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[IncidentSerial], error)
|
SerialsForIncident(ctx context.Context, in *SerialsForIncidentRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[IncidentSerial], error)
|
||||||
|
CheckIdentifiersPaused(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*Identifiers, error)
|
||||||
|
GetPausedIdentifiers(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*Identifiers, error)
|
||||||
// Adders
|
// Adders
|
||||||
AddBlockedKey(ctx context.Context, in *AddBlockedKeyRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
AddBlockedKey(ctx context.Context, in *AddBlockedKeyRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
AddCertificate(ctx context.Context, in *AddCertificateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
AddCertificate(ctx context.Context, in *AddCertificateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
|
@ -1373,6 +1455,8 @@ type StorageAuthorityClient interface {
|
||||||
UpdateRevokedCertificate(ctx context.Context, in *RevokeCertificateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
UpdateRevokedCertificate(ctx context.Context, in *RevokeCertificateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
LeaseCRLShard(ctx context.Context, in *LeaseCRLShardRequest, opts ...grpc.CallOption) (*LeaseCRLShardResponse, error)
|
LeaseCRLShard(ctx context.Context, in *LeaseCRLShardRequest, opts ...grpc.CallOption) (*LeaseCRLShardResponse, error)
|
||||||
UpdateCRLShard(ctx context.Context, in *UpdateCRLShardRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
UpdateCRLShard(ctx context.Context, in *UpdateCRLShardRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
|
PauseIdentifiers(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*PauseIdentifiersResponse, error)
|
||||||
|
UnpauseAccount(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type storageAuthorityClient struct {
|
type storageAuthorityClient struct {
|
||||||
|
|
@ -1729,6 +1813,26 @@ func (c *storageAuthorityClient) SerialsForIncident(ctx context.Context, in *Ser
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type StorageAuthority_SerialsForIncidentClient = grpc.ServerStreamingClient[IncidentSerial]
|
type StorageAuthority_SerialsForIncidentClient = grpc.ServerStreamingClient[IncidentSerial]
|
||||||
|
|
||||||
|
func (c *storageAuthorityClient) CheckIdentifiersPaused(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*Identifiers, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(Identifiers)
|
||||||
|
err := c.cc.Invoke(ctx, StorageAuthority_CheckIdentifiersPaused_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *storageAuthorityClient) GetPausedIdentifiers(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*Identifiers, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(Identifiers)
|
||||||
|
err := c.cc.Invoke(ctx, StorageAuthority_GetPausedIdentifiers_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *storageAuthorityClient) AddBlockedKey(ctx context.Context, in *AddBlockedKeyRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
func (c *storageAuthorityClient) AddBlockedKey(ctx context.Context, in *AddBlockedKeyRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
out := new(emptypb.Empty)
|
out := new(emptypb.Empty)
|
||||||
|
|
@ -1909,6 +2013,26 @@ func (c *storageAuthorityClient) UpdateCRLShard(ctx context.Context, in *UpdateC
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *storageAuthorityClient) PauseIdentifiers(ctx context.Context, in *PauseRequest, opts ...grpc.CallOption) (*PauseIdentifiersResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(PauseIdentifiersResponse)
|
||||||
|
err := c.cc.Invoke(ctx, StorageAuthority_PauseIdentifiers_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *storageAuthorityClient) UnpauseAccount(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(emptypb.Empty)
|
||||||
|
err := c.cc.Invoke(ctx, StorageAuthority_UnpauseAccount_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StorageAuthorityServer is the server API for StorageAuthority service.
|
// StorageAuthorityServer is the server API for StorageAuthority service.
|
||||||
// All implementations must embed UnimplementedStorageAuthorityServer
|
// All implementations must embed UnimplementedStorageAuthorityServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
|
|
@ -1945,6 +2069,8 @@ type StorageAuthorityServer interface {
|
||||||
KeyBlocked(context.Context, *SPKIHash) (*Exists, error)
|
KeyBlocked(context.Context, *SPKIHash) (*Exists, error)
|
||||||
ReplacementOrderExists(context.Context, *Serial) (*Exists, error)
|
ReplacementOrderExists(context.Context, *Serial) (*Exists, error)
|
||||||
SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error
|
SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error
|
||||||
|
CheckIdentifiersPaused(context.Context, *PauseRequest) (*Identifiers, error)
|
||||||
|
GetPausedIdentifiers(context.Context, *RegistrationID) (*Identifiers, error)
|
||||||
// Adders
|
// Adders
|
||||||
AddBlockedKey(context.Context, *AddBlockedKeyRequest) (*emptypb.Empty, error)
|
AddBlockedKey(context.Context, *AddBlockedKeyRequest) (*emptypb.Empty, error)
|
||||||
AddCertificate(context.Context, *AddCertificateRequest) (*emptypb.Empty, error)
|
AddCertificate(context.Context, *AddCertificateRequest) (*emptypb.Empty, error)
|
||||||
|
|
@ -1964,6 +2090,8 @@ type StorageAuthorityServer interface {
|
||||||
UpdateRevokedCertificate(context.Context, *RevokeCertificateRequest) (*emptypb.Empty, error)
|
UpdateRevokedCertificate(context.Context, *RevokeCertificateRequest) (*emptypb.Empty, error)
|
||||||
LeaseCRLShard(context.Context, *LeaseCRLShardRequest) (*LeaseCRLShardResponse, error)
|
LeaseCRLShard(context.Context, *LeaseCRLShardRequest) (*LeaseCRLShardResponse, error)
|
||||||
UpdateCRLShard(context.Context, *UpdateCRLShardRequest) (*emptypb.Empty, error)
|
UpdateCRLShard(context.Context, *UpdateCRLShardRequest) (*emptypb.Empty, error)
|
||||||
|
PauseIdentifiers(context.Context, *PauseRequest) (*PauseIdentifiersResponse, error)
|
||||||
|
UnpauseAccount(context.Context, *RegistrationID) (*emptypb.Empty, error)
|
||||||
mustEmbedUnimplementedStorageAuthorityServer()
|
mustEmbedUnimplementedStorageAuthorityServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2064,6 +2192,12 @@ func (UnimplementedStorageAuthorityServer) ReplacementOrderExists(context.Contex
|
||||||
func (UnimplementedStorageAuthorityServer) SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error {
|
func (UnimplementedStorageAuthorityServer) SerialsForIncident(*SerialsForIncidentRequest, grpc.ServerStreamingServer[IncidentSerial]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method SerialsForIncident not implemented")
|
return status.Errorf(codes.Unimplemented, "method SerialsForIncident not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedStorageAuthorityServer) CheckIdentifiersPaused(context.Context, *PauseRequest) (*Identifiers, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CheckIdentifiersPaused not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedStorageAuthorityServer) GetPausedIdentifiers(context.Context, *RegistrationID) (*Identifiers, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetPausedIdentifiers not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedStorageAuthorityServer) AddBlockedKey(context.Context, *AddBlockedKeyRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStorageAuthorityServer) AddBlockedKey(context.Context, *AddBlockedKeyRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method AddBlockedKey not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method AddBlockedKey not implemented")
|
||||||
}
|
}
|
||||||
|
|
@ -2118,6 +2252,12 @@ func (UnimplementedStorageAuthorityServer) LeaseCRLShard(context.Context, *Lease
|
||||||
func (UnimplementedStorageAuthorityServer) UpdateCRLShard(context.Context, *UpdateCRLShardRequest) (*emptypb.Empty, error) {
|
func (UnimplementedStorageAuthorityServer) UpdateCRLShard(context.Context, *UpdateCRLShardRequest) (*emptypb.Empty, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateCRLShard not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method UpdateCRLShard not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedStorageAuthorityServer) PauseIdentifiers(context.Context, *PauseRequest) (*PauseIdentifiersResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method PauseIdentifiers not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedStorageAuthorityServer) UnpauseAccount(context.Context, *RegistrationID) (*emptypb.Empty, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UnpauseAccount not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedStorageAuthorityServer) mustEmbedUnimplementedStorageAuthorityServer() {}
|
func (UnimplementedStorageAuthorityServer) mustEmbedUnimplementedStorageAuthorityServer() {}
|
||||||
|
|
||||||
// UnsafeStorageAuthorityServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeStorageAuthorityServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
|
@ -2661,6 +2801,42 @@ func _StorageAuthority_SerialsForIncident_Handler(srv interface{}, stream grpc.S
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type StorageAuthority_SerialsForIncidentServer = grpc.ServerStreamingServer[IncidentSerial]
|
type StorageAuthority_SerialsForIncidentServer = grpc.ServerStreamingServer[IncidentSerial]
|
||||||
|
|
||||||
|
func _StorageAuthority_CheckIdentifiersPaused_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PauseRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StorageAuthorityServer).CheckIdentifiersPaused(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StorageAuthority_CheckIdentifiersPaused_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StorageAuthorityServer).CheckIdentifiersPaused(ctx, req.(*PauseRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StorageAuthority_GetPausedIdentifiers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(RegistrationID)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StorageAuthorityServer).GetPausedIdentifiers(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StorageAuthority_GetPausedIdentifiers_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StorageAuthorityServer).GetPausedIdentifiers(ctx, req.(*RegistrationID))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _StorageAuthority_AddBlockedKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _StorageAuthority_AddBlockedKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(AddBlockedKeyRequest)
|
in := new(AddBlockedKeyRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
|
|
@ -2985,6 +3161,42 @@ func _StorageAuthority_UpdateCRLShard_Handler(srv interface{}, ctx context.Conte
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _StorageAuthority_PauseIdentifiers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(PauseRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StorageAuthorityServer).PauseIdentifiers(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StorageAuthority_PauseIdentifiers_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StorageAuthorityServer).PauseIdentifiers(ctx, req.(*PauseRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _StorageAuthority_UnpauseAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(RegistrationID)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(StorageAuthorityServer).UnpauseAccount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: StorageAuthority_UnpauseAccount_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(StorageAuthorityServer).UnpauseAccount(ctx, req.(*RegistrationID))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// StorageAuthority_ServiceDesc is the grpc.ServiceDesc for StorageAuthority service.
|
// StorageAuthority_ServiceDesc is the grpc.ServiceDesc for StorageAuthority service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
|
@ -3100,6 +3312,14 @@ var StorageAuthority_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "ReplacementOrderExists",
|
MethodName: "ReplacementOrderExists",
|
||||||
Handler: _StorageAuthority_ReplacementOrderExists_Handler,
|
Handler: _StorageAuthority_ReplacementOrderExists_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CheckIdentifiersPaused",
|
||||||
|
Handler: _StorageAuthority_CheckIdentifiersPaused_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetPausedIdentifiers",
|
||||||
|
Handler: _StorageAuthority_GetPausedIdentifiers_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "AddBlockedKey",
|
MethodName: "AddBlockedKey",
|
||||||
Handler: _StorageAuthority_AddBlockedKey_Handler,
|
Handler: _StorageAuthority_AddBlockedKey_Handler,
|
||||||
|
|
@ -3172,6 +3392,14 @@ var StorageAuthority_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "UpdateCRLShard",
|
MethodName: "UpdateCRLShard",
|
||||||
Handler: _StorageAuthority_UpdateCRLShard_Handler,
|
Handler: _StorageAuthority_UpdateCRLShard_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "PauseIdentifiers",
|
||||||
|
Handler: _StorageAuthority_PauseIdentifiers_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "UnpauseAccount",
|
||||||
|
Handler: _StorageAuthority_UnpauseAccount_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
129
sa/sa.go
129
sa/sa.go
|
|
@ -3,6 +3,7 @@ package sa
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -1312,3 +1313,131 @@ func (ssa *SQLStorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.Up
|
||||||
|
|
||||||
return &emptypb.Empty{}, nil
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PauseIdentifiers pauses a set of identifiers for the provided account. If an
|
||||||
|
// identifier is currently paused, this is a no-op. If an identifier was
|
||||||
|
// previously paused and unpaused, it will be repaused. All work is accomplished
|
||||||
|
// in a transaction to limit possible race conditions.
|
||||||
|
func (ssa *SQLStorageAuthority) PauseIdentifiers(ctx context.Context, req *sapb.PauseRequest) (*sapb.PauseIdentifiersResponse, error) {
|
||||||
|
if core.IsAnyNilOrZero(req.RegistrationID, req.Identifiers) {
|
||||||
|
return nil, errIncompleteRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal the identifier now that we've crossed the RPC boundary.
|
||||||
|
identifiers, err := newIdentifierModelsFromPB(req.Identifiers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &sapb.PauseIdentifiersResponse{}
|
||||||
|
_, err = db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||||
|
for _, identifier := range identifiers {
|
||||||
|
pauseError := func(op string, err error) error {
|
||||||
|
return fmt.Errorf("while %s identifier %s for registration ID %d: %w",
|
||||||
|
op, identifier.Value, req.RegistrationID, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry pausedModel
|
||||||
|
err := tx.SelectOne(ctx, &entry, `
|
||||||
|
SELECT pausedAt, unpausedAt
|
||||||
|
FROM paused
|
||||||
|
WHERE
|
||||||
|
registrationID = ? AND
|
||||||
|
identifierType = ? AND
|
||||||
|
identifierValue = ?`,
|
||||||
|
req.RegistrationID,
|
||||||
|
identifier.Type,
|
||||||
|
identifier.Value,
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err != nil && !errors.Is(err, sql.ErrNoRows):
|
||||||
|
// Error querying the database.
|
||||||
|
return nil, pauseError("querying pause status for", err)
|
||||||
|
|
||||||
|
case err != nil && errors.Is(err, sql.ErrNoRows):
|
||||||
|
// Not currently or previously paused, insert a new pause record.
|
||||||
|
pausedAt := ssa.clk.Now().Truncate(time.Second)
|
||||||
|
err = tx.Insert(ctx, &pausedModel{
|
||||||
|
RegistrationID: req.RegistrationID,
|
||||||
|
PausedAt: &pausedAt,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifier.Type,
|
||||||
|
Value: identifier.Value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil && !db.IsDuplicate(err) {
|
||||||
|
return nil, pauseError("pausing", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier successfully paused.
|
||||||
|
response.Paused++
|
||||||
|
continue
|
||||||
|
|
||||||
|
case entry.UnpausedAt == nil || entry.PausedAt.After(*entry.UnpausedAt):
|
||||||
|
// Identifier is already paused.
|
||||||
|
continue
|
||||||
|
|
||||||
|
case entry.UnpausedAt.After(*entry.PausedAt):
|
||||||
|
// Previously paused (and unpaused), repause the identifier.
|
||||||
|
_, err := tx.ExecContext(ctx, `
|
||||||
|
UPDATE paused
|
||||||
|
SET pausedAt = ?,
|
||||||
|
unpausedAt = NULL
|
||||||
|
WHERE
|
||||||
|
registrationID = ? AND
|
||||||
|
identifierType = ? AND
|
||||||
|
identifierValue = ? AND
|
||||||
|
unpausedAt IS NOT NULL`,
|
||||||
|
ssa.clk.Now().Truncate(time.Second),
|
||||||
|
req.RegistrationID,
|
||||||
|
identifier.Type,
|
||||||
|
identifier.Value,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, pauseError("repausing", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier successfully repaused.
|
||||||
|
response.Repaused++
|
||||||
|
continue
|
||||||
|
|
||||||
|
default:
|
||||||
|
// This indicates a database state which should never occur.
|
||||||
|
return nil, fmt.Errorf("impossible database state encountered while pausing identifier %s",
|
||||||
|
identifier.Value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// Error occurred during transaction.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnpauseAccount will unpause all paused identifiers for the provided account.
|
||||||
|
// If no identifiers are currently paused, this is a no-op.
|
||||||
|
func (ssa *SQLStorageAuthority) UnpauseAccount(ctx context.Context, req *sapb.RegistrationID) (*emptypb.Empty, error) {
|
||||||
|
if core.IsAnyNilOrZero(req.Id) {
|
||||||
|
return nil, errIncompleteRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ssa.dbMap.ExecContext(ctx, `
|
||||||
|
UPDATE paused
|
||||||
|
SET unpausedAt = ?
|
||||||
|
WHERE
|
||||||
|
registrationID = ? AND
|
||||||
|
unpausedAt IS NULL`,
|
||||||
|
ssa.clk.Now().Truncate(time.Second),
|
||||||
|
req.Id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
581
sa/sa_test.go
581
sa/sa_test.go
|
|
@ -40,6 +40,7 @@ import (
|
||||||
berrors "github.com/letsencrypt/boulder/errors"
|
berrors "github.com/letsencrypt/boulder/errors"
|
||||||
"github.com/letsencrypt/boulder/features"
|
"github.com/letsencrypt/boulder/features"
|
||||||
bgrpc "github.com/letsencrypt/boulder/grpc"
|
bgrpc "github.com/letsencrypt/boulder/grpc"
|
||||||
|
"github.com/letsencrypt/boulder/identifier"
|
||||||
blog "github.com/letsencrypt/boulder/log"
|
blog "github.com/letsencrypt/boulder/log"
|
||||||
"github.com/letsencrypt/boulder/metrics"
|
"github.com/letsencrypt/boulder/metrics"
|
||||||
"github.com/letsencrypt/boulder/probs"
|
"github.com/letsencrypt/boulder/probs"
|
||||||
|
|
@ -4307,3 +4308,583 @@ func TestGetSerialsByAccount(t *testing.T) {
|
||||||
test.AssertNotError(t, err, "calling GetSerialsByAccount")
|
test.AssertNotError(t, err, "calling GetSerialsByAccount")
|
||||||
test.AssertEquals(t, len(seen), 2)
|
test.AssertEquals(t, len(seen), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnpauseAccount(t *testing.T) {
|
||||||
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
||||||
|
t.Skip("Test requires paused database table")
|
||||||
|
}
|
||||||
|
sa, _, cleanUp := initSA(t)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
ptrTime := func(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
state []pausedModel
|
||||||
|
req *sapb.RegistrationID
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "UnpauseAccount with no paused identifiers",
|
||||||
|
args: args{
|
||||||
|
state: nil,
|
||||||
|
req: &sapb.RegistrationID{Id: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UnpauseAccount with one paused identifier",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.RegistrationID{Id: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "UnpauseAccount with multiple paused identifiers",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.org",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.RegistrationID{Id: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Setup table state.
|
||||||
|
for _, state := range tt.args.state {
|
||||||
|
err := sa.dbMap.Insert(ctx, &state)
|
||||||
|
test.AssertNotError(t, err, "inserting test identifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := sa.UnpauseAccount(ctx, tt.args.req)
|
||||||
|
test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()")
|
||||||
|
|
||||||
|
// Count the number of paused identifiers.
|
||||||
|
var count int
|
||||||
|
err = sa.dbReadOnlyMap.SelectOne(
|
||||||
|
ctx,
|
||||||
|
&count,
|
||||||
|
"SELECT COUNT(*) FROM paused WHERE registrationID = ? AND unpausedAt IS NULL",
|
||||||
|
tt.args.req.Id,
|
||||||
|
)
|
||||||
|
test.AssertNotError(t, err, "SELECT COUNT(*) failed")
|
||||||
|
test.AssertEquals(t, count, 0)
|
||||||
|
|
||||||
|
// Drop all rows from the paused table.
|
||||||
|
_, err = sa.dbMap.ExecContext(ctx, "TRUNCATE TABLE paused")
|
||||||
|
test.AssertNotError(t, err, "Truncate table paused failed")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPauseIdentifiers(t *testing.T) {
|
||||||
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
||||||
|
t.Skip("Test requires paused database table")
|
||||||
|
}
|
||||||
|
sa, _, cleanUp := initSA(t)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
ptrTime := func(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
state []pausedModel
|
||||||
|
req *sapb.PauseRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *sapb.PauseIdentifiersResponse
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "An identifier which is not now or previously paused",
|
||||||
|
args: args{
|
||||||
|
state: nil,
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.PauseIdentifiersResponse{
|
||||||
|
Paused: 1,
|
||||||
|
Repaused: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "One unpaused entry which was previously paused",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.PauseIdentifiersResponse{
|
||||||
|
Paused: 0,
|
||||||
|
Repaused: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "An identifier which is currently paused",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.PauseIdentifiersResponse{
|
||||||
|
Paused: 0,
|
||||||
|
Repaused: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Two previously paused entries and one new entry",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.PauseIdentifiersResponse{
|
||||||
|
Paused: 1,
|
||||||
|
Repaused: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Setup table state.
|
||||||
|
for _, state := range tt.args.state {
|
||||||
|
err := sa.dbMap.Insert(ctx, &state)
|
||||||
|
test.AssertNotError(t, err, "inserting test identifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := sa.PauseIdentifiers(ctx, tt.args.req)
|
||||||
|
test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()")
|
||||||
|
test.AssertEquals(t, got.Paused, tt.want.Paused)
|
||||||
|
test.AssertEquals(t, got.Repaused, tt.want.Repaused)
|
||||||
|
|
||||||
|
// Drop all rows from the paused table.
|
||||||
|
_, err = sa.dbMap.ExecContext(ctx, "TRUNCATE TABLE paused")
|
||||||
|
test.AssertNotError(t, err, "Truncate table paused failed")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckIdentifiersPaused(t *testing.T) {
|
||||||
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
||||||
|
t.Skip("Test requires paused database table")
|
||||||
|
}
|
||||||
|
sa, _, cleanUp := initSA(t)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
ptrTime := func(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
state []pausedModel
|
||||||
|
req *sapb.PauseRequest
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *sapb.Identifiers
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "No paused identifiers",
|
||||||
|
args: args{
|
||||||
|
state: nil,
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.Identifiers{
|
||||||
|
Identifiers: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "One paused identifier",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.Identifiers{
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Two paused identifiers, one unpaused",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.org",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.PauseRequest{
|
||||||
|
RegistrationID: 1,
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &sapb.Identifiers{
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Setup table state.
|
||||||
|
for _, state := range tt.args.state {
|
||||||
|
err := sa.dbMap.Insert(ctx, &state)
|
||||||
|
test.AssertNotError(t, err, "inserting test identifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := sa.CheckIdentifiersPaused(ctx, tt.args.req)
|
||||||
|
test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()")
|
||||||
|
test.AssertDeepEquals(t, got.Identifiers, tt.want.Identifiers)
|
||||||
|
|
||||||
|
// Drop all rows from the paused table.
|
||||||
|
_, err = sa.dbMap.ExecContext(ctx, "TRUNCATE TABLE paused")
|
||||||
|
test.AssertNotError(t, err, "Truncate table paused failed")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPausedIdentifiers(t *testing.T) {
|
||||||
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
||||||
|
t.Skip("Test requires paused database table")
|
||||||
|
}
|
||||||
|
sa, _, cleanUp := initSA(t)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
ptrTime := func(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
state []pausedModel
|
||||||
|
req *sapb.RegistrationID
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *sapb.Identifiers
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "No paused identifiers",
|
||||||
|
args: args{
|
||||||
|
state: nil,
|
||||||
|
req: &sapb.RegistrationID{Id: 1},
|
||||||
|
},
|
||||||
|
want: &sapb.Identifiers{
|
||||||
|
Identifiers: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "One paused identifier",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.RegistrationID{Id: 1},
|
||||||
|
},
|
||||||
|
want: &sapb.Identifiers{
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Two paused identifiers, one unpaused",
|
||||||
|
args: args{
|
||||||
|
state: []pausedModel{
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.org",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: &sapb.RegistrationID{Id: 1},
|
||||||
|
},
|
||||||
|
want: &sapb.Identifiers{
|
||||||
|
Identifiers: []*sapb.Identifier{
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: string(identifier.DNS),
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Setup table state.
|
||||||
|
for _, state := range tt.args.state {
|
||||||
|
err := sa.dbMap.Insert(ctx, &state)
|
||||||
|
test.AssertNotError(t, err, "inserting test identifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := sa.GetPausedIdentifiers(ctx, tt.args.req)
|
||||||
|
test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()")
|
||||||
|
test.AssertDeepEquals(t, got.Identifiers, tt.want.Identifiers)
|
||||||
|
|
||||||
|
// Drop all rows from the paused table.
|
||||||
|
_, err = sa.dbMap.ExecContext(ctx, "TRUNCATE TABLE paused")
|
||||||
|
test.AssertNotError(t, err, "Truncate table paused failed")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPausedIdentifiersOnlyUnpausesOneAccount(t *testing.T) {
|
||||||
|
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
||||||
|
t.Skip("Test requires paused database table")
|
||||||
|
}
|
||||||
|
sa, _, cleanUp := initSA(t)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
ptrTime := func(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert two paused identifiers for two different accounts.
|
||||||
|
err := sa.dbMap.Insert(ctx, &pausedModel{
|
||||||
|
RegistrationID: 1,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.com",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
})
|
||||||
|
test.AssertNotError(t, err, "inserting test identifier")
|
||||||
|
|
||||||
|
err = sa.dbMap.Insert(ctx, &pausedModel{
|
||||||
|
RegistrationID: 2,
|
||||||
|
identifierModel: identifierModel{
|
||||||
|
Type: identifierTypeToUint[string(identifier.DNS)],
|
||||||
|
Value: "example.net",
|
||||||
|
},
|
||||||
|
PausedAt: ptrTime(sa.clk.Now().Add(-time.Hour)),
|
||||||
|
})
|
||||||
|
test.AssertNotError(t, err, "inserting test identifier")
|
||||||
|
|
||||||
|
// Unpause the first account.
|
||||||
|
_, err = sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: 1})
|
||||||
|
test.AssertNotError(t, err, "UnpauseAccount failed")
|
||||||
|
|
||||||
|
// Check that the second account's identifier is still paused.
|
||||||
|
identifiers, err := sa.GetPausedIdentifiers(ctx, &sapb.RegistrationID{Id: 2})
|
||||||
|
test.AssertNotError(t, err, "GetPausedIdentifiers failed")
|
||||||
|
test.AssertEquals(t, len(identifiers.Identifiers), 1)
|
||||||
|
test.AssertEquals(t, identifiers.Identifiers[0].Value, "example.net")
|
||||||
|
}
|
||||||
|
|
|
||||||
93
sa/saro.go
93
sa/saro.go
|
|
@ -1402,3 +1402,96 @@ func (ssa *SQLStorageAuthorityRO) GetSerialsByAccount(req *sapb.RegistrationID,
|
||||||
return stream.Send(&sapb.Serial{Serial: row.Serial})
|
return stream.Send(&sapb.Serial{Serial: row.Serial})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIdentifiersPaused takes a slice of identifiers and returns a slice of
|
||||||
|
// the first 15 identifier values which are currently paused for the provided
|
||||||
|
// account. If no matches are found, an empty slice is returned.
|
||||||
|
func (ssa *SQLStorageAuthorityRO) CheckIdentifiersPaused(ctx context.Context, req *sapb.PauseRequest) (*sapb.Identifiers, error) {
|
||||||
|
if core.IsAnyNilOrZero(req.RegistrationID, req.Identifiers) {
|
||||||
|
return nil, errIncompleteRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
identifiers, err := newIdentifierModelsFromPB(req.Identifiers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(identifiers) == 0 {
|
||||||
|
// No identifier values to check.
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
identifiersByType := map[uint8][]string{}
|
||||||
|
for _, id := range identifiers {
|
||||||
|
identifiersByType[id.Type] = append(identifiersByType[id.Type], id.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a query to retrieve up to 15 paused identifiers using OR clauses
|
||||||
|
// for conditions specific to each type. This approach handles mixed
|
||||||
|
// identifier types in a single query. Assuming 3 DNS identifiers and 1 IP
|
||||||
|
// identifier, the resulting query would look like:
|
||||||
|
//
|
||||||
|
// SELECT identifierType, identifierValue
|
||||||
|
// FROM paused WHERE registrationID = ? AND
|
||||||
|
// unpausedAt IS NULL AND
|
||||||
|
// ((identifierType = ? AND identifierValue IN (?, ?, ?)) OR
|
||||||
|
// (identifierType = ? AND identifierValue IN (?)))
|
||||||
|
// LIMIT 15
|
||||||
|
//
|
||||||
|
// Corresponding args array for placeholders: [<regID>, 0, "example.com",
|
||||||
|
// "example.net", "example.org", 1, "1.2.3.4"]
|
||||||
|
|
||||||
|
var conditions []string
|
||||||
|
args := []interface{}{req.RegistrationID}
|
||||||
|
for idType, values := range identifiersByType {
|
||||||
|
conditions = append(conditions,
|
||||||
|
fmt.Sprintf("identifierType = ? AND identifierValue IN (%s)",
|
||||||
|
db.QuestionMarks(len(values)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
args = append(args, idType)
|
||||||
|
for _, value := range values {
|
||||||
|
args = append(args, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
SELECT identifierType, identifierValue
|
||||||
|
FROM paused
|
||||||
|
WHERE registrationID = ? AND unpausedAt IS NULL AND (%s) LIMIT 15`,
|
||||||
|
strings.Join(conditions, " OR "))
|
||||||
|
|
||||||
|
var matches []identifierModel
|
||||||
|
_, err = ssa.dbReadOnlyMap.Select(ctx, &matches, query, args...)
|
||||||
|
if err != nil && !db.IsNoRows(err) {
|
||||||
|
// Error querying the database.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPBFromIdentifierModels(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPausedIdentifiers returns a slice of paused identifiers for the provided
|
||||||
|
// account. If no paused identifiers are found, an empty slice is returned. The
|
||||||
|
// results are limited to the first 15 paused identifiers.
|
||||||
|
func (ssa *SQLStorageAuthorityRO) GetPausedIdentifiers(ctx context.Context, req *sapb.RegistrationID) (*sapb.Identifiers, error) {
|
||||||
|
if core.IsAnyNilOrZero(req.Id) {
|
||||||
|
return nil, errIncompleteRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches []identifierModel
|
||||||
|
_, err := ssa.dbReadOnlyMap.Select(ctx, &matches, `
|
||||||
|
SELECT identifierType, identifierValue
|
||||||
|
FROM paused
|
||||||
|
WHERE
|
||||||
|
registrationID = ? AND
|
||||||
|
unpausedAt IS NULL
|
||||||
|
LIMIT 15`,
|
||||||
|
req.Id,
|
||||||
|
)
|
||||||
|
if err != nil && !db.IsNoRows(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPBFromIdentifierModels(matches)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue