Add support for lifetime.

This commit is contained in:
Mariano Cano 2021-03-19 13:19:49 -07:00
parent ae4b8f58b8
commit 7958f6ebb5
3 changed files with 91 additions and 3 deletions

View file

@ -172,8 +172,9 @@ func (s *StepCAS) createCertificate(cr *x509.CertificateRequest, lifetime time.D
} }
resp, err := s.client.Sign(&api.SignRequest{ resp, err := s.client.Sign(&api.SignRequest{
CsrPEM: api.CertificateRequest{CertificateRequest: cr}, CsrPEM: api.CertificateRequest{CertificateRequest: cr},
OTT: token, OTT: token,
NotAfter: s.lifetime(lifetime),
}) })
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -203,3 +204,13 @@ func (s *StepCAS) revokeToken(subject string) (string, error) {
return "", errors.New("stepCAS does not have any provisioner configured") return "", errors.New("stepCAS does not have any provisioner configured")
} }
func (s *StepCAS) lifetime(d time.Duration) api.TimeDuration {
if s.x5c != nil {
d = s.x5c.Lifetime(d)
}
var td api.TimeDuration
td.SetDuration(d)
println(td.String(), d.String())
return td
}

View file

@ -17,6 +17,12 @@ import (
const defaultValidity = 5 * time.Minute const defaultValidity = 5 * time.Minute
// timeNow returns the current time.
// This method is used for unit testing purposes.
var timeNow = func() time.Time {
return time.Now()
}
type x5cIssuer struct { type x5cIssuer struct {
caURL *url.URL caURL *url.URL
certFile string certFile string
@ -58,6 +64,18 @@ func (i *x5cIssuer) RevokeToken(subject string) (string, error) {
return i.createToken(aud, subject, nil) return i.createToken(aud, subject, nil)
} }
func (i *x5cIssuer) Lifetime(d time.Duration) time.Duration {
cert, err := pemutil.ReadCertificate(i.certFile, pemutil.WithFirstBlock())
if err != nil {
return d
}
now := timeNow()
if now.Add(d + time.Minute).After(cert.NotAfter) {
return cert.NotAfter.Sub(now) - time.Minute
}
return d
}
func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error) { func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error) {
signer, err := newX5CSigner(i.certFile, i.keyFile) signer, err := newX5CSigner(i.certFile, i.keyFile)
if err != nil { if err != nil {
@ -86,7 +104,7 @@ func (i *x5cIssuer) createToken(aud, sub string, sans []string) (string, error)
} }
func defaultClaims(iss, sub, aud, id string) jose.Claims { func defaultClaims(iss, sub, aud, id string) jose.Claims {
now := time.Now() now := timeNow()
return jose.Claims{ return jose.Claims{
ID: id, ID: id,
Issuer: iss, Issuer: iss,

View file

@ -11,6 +11,7 @@ import (
"net/url" "net/url"
"reflect" "reflect"
"testing" "testing"
"time"
"go.step.sm/crypto/jose" "go.step.sm/crypto/jose"
) )
@ -25,6 +26,17 @@ func (b noneSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts)
return digest, nil return digest, nil
} }
func fakeTime(t *testing.T) {
t.Helper()
tmp := timeNow
t.Cleanup(func() {
timeNow = tmp
})
timeNow = func() time.Time {
return testX5CCrt.NotBefore
}
}
func Test_x5cIssuer_SignToken(t *testing.T) { func Test_x5cIssuer_SignToken(t *testing.T) {
caURL, err := url.Parse("https://ca.smallstep.com") caURL, err := url.Parse("https://ca.smallstep.com")
if err != nil { if err != nil {
@ -154,6 +166,53 @@ func Test_x5cIssuer_RevokeToken(t *testing.T) {
} }
} }
func Test_x5cIssuer_Lifetime(t *testing.T) {
fakeTime(t)
caURL, err := url.Parse("https://ca.smallstep.com")
if err != nil {
t.Fatal(err)
}
// With a leeway of 1m the max duration will be 59m.
maxDuration := testX5CCrt.NotAfter.Sub(timeNow()) - time.Minute
type fields struct {
caURL *url.URL
certFile string
keyFile string
issuer string
}
type args struct {
d time.Duration
}
tests := []struct {
name string
fields fields
args args
want time.Duration
}{
{"ok 0s", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{0}, 0},
{"ok 1m", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{time.Minute}, time.Minute},
{"ok max-1m", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{maxDuration - time.Minute}, maxDuration - time.Minute},
{"ok max", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{maxDuration}, maxDuration},
{"ok max+1m", fields{caURL, testX5CPath, testX5CKeyPath, "X5C"}, args{maxDuration + time.Minute}, maxDuration},
{"ok fail", fields{caURL, testX5CPath + ".missing", testX5CKeyPath, "X5C"}, args{maxDuration + time.Minute}, maxDuration + time.Minute},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &x5cIssuer{
caURL: tt.fields.caURL,
certFile: tt.fields.certFile,
keyFile: tt.fields.keyFile,
issuer: tt.fields.issuer,
}
if got := i.Lifetime(tt.args.d); got != tt.want {
t.Errorf("x5cIssuer.Lifetime() = %v, want %v", got, tt.want)
}
})
}
}
func Test_newJoseSigner(t *testing.T) { func Test_newJoseSigner(t *testing.T) {
mustSigner := func(args ...interface{}) crypto.Signer { mustSigner := func(args ...interface{}) crypto.Signer {
if err := args[len(args)-1]; err != nil { if err := args[len(args)-1]; err != nil {