forked from TrueCloudLab/certificates
Merge branch 'master' into cloud-identities
This commit is contained in:
commit
54570095d4
14 changed files with 318 additions and 87 deletions
4
Gopkg.lock
generated
4
Gopkg.lock
generated
|
@ -363,7 +363,7 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:f1f1df1e19d55a1ef1f0a63633191e7d2a99993c0a17f945c0b9ebd16b17871b"
|
digest = "1:5e778214d472b6d2ad4d544d293d1478d9b222db8ffc6079623fbe3e58e1841e"
|
||||||
name = "github.com/smallstep/nosql"
|
name = "github.com/smallstep/nosql"
|
||||||
packages = [
|
packages = [
|
||||||
".",
|
".",
|
||||||
|
@ -373,7 +373,7 @@
|
||||||
"mysql",
|
"mysql",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "5a355c598075a346d9ca9b50ec10e3f86ac66148"
|
revision = "b66b34823456721912ba037126e92414690c07d6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
|
@ -22,7 +22,6 @@ type Authority struct {
|
||||||
intermediateIdentity *x509util.Identity
|
intermediateIdentity *x509util.Identity
|
||||||
validateOnce bool
|
validateOnce bool
|
||||||
certificates *sync.Map
|
certificates *sync.Map
|
||||||
ottMap *sync.Map
|
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
provisioners *provisioner.Collection
|
provisioners *provisioner.Collection
|
||||||
db db.AuthDB
|
db db.AuthDB
|
||||||
|
@ -40,7 +39,6 @@ func New(config *Config) (*Authority, error) {
|
||||||
var a = &Authority{
|
var a = &Authority{
|
||||||
config: config,
|
config: config,
|
||||||
certificates: new(sync.Map),
|
certificates: new(sync.Map),
|
||||||
ottMap: new(sync.Map),
|
|
||||||
provisioners: provisioner.NewCollection(config.getAudiences()),
|
provisioners: provisioner.NewCollection(config.getAudiences()),
|
||||||
}
|
}
|
||||||
if err := a.init(); err != nil {
|
if err := a.init(); err != nil {
|
||||||
|
@ -58,8 +56,8 @@ func (a *Authority) init() error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Initialize step-ca Database if defined in configuration.
|
// Initialize step-ca Database.
|
||||||
// If a.config.DB is nil then a noopDB will be returned.
|
// If a.config.DB is nil then a simple, barebones in memory DB will be used.
|
||||||
if a.db, err = db.New(a.config.DB); err != nil {
|
if a.db, err = db.New(a.config.DB); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,12 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/cli/jose"
|
"github.com/smallstep/cli/jose"
|
||||||
)
|
)
|
||||||
|
|
||||||
type idUsed struct {
|
|
||||||
UsedAt int64 `json:"ua,omitempty"`
|
|
||||||
Subject string `json:"sub,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Claims extends jose.Claims with step attributes.
|
// Claims extends jose.Claims with step attributes.
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
jose.Claims
|
jose.Claims
|
||||||
|
@ -65,10 +59,12 @@ func (a *Authority) authorizeToken(ott string) (provisioner.Interface, error) {
|
||||||
|
|
||||||
// Store the token to protect against reuse.
|
// Store the token to protect against reuse.
|
||||||
if reuseKey, err := p.GetTokenID(ott); err == nil {
|
if reuseKey, err := p.GetTokenID(ott); err == nil {
|
||||||
if _, ok := a.ottMap.LoadOrStore(reuseKey, &idUsed{
|
ok, err := a.db.UseToken(reuseKey, ott)
|
||||||
UsedAt: time.Now().Unix(),
|
if err != nil {
|
||||||
Subject: claims.Subject,
|
return nil, &apiError{errors.Wrap(err, "authorizeToken: failed when checking if token already used"),
|
||||||
}); ok {
|
http.StatusInternalServerError, errContext}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
return nil, &apiError{errors.Errorf("authorizeToken: token already used"), http.StatusUnauthorized, errContext}
|
return nil, &apiError{errors.Errorf("authorizeToken: token already used"), http.StatusUnauthorized, errContext}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ func TestAuthority_authorizeToken(t *testing.T) {
|
||||||
http.StatusUnauthorized, context{"ott": raw}},
|
http.StatusUnauthorized, context{"ott": raw}},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok": func(t *testing.T) *authorizeTest {
|
"ok/simpledb": func(t *testing.T) *authorizeTest {
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "test.smallstep.com",
|
Subject: "test.smallstep.com",
|
||||||
Issuer: validIssuer,
|
Issuer: validIssuer,
|
||||||
|
@ -133,9 +133,8 @@ func TestAuthority_authorizeToken(t *testing.T) {
|
||||||
ott: raw,
|
ott: raw,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fail/token-already-used": func(t *testing.T) *authorizeTest {
|
"fail/simpledb/token-already-used": func(t *testing.T) *authorizeTest {
|
||||||
_a := testAuthority(t)
|
_a := testAuthority(t)
|
||||||
|
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "test.smallstep.com",
|
Subject: "test.smallstep.com",
|
||||||
Issuer: validIssuer,
|
Issuer: validIssuer,
|
||||||
|
@ -155,6 +154,79 @@ func TestAuthority_authorizeToken(t *testing.T) {
|
||||||
http.StatusUnauthorized, context{"ott": raw}},
|
http.StatusUnauthorized, context{"ott": raw}},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ok/mockNoSQLDB": func(t *testing.T) *authorizeTest {
|
||||||
|
_a := testAuthority(t)
|
||||||
|
_a.db = &MockAuthDB{
|
||||||
|
useToken: func(id, tok string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cl := jwt.Claims{
|
||||||
|
Subject: "test.smallstep.com",
|
||||||
|
Issuer: validIssuer,
|
||||||
|
NotBefore: jwt.NewNumericDate(now),
|
||||||
|
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
|
||||||
|
Audience: validAudience,
|
||||||
|
ID: "43",
|
||||||
|
}
|
||||||
|
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
return &authorizeTest{
|
||||||
|
auth: _a,
|
||||||
|
ott: raw,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/mockNoSQLDB/error": func(t *testing.T) *authorizeTest {
|
||||||
|
_a := testAuthority(t)
|
||||||
|
_a.db = &MockAuthDB{
|
||||||
|
useToken: func(id, tok string) (bool, error) {
|
||||||
|
return false, errors.New("force")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cl := jwt.Claims{
|
||||||
|
Subject: "test.smallstep.com",
|
||||||
|
Issuer: validIssuer,
|
||||||
|
NotBefore: jwt.NewNumericDate(now),
|
||||||
|
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
|
||||||
|
Audience: validAudience,
|
||||||
|
ID: "43",
|
||||||
|
}
|
||||||
|
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
return &authorizeTest{
|
||||||
|
auth: _a,
|
||||||
|
ott: raw,
|
||||||
|
err: &apiError{errors.New("authorizeToken: failed when checking if token already used: force"),
|
||||||
|
http.StatusInternalServerError, context{"ott": raw}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/mockNoSQLDB/token-already-used": func(t *testing.T) *authorizeTest {
|
||||||
|
_a := testAuthority(t)
|
||||||
|
_a.db = &MockAuthDB{
|
||||||
|
useToken: func(id, tok string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cl := jwt.Claims{
|
||||||
|
Subject: "test.smallstep.com",
|
||||||
|
Issuer: validIssuer,
|
||||||
|
NotBefore: jwt.NewNumericDate(now),
|
||||||
|
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
|
||||||
|
Audience: validAudience,
|
||||||
|
ID: "43",
|
||||||
|
}
|
||||||
|
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
return &authorizeTest{
|
||||||
|
auth: _a,
|
||||||
|
ott: raw,
|
||||||
|
err: &apiError{errors.New("authorizeToken: token already used"),
|
||||||
|
http.StatusUnauthorized, context{"ott": raw}},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, genTestCase := range tests {
|
for name, genTestCase := range tests {
|
||||||
|
|
|
@ -13,6 +13,7 @@ type MockAuthDB struct {
|
||||||
isRevoked func(string) (bool, error)
|
isRevoked func(string) (bool, error)
|
||||||
revoke func(rci *db.RevokedCertificateInfo) error
|
revoke func(rci *db.RevokedCertificateInfo) error
|
||||||
storeCertificate func(crt *x509.Certificate) error
|
storeCertificate func(crt *x509.Certificate) error
|
||||||
|
useToken func(id, tok string) (bool, error)
|
||||||
shutdown func() error
|
shutdown func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +34,16 @@ func (m *MockAuthDB) IsRevoked(sn string) (bool, error) {
|
||||||
return m.ret1.(bool), m.err
|
return m.ret1.(bool), m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockAuthDB) UseToken(id, tok string) (bool, error) {
|
||||||
|
if m.useToken != nil {
|
||||||
|
return m.useToken(id, tok)
|
||||||
|
}
|
||||||
|
if m.ret1 == nil {
|
||||||
|
return false, m.err
|
||||||
|
}
|
||||||
|
return m.ret1.(bool), m.err
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MockAuthDB) Revoke(rci *db.RevokedCertificateInfo) error {
|
func (m *MockAuthDB) Revoke(rci *db.RevokedCertificateInfo) error {
|
||||||
if m.revoke != nil {
|
if m.revoke != nil {
|
||||||
return m.revoke(rci)
|
return m.revoke(rci)
|
||||||
|
|
|
@ -592,7 +592,6 @@ func TestRevoke(t *testing.T) {
|
||||||
tests := map[string]func() test{
|
tests := map[string]func() test{
|
||||||
"error/token/authorizeRevoke error": func() test {
|
"error/token/authorizeRevoke error": func() test {
|
||||||
a := testAuthority(t)
|
a := testAuthority(t)
|
||||||
a.db = new(db.NoopDB)
|
|
||||||
ctx := getCtx()
|
ctx := getCtx()
|
||||||
ctx["ott"] = "foo"
|
ctx["ott"] = "foo"
|
||||||
return test{
|
return test{
|
||||||
|
@ -609,8 +608,6 @@ func TestRevoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"error/nil-db": func() test {
|
"error/nil-db": func() test {
|
||||||
a := testAuthority(t)
|
a := testAuthority(t)
|
||||||
a.db = new(db.NoopDB)
|
|
||||||
|
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "sn",
|
Subject: "sn",
|
||||||
Issuer: validIssuer,
|
Issuer: validIssuer,
|
||||||
|
@ -640,7 +637,12 @@ func TestRevoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"error/db-revoke": func() test {
|
"error/db-revoke": func() test {
|
||||||
a := testAuthority(t)
|
a := testAuthority(t)
|
||||||
a.db = &MockAuthDB{err: errors.New("force")}
|
a.db = &MockAuthDB{
|
||||||
|
useToken: func(id, tok string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
err: errors.New("force"),
|
||||||
|
}
|
||||||
|
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "sn",
|
Subject: "sn",
|
||||||
|
@ -671,7 +673,12 @@ func TestRevoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"error/already-revoked": func() test {
|
"error/already-revoked": func() test {
|
||||||
a := testAuthority(t)
|
a := testAuthority(t)
|
||||||
a.db = &MockAuthDB{err: db.ErrAlreadyExists}
|
a.db = &MockAuthDB{
|
||||||
|
useToken: func(id, tok string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
err: db.ErrAlreadyExists,
|
||||||
|
}
|
||||||
|
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "sn",
|
Subject: "sn",
|
||||||
|
@ -702,7 +709,11 @@ func TestRevoke(t *testing.T) {
|
||||||
},
|
},
|
||||||
"ok/token": func() test {
|
"ok/token": func() test {
|
||||||
a := testAuthority(t)
|
a := testAuthority(t)
|
||||||
a.db = &MockAuthDB{}
|
a.db = &MockAuthDB{
|
||||||
|
useToken: func(id, tok string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
cl := jwt.Claims{
|
cl := jwt.Claims{
|
||||||
Subject: "sn",
|
Subject: "sn",
|
||||||
|
|
9
ca/ca.go
9
ca/ca.go
|
@ -189,6 +189,15 @@ func (ca *CA) Reload() error {
|
||||||
logContinue("Reload failed because server could not be replaced.")
|
logContinue("Reload failed because server could not be replaced.")
|
||||||
return errors.Wrap(err, "error reloading server")
|
return errors.Wrap(err, "error reloading server")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Stop previous renewer
|
||||||
|
// 2. Replace ca properties
|
||||||
|
// Do not replace ca.srv
|
||||||
|
ca.renewer.Stop()
|
||||||
|
ca.auth = newCA.auth
|
||||||
|
ca.config = newCA.config
|
||||||
|
ca.opts = newCA.opts
|
||||||
|
ca.renewer = newCA.renewer
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
db/db.go
23
db/db.go
|
@ -10,8 +10,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
revokedCertsTable = []byte("revoked_x509_certs")
|
|
||||||
certsTable = []byte("x509_certs")
|
certsTable = []byte("x509_certs")
|
||||||
|
revokedCertsTable = []byte("revoked_x509_certs")
|
||||||
|
usedOTTTable = []byte("used_ott")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrAlreadyExists can be returned if the DB attempts to set a key that has
|
// ErrAlreadyExists can be returned if the DB attempts to set a key that has
|
||||||
|
@ -31,6 +32,7 @@ type AuthDB interface {
|
||||||
IsRevoked(sn string) (bool, error)
|
IsRevoked(sn string) (bool, error)
|
||||||
Revoke(rci *RevokedCertificateInfo) error
|
Revoke(rci *RevokedCertificateInfo) error
|
||||||
StoreCertificate(crt *x509.Certificate) error
|
StoreCertificate(crt *x509.Certificate) error
|
||||||
|
UseToken(id, tok string) (bool, error)
|
||||||
Shutdown() error
|
Shutdown() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +45,7 @@ type DB struct {
|
||||||
// New returns a new database client that implements the AuthDB interface.
|
// New returns a new database client that implements the AuthDB interface.
|
||||||
func New(c *Config) (AuthDB, error) {
|
func New(c *Config) (AuthDB, error) {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return new(NoopDB), nil
|
return newSimpleDB(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := nosql.New(c.Type, c.DataSource, nosql.WithDatabase(c.Database),
|
db, err := nosql.New(c.Type, c.DataSource, nosql.WithDatabase(c.Database),
|
||||||
|
@ -126,6 +128,23 @@ func (db *DB) StoreCertificate(crt *x509.Certificate) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseToken returns true if we were able to successfully store the token for
|
||||||
|
// for the first time, false otherwise.
|
||||||
|
func (db *DB) UseToken(id, tok string) (bool, error) {
|
||||||
|
// If the error is `Not Found` then the certificate has not been revoked.
|
||||||
|
// Any other error should be propagated to the caller.
|
||||||
|
_, found, err := db.LoadOrStore(usedOTTTable, []byte(id), []byte(tok))
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return false, errors.Wrapf(err, "error LoadOrStore-ing token %s/%s",
|
||||||
|
string(usedOTTTable), id)
|
||||||
|
case found:
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Shutdown sends a shutdown message to the database.
|
// Shutdown sends a shutdown message to the database.
|
||||||
func (db *DB) Shutdown() error {
|
func (db *DB) Shutdown() error {
|
||||||
if db.isUp {
|
if db.isUp {
|
||||||
|
|
|
@ -20,6 +20,17 @@ type MockNoSQLDB struct {
|
||||||
del func(bucket, key []byte) error
|
del func(bucket, key []byte) error
|
||||||
list func(bucket []byte) ([]*database.Entry, error)
|
list func(bucket []byte) ([]*database.Entry, error)
|
||||||
update func(tx *database.Tx) error
|
update func(tx *database.Tx) error
|
||||||
|
loadOrStore func(bucket, key, value []byte) ([]byte, bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockNoSQLDB) LoadOrStore(bucket, key, value []byte) ([]byte, bool, error) {
|
||||||
|
if m.get != nil {
|
||||||
|
return m.loadOrStore(bucket, key, value)
|
||||||
|
}
|
||||||
|
if m.ret1 == nil {
|
||||||
|
return nil, false, m.err
|
||||||
|
}
|
||||||
|
return m.ret1.([]byte), m.ret2.(bool), m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockNoSQLDB) Get(bucket, key []byte) ([]byte, error) {
|
func (m *MockNoSQLDB) Get(bucket, key []byte) ([]byte, error) {
|
||||||
|
@ -188,3 +199,66 @@ func TestRevoke(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUseToken(t *testing.T) {
|
||||||
|
type result struct {
|
||||||
|
err error
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
tests := map[string]struct {
|
||||||
|
id, tok string
|
||||||
|
db *DB
|
||||||
|
want result
|
||||||
|
}{
|
||||||
|
"fail/force-LoadOrStore-error": {
|
||||||
|
id: "id",
|
||||||
|
tok: "token",
|
||||||
|
db: &DB{&MockNoSQLDB{
|
||||||
|
loadOrStore: func(bucket, key, value []byte) ([]byte, bool, error) {
|
||||||
|
return nil, false, errors.New("force")
|
||||||
|
},
|
||||||
|
}, true},
|
||||||
|
want: result{
|
||||||
|
ok: false,
|
||||||
|
err: errors.New("error LoadOrStore-ing token id/token"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fail/LoadOrStore-found": {
|
||||||
|
id: "id",
|
||||||
|
tok: "token",
|
||||||
|
db: &DB{&MockNoSQLDB{
|
||||||
|
loadOrStore: func(bucket, key, value []byte) ([]byte, bool, error) {
|
||||||
|
return []byte("foo"), true, nil
|
||||||
|
},
|
||||||
|
}, true},
|
||||||
|
want: result{
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ok/LoadOrStore-not-found": {
|
||||||
|
id: "id",
|
||||||
|
tok: "token",
|
||||||
|
db: &DB{&MockNoSQLDB{
|
||||||
|
loadOrStore: func(bucket, key, value []byte) ([]byte, bool, error) {
|
||||||
|
return nil, false, nil
|
||||||
|
},
|
||||||
|
}, true},
|
||||||
|
want: result{
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tc := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
ok, err := tc.db.UseToken(tc.id, tc.tok)
|
||||||
|
if err != nil {
|
||||||
|
if assert.NotNil(t, tc.want.err) {
|
||||||
|
assert.HasPrefix(t, tc.want.err.Error(), err.Error())
|
||||||
|
}
|
||||||
|
assert.False(t, ok)
|
||||||
|
} else {
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
38
db/noop.go
38
db/noop.go
|
@ -1,38 +0,0 @@
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/x509"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrNotImplemented is an error returned when an operation is Not Implemented.
|
|
||||||
var ErrNotImplemented = errors.Errorf("not implemented")
|
|
||||||
|
|
||||||
// NoopDB implements the DB interface with Noops
|
|
||||||
type NoopDB int
|
|
||||||
|
|
||||||
// Init noop
|
|
||||||
func (n *NoopDB) Init(c *Config) (AuthDB, error) {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRevoked noop
|
|
||||||
func (n *NoopDB) IsRevoked(sn string) (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revoke returns a "NotImplemented" error.
|
|
||||||
func (n *NoopDB) Revoke(rci *RevokedCertificateInfo) error {
|
|
||||||
return ErrNotImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
// StoreCertificate returns a "NotImplemented" error.
|
|
||||||
func (n *NoopDB) StoreCertificate(crt *x509.Certificate) error {
|
|
||||||
return ErrNotImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown returns nil
|
|
||||||
func (n *NoopDB) Shutdown() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_noop(t *testing.T) {
|
|
||||||
db := new(NoopDB)
|
|
||||||
|
|
||||||
_db, err := db.Init(&Config{})
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
assert.Equals(t, db, _db)
|
|
||||||
|
|
||||||
isRevoked, err := db.IsRevoked("foo")
|
|
||||||
assert.False(t, isRevoked)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
assert.Equals(t, db.Revoke(&RevokedCertificateInfo{}), ErrNotImplemented)
|
|
||||||
}
|
|
63
db/simple.go
Normal file
63
db/simple.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNotImplemented is an error returned when an operation is Not Implemented.
|
||||||
|
var ErrNotImplemented = errors.Errorf("not implemented")
|
||||||
|
|
||||||
|
// SimpleDB is a barebones implementation of the DB interface. It is NOT an
|
||||||
|
// in memory implementation of the DB, but rather the bare minimum of
|
||||||
|
// functionality that the CA requires to operate securely.
|
||||||
|
type SimpleDB struct {
|
||||||
|
usedTokens *sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSimpleDB(c *Config) (AuthDB, error) {
|
||||||
|
db := &SimpleDB{}
|
||||||
|
db.usedTokens = new(sync.Map)
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRevoked noop
|
||||||
|
func (s *SimpleDB) IsRevoked(sn string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoke returns a "NotImplemented" error.
|
||||||
|
func (s *SimpleDB) Revoke(rci *RevokedCertificateInfo) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCertificate returns a "NotImplemented" error.
|
||||||
|
func (s *SimpleDB) StoreCertificate(crt *x509.Certificate) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
type usedToken struct {
|
||||||
|
UsedAt int64 `json:"ua,omitempty"`
|
||||||
|
Token string `json:"tok,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseToken returns a "NotImplemented" error.
|
||||||
|
func (s *SimpleDB) UseToken(id, tok string) (bool, error) {
|
||||||
|
if _, ok := s.usedTokens.LoadOrStore(id, &usedToken{
|
||||||
|
UsedAt: time.Now().Unix(),
|
||||||
|
Token: tok,
|
||||||
|
}); ok {
|
||||||
|
// Token already exists in DB.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// Successfully stored token.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown returns nil
|
||||||
|
func (s *SimpleDB) Shutdown() error {
|
||||||
|
return nil
|
||||||
|
}
|
37
db/simple_test.go
Normal file
37
db/simple_test.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/smallstep/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimpleDB(t *testing.T) {
|
||||||
|
db, err := newSimpleDB(nil)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
// Revoke
|
||||||
|
assert.Equals(t, ErrNotImplemented, db.Revoke(nil))
|
||||||
|
|
||||||
|
// IsRevoked -- verify noop
|
||||||
|
isRevoked, err := db.IsRevoked("foo")
|
||||||
|
assert.False(t, isRevoked)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// StoreCertificate
|
||||||
|
assert.Equals(t, ErrNotImplemented, db.StoreCertificate(nil))
|
||||||
|
|
||||||
|
// UseToken
|
||||||
|
ok, err := db.UseToken("foo", "bar")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
ok, err = db.UseToken("foo", "cat")
|
||||||
|
assert.False(t, ok)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// Shutdown -- verify noop
|
||||||
|
assert.FatalError(t, db.Shutdown())
|
||||||
|
ok, err = db.UseToken("foo", "cat")
|
||||||
|
assert.False(t, ok)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
FROM smallstep/step-cli:0.9.0
|
FROM smallstep/step-cli:latest
|
||||||
|
|
||||||
ARG BINPATH="bin/step-ca"
|
ARG BINPATH="bin/step-ca"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue