1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-02 22:17:10 +08:00
Mirko Teodorovic 74aa93fbb6
NOISSUE - Certs service refactor (#1369)
* remove owner id

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add certs mock

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* remove not wanted changes

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* refactor certs

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* addint tests

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* addint tests

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* adding tests

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add certs test

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add certs test

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add cert test, remove default implementation

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix default value for vault host

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add cert test, remove default implementation

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* linter cleaning

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix comments, and logging

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* use mocks from other services

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* rename struct and url path params

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* resolve minor comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* resolve comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* align url params naming

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* resolve comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* resolve comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix typo

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* resolve comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* remove struct revoke

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* refactor certRes

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
2021-03-15 12:27:32 +01:00

189 lines
4.5 KiB
Go

// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
// Package pki wraps vault client
package pki
import (
"encoding/json"
"io/ioutil"
"net/http"
"time"
"github.com/hashicorp/vault/api"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mitchellh/mapstructure"
)
const (
issue = "issue"
revoke = "revoke"
apiVer = "v1"
)
var (
// ErrNotImplemented indicate that method called is not implemented
ErrNotImplemented = errors.New("method not implemented for certs")
// ErrMissingCACertificate indicates missing CA certificate
ErrMissingCACertificate = errors.New("missing CA certificate for certificate signing")
// ErrFailedCertCreation indicates failed to certificate creation
ErrFailedCertCreation = errors.New("failed to create client certificate")
// ErrFailedCertRevocation indicates failed certificate revocation
ErrFailedCertRevocation = errors.New("failed to revoke certificate")
errFailedVaultCertIssue = errors.New("failed to issue vault certificate")
errFailedCertDecoding = errors.New("failed to decode response from vault service")
)
type Cert struct {
ClientCert string `json:"client_cert" mapstructure:"certificate"`
IssuingCA string `json:"issuing_ca" mapstructure:"issuing_ca"`
CAChain []string `json:"ca_chain" mapstructure:"ca_chain"`
ClientKey string `json:"client_key" mapstructure:"private_key"`
PrivateKeyType string `json:"private_key_type" mapstructure:"private_key_type"`
Serial string `json:"serial" mapstructure:"serial_number"`
Expire time.Time `json:"expire" mapstructure:"-"`
}
type Agent interface {
// IssueCert issues certificate on PKI
IssueCert(cn string, ttl, keyType string, keyBits int) (Cert, error)
// Revoke revokes certificate from PKI
Revoke(serial string) (time.Time, error)
}
type pkiAgent struct {
token string
path string
role string
host string
issueURL string
revokeURL string
client *api.Client
}
type certReq struct {
CommonName string `json:"common_name"`
TTL string `json:"ttl"`
KeyBits int `json:"key_bits"`
KeyType string `json:"key_type"`
}
type certRevokeReq struct {
SerialNumber string `json:"serial_number"`
}
func NewVaultClient(token, host, path, role string) (Agent, error) {
conf := &api.Config{
Address: host,
}
client, err := api.NewClient(conf)
if err != nil {
return nil, err
}
client.SetToken(token)
p := pkiAgent{
token: token,
host: host,
role: role,
path: path,
client: client,
issueURL: "/" + apiVer + "/" + path + "/" + issue + "/" + role,
revokeURL: "/" + apiVer + "/" + path + "/" + revoke,
}
return &p, nil
}
func (p *pkiAgent) IssueCert(cn string, ttl, keyType string, keyBits int) (Cert, error) {
cReq := certReq{
CommonName: cn,
TTL: ttl,
KeyBits: keyBits,
KeyType: keyType,
}
r := p.client.NewRequest("POST", p.issueURL)
if err := r.SetJSONBody(cReq); err != nil {
return Cert{}, err
}
resp, err := p.client.RawRequest(r)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return Cert{}, err
}
if resp.StatusCode >= http.StatusBadRequest {
_, err := ioutil.ReadAll(resp.Body)
if err != nil {
return Cert{}, err
}
return Cert{}, errors.Wrap(errFailedVaultCertIssue, err)
}
s, _ := api.ParseSecret(resp.Body)
cert := Cert{}
if err = mapstructure.Decode(s.Data, &cert); err != nil {
return Cert{}, errors.Wrap(errFailedCertDecoding, err)
}
// Expire time calc must be revised value doesnt look correct
exp, err := s.Data["expiration"].(json.Number).Float64()
if err != nil {
return cert, err
}
expTime := time.Unix(0, int64(exp)*int64(time.Millisecond))
cert.Expire = expTime
return cert, nil
}
func (p *pkiAgent) Revoke(serial string) (time.Time, error) {
cReq := certRevokeReq{
SerialNumber: serial,
}
r := p.client.NewRequest("POST", p.revokeURL)
if err := r.SetJSONBody(cReq); err != nil {
return time.Time{}, err
}
resp, err := p.client.RawRequest(r)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return time.Time{}, err
}
if resp.StatusCode >= http.StatusBadRequest {
_, err := ioutil.ReadAll(resp.Body)
if err != nil {
return time.Time{}, err
}
return time.Time{}, errors.Wrap(errFailedVaultCertIssue, err)
}
s, err := api.ParseSecret(resp.Body)
if err != nil {
return time.Time{}, err
}
rev, err := s.Data["revocation_time"].(json.Number).Float64()
if err != nil {
return time.Time{}, err
}
return time.Unix(0, int64(rev)*int64(time.Millisecond)), nil
}