Add legacydb package to support existing MySQL DB
- add new legacydb package - fix warnings
This commit is contained in:
parent
eb92755ef6
commit
a6317c82c5
8 changed files with 1437 additions and 67 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2022 CAcert Inc.
|
Copyright CAcert Inc.
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -31,6 +31,10 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosignerclient/internal/legacydb"
|
||||||
|
|
||||||
"git.cacert.org/cacert-gosignerclient/internal/client"
|
"git.cacert.org/cacert-gosignerclient/internal/client"
|
||||||
"git.cacert.org/cacert-gosignerclient/internal/config"
|
"git.cacert.org/cacert-gosignerclient/internal/config"
|
||||||
"git.cacert.org/cacert-gosignerclient/internal/handler"
|
"git.cacert.org/cacert-gosignerclient/internal/handler"
|
||||||
|
@ -103,6 +107,8 @@ func generateDefaultConfig() error {
|
||||||
serial:
|
serial:
|
||||||
device: /dev/ttyUSB0
|
device: /dev/ttyUSB0
|
||||||
baud: 112500
|
baud: 112500
|
||||||
|
database:
|
||||||
|
dsn: "user:password@/dbname"
|
||||||
`
|
`
|
||||||
|
|
||||||
cfg, err := config.LoadConfiguration(strings.NewReader(defaultBaseConfiguration))
|
cfg, err := config.LoadConfiguration(strings.NewReader(defaultBaseConfiguration))
|
||||||
|
@ -141,6 +147,18 @@ func startClient(configFile string, logger *logrus.Logger) error {
|
||||||
|
|
||||||
defer func() { _ = signerClient.Close() }()
|
defer func() { _ = signerClient.Close() }()
|
||||||
|
|
||||||
|
commands := make(chan *protocol.Command, clientConfig.CommandChannelCapacity)
|
||||||
|
|
||||||
|
legacyDB, err := legacydb.New(logger, &clientConfig.Database, commands)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not initialize legacy database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = legacyDB.Close() }()
|
||||||
|
|
||||||
|
signerClient.RegisterCommandSource(legacyDB)
|
||||||
|
signerClient.RegisterResponseSink(legacyDB)
|
||||||
|
|
||||||
logger.Info("setup complete, starting client operation")
|
logger.Info("setup complete, starting client operation")
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
@ -157,14 +175,14 @@ func startClient(configFile string, logger *logrus.Logger) error {
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
callbacks := make(chan interface{}, client.CallBackBufferSize)
|
callbacks := make(chan any, client.CallBackBufferSize)
|
||||||
|
|
||||||
clientHandler, err := handler.New(clientConfig, logger, callbacks)
|
clientHandler, err := handler.New(clientConfig, logger, callbacks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not setup client handler: %w", err)
|
return fmt.Errorf("could not setup client handler: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = signerClient.Run(ctx, callbacks, clientHandler); err != nil {
|
if err = signerClient.Run(ctx, callbacks, clientHandler, commands); err != nil {
|
||||||
return fmt.Errorf("error in client: %w", err)
|
return fmt.Errorf("error in client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2022 CAcert Inc.
|
Copyright CAcert Inc.
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -82,21 +82,24 @@ type Client struct {
|
||||||
config *config.ClientConfig
|
config *config.ClientConfig
|
||||||
signerInfo *SignerInfo
|
signerInfo *SignerInfo
|
||||||
knownCACertificates map[string]*CACertificateInfo
|
knownCACertificates map[string]*CACertificateInfo
|
||||||
|
commandSources []CommandSource
|
||||||
|
responseSinks map[messages.ResponseCode]ResponseSink
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Run(
|
func (c *Client) Run(
|
||||||
ctx context.Context, callback <-chan interface{}, handler protocol.ClientHandler,
|
ctx context.Context, callback <-chan any, handler protocol.ClientHandler,
|
||||||
|
commands chan *protocol.Command,
|
||||||
) error {
|
) error {
|
||||||
const componentCount = 4
|
const componentCount = 4
|
||||||
|
|
||||||
protocolErrors, framerErrors := make(chan error), make(chan error)
|
protocolErrors, framerErrors, sourceErrors := make(chan error), make(chan error), make(chan error)
|
||||||
|
|
||||||
subCtx, cancel := context.WithCancel(ctx)
|
subCtx, cancel := context.WithCancel(ctx)
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
wg.Add(componentCount)
|
wg.Add(componentCount)
|
||||||
|
wg.Add(len(c.commandSources))
|
||||||
|
|
||||||
commands := make(chan *protocol.Command, c.config.CommandChannelCapacity)
|
|
||||||
fromSigner := make(chan []byte)
|
fromSigner := make(chan []byte)
|
||||||
toSigner := make(chan []byte)
|
toSigner := make(chan []byte)
|
||||||
|
|
||||||
|
@ -107,6 +110,8 @@ func (c *Client) Run(
|
||||||
c.logger.Info("shutdown complete")
|
c.logger.Info("shutdown complete")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
c.RunSources(subCtx, &wg, sourceErrors)
|
||||||
|
|
||||||
go func(f protocol.Framer) {
|
go func(f protocol.Framer) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
|
@ -171,6 +176,12 @@ func (c *Client) Run(
|
||||||
return fmt.Errorf("error from protocol: %w", err)
|
return fmt.Errorf("error from protocol: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
case err := <-sourceErrors:
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error from command source: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,10 +216,15 @@ func (c *Client) Close() error {
|
||||||
|
|
||||||
type commandGenerator func(context.Context, chan<- *protocol.Command) error
|
type commandGenerator func(context.Context, chan<- *protocol.Command) error
|
||||||
|
|
||||||
func (c *Client) commandLoop(ctx context.Context, commands chan *protocol.Command, callback <-chan interface{}) {
|
func (c *Client) commandLoop(ctx context.Context, commands chan *protocol.Command, callback <-chan any) {
|
||||||
healthTimer := time.NewTimer(c.config.HealthStart)
|
healthTimer := time.NewTimer(c.config.HealthStart)
|
||||||
fetchCRLTimer := time.NewTimer(c.config.FetchCRLStart)
|
fetchCRLTimer := time.NewTimer(c.config.FetchCRLStart)
|
||||||
nextCommands := make(chan *protocol.Command)
|
|
||||||
|
defer func() {
|
||||||
|
close(commands)
|
||||||
|
|
||||||
|
c.logger.Info("command loop stopped")
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -216,57 +232,54 @@ func (c *Client) commandLoop(ctx context.Context, commands chan *protocol.Comman
|
||||||
return
|
return
|
||||||
case callbackData := <-callback:
|
case callbackData := <-callback:
|
||||||
go func() {
|
go func() {
|
||||||
err := c.handleCallback(ctx, nextCommands, callbackData)
|
err := c.handleCallback(ctx, commands, callbackData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.WithError(err).Error("callback handling failed")
|
c.logger.WithError(err).Error("callback handling failed")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
case <-fetchCRLTimer.C:
|
case <-fetchCRLTimer.C:
|
||||||
go c.scheduleRequiredCRLFetches(ctx, nextCommands)
|
go c.scheduleRequiredCRLFetches(ctx, commands)
|
||||||
|
|
||||||
fetchCRLTimer.Reset(c.config.FetchCRLInterval)
|
fetchCRLTimer.Reset(c.config.FetchCRLInterval)
|
||||||
case <-healthTimer.C:
|
case <-healthTimer.C:
|
||||||
go c.scheduleHealthCheck(ctx, nextCommands)
|
go c.scheduleHealthCheck(ctx, commands)
|
||||||
|
|
||||||
healthTimer.Reset(c.config.HealthInterval)
|
healthTimer.Reset(c.config.HealthInterval)
|
||||||
case nextCommand, ok := <-nextCommands:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commands <- nextCommand
|
type ErrNoResponseSink struct {
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
c.logger.WithFields(map[string]interface{}{
|
func (e ErrNoResponseSink) Error() string {
|
||||||
"command": nextCommand.Announce,
|
return fmt.Sprintf("no response sink for %s response found", e.msg)
|
||||||
"buffer length": len(commands),
|
|
||||||
}).Trace("sent command")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) handleCallback(
|
func (c *Client) handleCallback(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
newCommands chan<- *protocol.Command,
|
newCommands chan<- *protocol.Command,
|
||||||
data interface{},
|
data any,
|
||||||
) error {
|
) error {
|
||||||
var handler commandGenerator
|
var (
|
||||||
|
handler commandGenerator
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
switch d := data.(type) {
|
switch d := data.(type) {
|
||||||
case SignerInfo:
|
case SignerInfo:
|
||||||
handler = c.updateSignerInfo(d)
|
handler = c.updateSignerInfo(d)
|
||||||
case *messages.CAInfoResponse:
|
case *protocol.Response:
|
||||||
handler = c.updateCAInformation(d)
|
handler, err = c.handleResponse(d)
|
||||||
case *messages.FetchCRLResponse:
|
if err != nil {
|
||||||
handler = c.updateCRL(d)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown callback data of type %T", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := handler(ctx, newCommands); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown callback data of type %T", d)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return handler(ctx, newCommands)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) updateSignerInfo(
|
func (c *Client) updateSignerInfo(
|
||||||
|
@ -609,6 +622,94 @@ func (c *Client) setLastKnownCRL(caName string, number *big.Int) {
|
||||||
caInfo.LastKnownCRL = number
|
caInfo.LastKnownCRL = number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommandSource interface {
|
||||||
|
Run(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseSink interface {
|
||||||
|
SupportedResponses() []messages.ResponseCode
|
||||||
|
HandleResponse(context.Context, *messages.ResponseAnnounce, any) error
|
||||||
|
NotifyError(ctx context.Context, requestID, message string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RegisterCommandSource(source CommandSource) {
|
||||||
|
c.commandSources = append(c.commandSources, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RegisterResponseSink(sink ResponseSink) {
|
||||||
|
for _, code := range sink.SupportedResponses() {
|
||||||
|
c.responseSinks[code] = sink
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleResponse(r *protocol.Response) (commandGenerator, error) {
|
||||||
|
var handler commandGenerator
|
||||||
|
|
||||||
|
switch payload := r.Response.(type) {
|
||||||
|
case *messages.CAInfoResponse:
|
||||||
|
handler = c.updateCAInformation(payload)
|
||||||
|
case *messages.FetchCRLResponse:
|
||||||
|
handler = c.updateCRL(payload)
|
||||||
|
case *messages.ErrorResponse:
|
||||||
|
handler = func(ctx context.Context, _ chan<- *protocol.Command) error {
|
||||||
|
for _, sink := range c.responseSinks {
|
||||||
|
if err := sink.NotifyError(ctx, r.Announce.ID, payload.Message); err != nil {
|
||||||
|
return fmt.Errorf("error from response sink: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case *messages.SignCertificateResponse:
|
||||||
|
sink, ok := c.responseSinks[messages.RespSignCertificate]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrNoResponseSink{"sign certificate"}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = func(ctx context.Context, _ chan<- *protocol.Command) error {
|
||||||
|
if err := sink.HandleResponse(ctx, r.Announce, payload); err != nil {
|
||||||
|
return fmt.Errorf("error from response sink: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case *messages.SignOpenPGPResponse:
|
||||||
|
sink, ok := c.responseSinks[messages.RespSignOpenPGP]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrNoResponseSink{"sign openpgp"}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = func(ctx context.Context, _ chan<- *protocol.Command) error {
|
||||||
|
if err := sink.HandleResponse(ctx, r.Announce, payload); err != nil {
|
||||||
|
return fmt.Errorf("error from response sink: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unhandled response %s", payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RunSources(ctx context.Context, wg *sync.WaitGroup, errorChan chan error) {
|
||||||
|
for _, source := range c.commandSources {
|
||||||
|
go func(s CommandSource) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
err := s.Run(ctx)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.WithError(err).Error("command source failed")
|
||||||
|
|
||||||
|
errorChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Info("command source stopped")
|
||||||
|
}(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
cfg *config.ClientConfig,
|
cfg *config.ClientConfig,
|
||||||
logger *logrus.Logger,
|
logger *logrus.Logger,
|
||||||
|
@ -623,6 +724,8 @@ func New(
|
||||||
framer: cobsFramer,
|
framer: cobsFramer,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
knownCACertificates: make(map[string]*CACertificateInfo),
|
knownCACertificates: make(map[string]*CACertificateInfo),
|
||||||
|
responseSinks: make(map[messages.ResponseCode]ResponseSink),
|
||||||
|
commandSources: make([]CommandSource, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.setupConnection(&serial.Config{
|
err = client.setupConnection(&serial.Config{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2022 CAcert Inc.
|
Copyright 2022-2023 CAcert Inc.
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -36,6 +36,9 @@ const (
|
||||||
defaultResponseDataTimeout = 2 * time.Second
|
defaultResponseDataTimeout = 2 * time.Second
|
||||||
defaultFilesDirectory = "public"
|
defaultFilesDirectory = "public"
|
||||||
defaultCommandChannelCapacity = 100
|
defaultCommandChannelCapacity = 100
|
||||||
|
defaultDatabaseConnMaxLiveTime = 3 * time.Minute
|
||||||
|
defaultDatabaseMaxOpenConns = 10
|
||||||
|
defaultDatabaseMaxIdleConns = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
type SettingsError struct {
|
type SettingsError struct {
|
||||||
|
@ -52,6 +55,13 @@ type Serial struct {
|
||||||
Timeout time.Duration `yaml:"timeout"`
|
Timeout time.Duration `yaml:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
DSN string `yaml:"dsn"`
|
||||||
|
ConnMaxLiveTime time.Duration `yaml:"conn-max-live-time"`
|
||||||
|
MaxOpenConns int `yaml:"max-open-conns"`
|
||||||
|
MaxIdleConns int `yaml:"max-idle-conns"`
|
||||||
|
}
|
||||||
|
|
||||||
type ClientConfig struct {
|
type ClientConfig struct {
|
||||||
Serial Serial `yaml:"serial"`
|
Serial Serial `yaml:"serial"`
|
||||||
HealthInterval time.Duration `yaml:"health-interval"`
|
HealthInterval time.Duration `yaml:"health-interval"`
|
||||||
|
@ -63,6 +73,7 @@ type ClientConfig struct {
|
||||||
PublicCRLDirectory string `yaml:"public-crl-directory"`
|
PublicCRLDirectory string `yaml:"public-crl-directory"`
|
||||||
PublicCertificateDirectory string `yaml:"public-certificate-directory"`
|
PublicCertificateDirectory string `yaml:"public-certificate-directory"`
|
||||||
CommandChannelCapacity int `yaml:"command-channel-capacity"`
|
CommandChannelCapacity int `yaml:"command-channel-capacity"`
|
||||||
|
Database Database `yaml:"database"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||||
|
@ -77,6 +88,7 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||||
PublicCRLDirectory string `yaml:"public-crl-directory"`
|
PublicCRLDirectory string `yaml:"public-crl-directory"`
|
||||||
PublicCertificateDirectory string `yaml:"public-certificate-directory"`
|
PublicCertificateDirectory string `yaml:"public-certificate-directory"`
|
||||||
CommandChannelCapacity int `yaml:"command-channel-capacity"`
|
CommandChannelCapacity int `yaml:"command-channel-capacity"`
|
||||||
|
Database Database `yaml:"database"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := n.Decode(&data)
|
err := n.Decode(&data)
|
||||||
|
@ -84,16 +96,8 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||||
return fmt.Errorf("could not decode YAML: %w", err)
|
return fmt.Errorf("could not decode YAML: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.Serial.Device == "" {
|
if err := checkSerialConfig(&data.Serial); err != nil {
|
||||||
return SettingsError{"you must specify a serial 'device'"}
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if data.Serial.Baud == 0 {
|
|
||||||
data.Serial.Baud = 115200
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.Serial.Timeout == 0 {
|
|
||||||
data.Serial.Timeout = defaultSerialTimeout
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Serial = data.Serial
|
c.Serial = data.Serial
|
||||||
|
@ -152,6 +156,50 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
|
||||||
|
|
||||||
c.PublicCertificateDirectory = data.PublicCRLDirectory
|
c.PublicCertificateDirectory = data.PublicCRLDirectory
|
||||||
|
|
||||||
|
if err := checkDatabaseConfig(&data.Database); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Database = data.Database
|
||||||
|
|
||||||
|
c.Database = data.Database
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDatabaseConfig(d *Database) error {
|
||||||
|
if d.DSN == "" {
|
||||||
|
return SettingsError{"you must specify a database 'dsn'"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.ConnMaxLiveTime == 0 {
|
||||||
|
d.ConnMaxLiveTime = defaultDatabaseConnMaxLiveTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.MaxOpenConns == 0 {
|
||||||
|
d.MaxOpenConns = defaultDatabaseMaxOpenConns
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.MaxIdleConns == 0 {
|
||||||
|
d.MaxIdleConns = defaultDatabaseMaxIdleConns
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSerialConfig(s *Serial) error {
|
||||||
|
if s.Device == "" {
|
||||||
|
return SettingsError{"you must specify a serial 'device'"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Baud == 0 {
|
||||||
|
s.Baud = 115200
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Timeout == 0 {
|
||||||
|
s.Timeout = defaultSerialTimeout
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2022 CAcert Inc.
|
Copyright 2022-2023 CAcert Inc.
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -37,7 +37,7 @@ import (
|
||||||
type SignerClientHandler struct {
|
type SignerClientHandler struct {
|
||||||
logger *logrus.Logger
|
logger *logrus.Logger
|
||||||
config *config.ClientConfig
|
config *config.ClientConfig
|
||||||
clientCallback chan<- interface{}
|
clientCallback chan<- any
|
||||||
}
|
}
|
||||||
|
|
||||||
var errInputClosed = errors.New("input channel has been closed")
|
var errInputClosed = errors.New("input channel has been closed")
|
||||||
|
@ -155,16 +155,13 @@ func (s *SignerClientHandler) HandleResponse(ctx context.Context, response *prot
|
||||||
s.logger.WithField("response", response).Debug("full response")
|
s.logger.WithField("response", response).Debug("full response")
|
||||||
|
|
||||||
switch r := response.Response.(type) {
|
switch r := response.Response.(type) {
|
||||||
case *messages.ErrorResponse:
|
|
||||||
s.logger.WithField("message", r.Message).Error("error from signer")
|
|
||||||
case *messages.HealthResponse:
|
case *messages.HealthResponse:
|
||||||
s.handleHealthResponse(ctx, r)
|
s.handleHealthResponse(ctx, r)
|
||||||
case *messages.CAInfoResponse:
|
|
||||||
s.handleCAInfoResponse(ctx, r)
|
|
||||||
case *messages.FetchCRLResponse:
|
|
||||||
s.handleFetchCRLResponse(ctx, r)
|
|
||||||
default:
|
default:
|
||||||
s.logger.WithField("response", response).Warnf("unhandled response of type %T", response.Response)
|
s.logger.WithField("response", response).Tracef(
|
||||||
|
"delegate response handling of type %T", response.Response,
|
||||||
|
)
|
||||||
|
s.handleGenericResponse(ctx, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -215,20 +212,11 @@ func (s *SignerClientHandler) handleHealthResponse(ctx context.Context, r *messa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SignerClientHandler) handleCAInfoResponse(ctx context.Context, r *messages.CAInfoResponse) {
|
func (s *SignerClientHandler) handleGenericResponse(ctx context.Context, response *protocol.Response) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case s.clientCallback <- r:
|
case s.clientCallback <- response:
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SignerClientHandler) handleFetchCRLResponse(ctx context.Context, r *messages.FetchCRLResponse) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case s.clientCallback <- r:
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +224,7 @@ func (s *SignerClientHandler) handleFetchCRLResponse(ctx context.Context, r *mes
|
||||||
func New(
|
func New(
|
||||||
config *config.ClientConfig,
|
config *config.ClientConfig,
|
||||||
logger *logrus.Logger,
|
logger *logrus.Logger,
|
||||||
clientCallback chan interface{},
|
clientCallback chan any,
|
||||||
) (protocol.ClientHandler, error) {
|
) (protocol.ClientHandler, error) {
|
||||||
return &SignerClientHandler{
|
return &SignerClientHandler{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
1014
internal/legacydb/legacydb.go
Normal file
1014
internal/legacydb/legacydb.go
Normal file
File diff suppressed because it is too large
Load diff
186
internal/legacydb/legacydb_test.go
Normal file
186
internal/legacydb/legacydb_test.go
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
Copyright CAcert Inc.
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
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 legacydb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_extractSubjectParts(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
subject string
|
||||||
|
want *x509.Certificate
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"personal user subject",
|
||||||
|
"/CN=John Doe/emailAddress=john.doe@example.org",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "John Doe"},
|
||||||
|
EmailAddresses: []string{"john.doe@example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subject with supported and unsupported alt names",
|
||||||
|
"/CN=a.example.com/subjectAltName=DNS:a.example.com/" +
|
||||||
|
"subjectAltName=otherName:1.3.6.1.5.5.7.8.5;UTF8:a.example.com",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "a.example.com"},
|
||||||
|
DNSNames: []string{"a.example.com"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subject with ISO-8859-1 special characters",
|
||||||
|
"/CN=D\xf6ner Kebap/emailAddress=doener@example.org",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Döner Kebap"},
|
||||||
|
EmailAddresses: []string{"doener@example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subject with Windows1252 special characters",
|
||||||
|
"/CN=J\xe1no\x9a Test\x9c/emailAddress=janos.testoe@example.org",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Jánoš Testœ"},
|
||||||
|
EmailAddresses: []string{"janos.testoe@example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"WoT User subject",
|
||||||
|
"/CN=CAcert WoT User/emailAddress=test@example.org",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "CAcert WoT User"},
|
||||||
|
EmailAddresses: []string{"test@example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Keep address order",
|
||||||
|
"/CN=CAcert WoT User/emailAddress=wot.user@example.com/emailAddress=wu@example.com",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "CAcert WoT User"},
|
||||||
|
EmailAddresses: []string{"wot.user@example.com", "wu@example.com"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Keep DNS name order",
|
||||||
|
"/CN=Test User/subjectAltName=DNS:www.example.com/subjectAltName=DNS:example.com",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "Test User"},
|
||||||
|
DNSNames: []string{"www.example.com", "example.com"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Organization user without OU",
|
||||||
|
"/CN=Test User/emailAddress=test@example.org/organizationName=Acme Inc./" +
|
||||||
|
"localityName=Example town/stateOrProvinceName=BW/countryName=DE",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "Test User",
|
||||||
|
Organization: []string{"Acme Inc."},
|
||||||
|
Locality: []string{"Example town"},
|
||||||
|
Province: []string{"BW"},
|
||||||
|
Country: []string{"DE"},
|
||||||
|
},
|
||||||
|
EmailAddresses: []string{"test@example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Organization user with OU",
|
||||||
|
"/CN=Test User/emailAddress=test@example.org/organizationalUnitName=IT/" +
|
||||||
|
"organizationName=Acme Inc./localityName=Example town/countryName=DE",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "Test User",
|
||||||
|
Organization: []string{"Acme Inc."},
|
||||||
|
OrganizationalUnit: []string{"IT"},
|
||||||
|
Locality: []string{"Example town"},
|
||||||
|
Country: []string{"DE"},
|
||||||
|
},
|
||||||
|
EmailAddresses: []string{"test@example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Organization domain without OU",
|
||||||
|
"/organizationName=Acme Inc./localityName=Example Town/stateOrProvinceName=BW/countryName=DE/" +
|
||||||
|
"commonName=www.example.org",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "www.example.org",
|
||||||
|
Organization: []string{"Acme Inc."},
|
||||||
|
Locality: []string{"Example Town"},
|
||||||
|
Province: []string{"BW"},
|
||||||
|
Country: []string{"DE"},
|
||||||
|
},
|
||||||
|
DNSNames: []string{"www.example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Organization domain with OU",
|
||||||
|
"/organizationalUnitName=IT/organizationName=Acme Inc./localityName=Example Town/" +
|
||||||
|
"stateOrProvinceName=BW/countryName=DE/commonName=example.org",
|
||||||
|
&x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "example.org",
|
||||||
|
Organization: []string{"Acme Inc."},
|
||||||
|
OrganizationalUnit: []string{"IT"},
|
||||||
|
Locality: []string{"Example Town"},
|
||||||
|
Province: []string{"BW"},
|
||||||
|
Country: []string{"DE"},
|
||||||
|
},
|
||||||
|
DNSNames: []string{"example.org"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Empty subject",
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"No = in part",
|
||||||
|
"/CNexample",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := extractSubjectParts(tt.subject)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("extractSubjectParts() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, got, "extractSubjectParts() got = %v, want %v", got, tt.want)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
12
templates/mail/en/gpg_body.txt
Normal file
12
templates/mail/en/gpg_body.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Your CAcert signed key for {{ .Email }} is available online at:
|
||||||
|
|
||||||
|
https://www.cacert.org/gpg.php?id=3&cert={{ .RowID }}
|
||||||
|
|
||||||
|
To help improve the trust of CAcert in general, it's appreciated if you could also sign our key and upload it to a key
|
||||||
|
server. Below is a copy of our primary key details:
|
||||||
|
|
||||||
|
pub 1024D/65D0FD58 2003-07-11 CA Cert Signing Authority (Root CA) <gpg@cacert.org>
|
||||||
|
Key fingerprint = A31D 4F81 EF4E BD07 B456 FA04 D2BB 0D01 65D0 FD58
|
||||||
|
|
||||||
|
Best regards
|
||||||
|
CAcert.org Support!
|
1
templates/mail/en/gpg_subject.txt
Normal file
1
templates/mail/en/gpg_subject.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[CAcert.org] Your GPG/PGP Key
|
Loading…
Reference in a new issue