Skip to content

Commit

Permalink
fix: fix banking circle connector (#395)
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-nicolas authored Jun 29, 2023
1 parent e295856 commit 9458ebd
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repos:
- id: detect-aws-credentials
args: [--allow-missing-credentials]
- id: detect-private-key
exclude: components/operator/garden|components/auth/cmd/serve.go
exclude: components/operator/garden|components/auth/cmd/serve.go|components/payments/internal/app/connectors/bankingcircle/config.go|docs/docs/payments/connectors/bankingcircle.mdx
- repo: local
hooks:
- id: moon
Expand Down
10 changes: 8 additions & 2 deletions components/payments/docs/connectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Docs: [https://docs.bankingcircleconnect.com/](https://docs.bankingcircleconnect
Password string `json:"password" yaml:"password" bson:"password"`
Endpoint string `json:"endpoint" yaml:"endpoint" bson:"endpoint"`
AuthorizationEndpoint string `json:"authorizationEndpoint" yaml:"authorizationEndpoint" bson:"authorizationEndpoint"`
UserCertificate string `json:"userCertificate" yaml:"userCertificate" bson:"userCertificate"`
UserCertificateKey string `json:"userCertificateKey" yaml:"userCertificateKey" bson:"userCertificateKey"`
```

#### Sandbox defaults
Expand All @@ -26,7 +28,9 @@ Docs: [https://docs.bankingcircleconnect.com/](https://docs.bankingcircleconnect
"username": "username",
"password": "password",
"endpoint": "https://sandbox.bankingcircle.com",
"authorizationEndpoint": "https://authorizationsandbox.bankingcircleconnect.com"
"authorizationEndpoint": "https://authorizationsandbox.bankingcircleconnect.com",
"userCertificate": "userCertificate",
"userCertificateKey": "userCertificateKey"
}
```

Expand All @@ -36,7 +40,9 @@ Docs: [https://docs.bankingcircleconnect.com/](https://docs.bankingcircleconnect
"username": "username",
"password": "password",
"endpoint": "https://www.bankingcircleconnect.com/",
"authorizationEndpoint": "https://authorization.bankingcircleconnect.com"
"authorizationEndpoint": "https://authorization.bankingcircleconnect.com",
"userCertificate": "userCertificate",
"userCertificateKey": "userCertificateKey"
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package bankingcircle

import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"

"github.com/formancehq/stack/libs/go-libs/logging"
Expand All @@ -27,16 +29,35 @@ type client struct {
accessTokenExpiresAt time.Time
}

func newHTTPClient() *http.Client {
func newHTTPClient(userCertificate, userCertificateKey string) (*http.Client, error) {
cert, err := tls.X509KeyPair([]byte(userCertificate), []byte(userCertificateKey))
if err != nil {
return nil, err
}

tr := http.DefaultTransport.(*http.Transport).Clone()
tr.TLSClientConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
}

return &http.Client{
Timeout: 10 * time.Second,
Transport: otelhttp.NewTransport(http.DefaultTransport),
}
Transport: otelhttp.NewTransport(tr),
}, nil
}

func newClient(username, password, endpoint, authorizationEndpoint string, logger logging.Logger) (*client, error) {
func newClient(
username, password,
endpoint, authorizationEndpoint,
uCertificate, uCertificateKey string,
logger logging.Logger) (*client, error) {
httpClient, err := newHTTPClient(uCertificate, uCertificateKey)
if err != nil {
return nil, err
}

c := &client{
httpClient: newHTTPClient(),
httpClient: httpClient,

username: username,
password: password,
Expand Down Expand Up @@ -82,7 +103,7 @@ func (c *client) login(ctx context.Context) error {
//nolint:tagliatelle // allow for client-side structures
type response struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
ExpiresIn string `json:"expires_in"`
}

var res response
Expand All @@ -92,7 +113,13 @@ func (c *client) login(ctx context.Context) error {
}

c.accessToken = res.AccessToken
c.accessTokenExpiresAt = time.Now().Add(time.Duration(res.ExpiresIn) * time.Second)

expiresIn, err := strconv.Atoi(res.ExpiresIn)
if err != nil {
return fmt.Errorf("failed to convert expires_in to int: %w", err)
}

c.accessTokenExpiresAt = time.Now().Add(time.Duration(expiresIn) * time.Second)

return nil
}
Expand Down Expand Up @@ -177,7 +204,7 @@ type payment struct {
func (c *client) getAllPayments(ctx context.Context) ([]*payment, error) {
var payments []*payment

for page := 0; ; page++ {
for page := 1; ; page++ {
pagedPayments, err := c.getPayments(ctx, page)
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@ import (
"github.com/formancehq/payments/internal/app/connectors/configtemplate"
)

// PFX is not handle very well in Go if we have more than one certificate
// in the pfx data.
// To be safe for every user to pass the right data to the connector, let's
// use two config parameters instead of one: the user certificate and the key
// associated.
// To extract them for a pfx file, you can use the following commands:
// openssl pkcs12 -in PC20230412293693.pfx -clcerts -nokeys | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > clientcert.cer
// openssl pkcs12 -in PC20230412293693.pfx -nocerts -nodes | sed -ne '/-BEGIN PRIVATE KEY-/,/-END PRIVATE KEY-/p' > clientcert.key
type Config struct {
Username string `json:"username" yaml:"username" bson:"username"`
Password string `json:"password" yaml:"password" bson:"password"`
Endpoint string `json:"endpoint" yaml:"endpoint" bson:"endpoint"`
AuthorizationEndpoint string `json:"authorizationEndpoint" yaml:"authorizationEndpoint" bson:"authorizationEndpoint"`
UserCertificate string `json:"userCertificate" yaml:"userCertificate" bson:"userCertificate"`
UserCertificateKey string `json:"userCertificateKey" yaml:"userCertificateKey" bson:"userCertificateKey"`
}

// String obfuscates sensitive fields and returns a string representation of the config.
Expand All @@ -37,6 +47,14 @@ func (c Config) Validate() error {
return ErrMissingAuthorizationEndpoint
}

if c.UserCertificate == "" {
return ErrMissingUserCertificate
}

if c.UserCertificateKey == "" {
return ErrMissingUserCertificatePassphrase
}

return nil
}

Expand All @@ -51,6 +69,8 @@ func (c Config) BuildTemplate() (string, configtemplate.Config) {
cfg.AddParameter("password", configtemplate.TypeString, true)
cfg.AddParameter("endpoint", configtemplate.TypeString, true)
cfg.AddParameter("authorizationEndpoint", configtemplate.TypeString, true)
cfg.AddParameter("userCertificate", configtemplate.TypeString, true)
cfg.AddParameter("userCertificateKey", configtemplate.TypeString, true)

return Name.String(), cfg
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ var (

// ErrMissingAuthorizationEndpoint is returned when the authorization endpoint is missing.
ErrMissingAuthorizationEndpoint = errors.New("missing authorization endpoint from config")

// ErrMissingUserCertificate is returned when the user certificate is missing.
ErrMissingUserCertificate = errors.New("missing user certificate from config")

// ErrMissingUserCertificatePassphrase is returned when the user certificate passphrase is missing.
ErrMissingUserCertificatePassphrase = errors.New("missing user certificate passphrase from config")
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ type TaskDescriptor struct {
}

func resolveTasks(logger logging.Logger, config Config) func(taskDefinition models.TaskDescriptor) task.Task {
bankingCircleClient, err := newClient(config.Username, config.Password, config.Endpoint, config.AuthorizationEndpoint, logger)
bankingCircleClient, err := newClient(
config.Username,
config.Password,
config.Endpoint,
config.AuthorizationEndpoint,
config.UserCertificate,
config.UserCertificateKey,
logger,
)
if err != nil {
logger.Error(err)

Expand Down
17 changes: 15 additions & 2 deletions docs/docs/payments/connectors/bankingcircle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import ApiWarning from '../partials/_api_key.mdx';

Before you begin, you need to have a BankingCircle account. If you don't have one, you can [sign up for a BankingCircle account](https://www.bankingcircle.com/contact-us) here. Make sure you have prepared your BankingCircle API credentials, with the least amount of permissions required to access the functionality of the BankingCircle Connector you plan to use. You can find more information about how to create an API key [here](https://docs.bankingcircleconnect.com/).

You will also need a client certificate and the unecrypted key that you can extract from the pfx file of your BankingCircle account:

```shell
openssl pkcs12 -in <filename.pfx> -nocerts -nodes | sed -ne '/-BEGIN PRIVATE KEY-/,/-END PRIVATE KEY-/p' > <clientcert.key>
openssl pkcs12 -in <filename.pfx> -clcerts -nokeys | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > <clientcert.cer>
```

<ApiWarning/>

## Setup
Expand All @@ -20,15 +27,19 @@ read "BC_USERNAME"
read "BC_PASSWORD"
read "BC_ENDPOINT"
read "BC_AUTH_ENDPOINT"
fctl payments connectors install wise $BC_USERNAME $BC_PASSWORD $BC_ENDPOINT $BC_AUTH_ENDPOINT
read "BC_USER_CERTIFICATE"
read "BC_USER_CERTIFICATE_KEY"
fctl payments connectors install bankingcircle $BC_USERNAME $BC_PASSWORD $BC_ENDPOINT $BC_AUTH_ENDPOINT $BC_USER_CERTIFICATE $BC_USER_CERTIFICATE_KEY
```

```shell
STACK="stack_123" \
read "BC_USERNAME" && \
read "BC_PASSWORD" && \
read "BC_ENDPOINT" && \
read "BC_AUTH_ENDPOINT"
read "BC_AUTH_ENDPOINT" && \
read "BC_USER_CERTIFICATE" && \
read "BC_USER_CERTIFICATE_KEY"
echo -X POST "https://$STACK.formance.cloud/api/payments/connectors/bankingcircle" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
Expand All @@ -37,6 +48,8 @@ echo -X POST "https://$STACK.formance.cloud/api/payments/connectors/bankingcircl
\"password\": \"$BC_PASSWORD\", \
\"endpoint\": \"$BC_ENDPOINT\", \
\"authEndpoint\": \"$BC_AUTH_ENDPOINT\" \
\"userCertificate\": \"$BC_USER_CERTIFICATE\" \
\"userCertificateKey\": \"$BC_USER_CERTIFICATE_KEY\" \
}"
```

Expand Down
1 change: 1 addition & 0 deletions tests/integration/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ services:
container_name: temporalite
build:
dockerfile: temporalite.Dockerfile
context: .
ports:
- 7233:7233
- 8233:8233
Expand Down

1 comment on commit 9458ebd

@vercel
Copy link

@vercel vercel bot commented on 9458ebd Jun 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.