2020-09-16 14:19:51 +00:00
|
|
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2020-07-23 15:05:21 +00:00
|
|
|
|
2020-08-14 14:11:14 +00:00
|
|
|
package credentialrequest
|
2020-07-23 15:05:21 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-07-27 13:08:39 +00:00
|
|
|
"crypto/x509/pkix"
|
2020-07-23 15:05:21 +00:00
|
|
|
"errors"
|
2020-07-24 16:52:38 +00:00
|
|
|
"fmt"
|
2020-07-23 15:05:21 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2020-07-27 13:08:39 +00:00
|
|
|
"github.com/golang/mock/gomock"
|
2020-08-06 22:14:30 +00:00
|
|
|
"github.com/sclevine/spec"
|
2020-07-23 15:05:21 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2020-07-23 16:50:23 +00:00
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
2020-07-23 15:05:21 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
2020-07-24 15:21:36 +00:00
|
|
|
"k8s.io/apiserver/pkg/authentication/user"
|
2020-07-23 15:05:21 +00:00
|
|
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
|
|
|
"k8s.io/apiserver/pkg/registry/rest"
|
2020-08-06 22:14:30 +00:00
|
|
|
"k8s.io/klog/v2"
|
2020-07-23 15:05:21 +00:00
|
|
|
|
2020-09-18 19:56:24 +00:00
|
|
|
loginapi "go.pinniped.dev/generated/1.19/apis/login"
|
2020-09-21 16:37:54 +00:00
|
|
|
"go.pinniped.dev/internal/mocks/credentialrequestmocks"
|
2020-09-18 19:56:24 +00:00
|
|
|
"go.pinniped.dev/internal/testutil"
|
2020-07-23 15:05:21 +00:00
|
|
|
)
|
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
func TestNew(t *testing.T) {
|
|
|
|
r := NewREST(nil, nil)
|
|
|
|
require.NotNil(t, r)
|
|
|
|
require.True(t, r.NamespaceScoped())
|
|
|
|
require.IsType(t, &loginapi.TokenCredentialRequest{}, r.New())
|
2020-07-23 15:05:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 22:14:30 +00:00
|
|
|
func TestCreate(t *testing.T) {
|
|
|
|
spec.Run(t, "create", func(t *testing.T, when spec.G, it spec.S) {
|
|
|
|
var r *require.Assertions
|
|
|
|
var ctrl *gomock.Controller
|
|
|
|
var logger *testutil.TranscriptLogger
|
|
|
|
|
|
|
|
it.Before(func() {
|
|
|
|
r = require.New(t)
|
|
|
|
ctrl = gomock.NewController(t)
|
|
|
|
logger = testutil.NewTranscriptLogger(t)
|
|
|
|
klog.SetLogger(logger) // this is unfortunately a global logger, so can't run these tests in parallel :(
|
|
|
|
})
|
|
|
|
|
|
|
|
it.After(func() {
|
|
|
|
klog.SetLogger(nil)
|
|
|
|
ctrl.Finish()
|
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateSucceedsWhenGivenATokenAndTheWebhookAuthenticatesTheToken", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
req := validCredentialRequest()
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
|
|
|
|
Return(&user.DefaultInfo{
|
|
|
|
Name: "test-user",
|
|
|
|
UID: "test-user-uid",
|
|
|
|
Groups: []string{"test-group-1", "test-group-2"},
|
|
|
|
}, nil)
|
2020-09-16 19:57:18 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
issuer := credentialrequestmocks.NewMockCertIssuer(ctrl)
|
2020-09-16 19:57:18 +00:00
|
|
|
issuer.EXPECT().IssuePEM(
|
|
|
|
pkix.Name{
|
|
|
|
CommonName: "test-user",
|
|
|
|
Organization: []string{"test-group-1", "test-group-2"}},
|
|
|
|
[]string{},
|
|
|
|
1*time.Hour,
|
|
|
|
).Return([]byte("test-cert"), []byte("test-key"), nil)
|
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(requestAuthenticator, issuer)
|
2020-09-16 19:57:18 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := callCreate(context.Background(), storage, req)
|
2020-09-16 19:57:18 +00:00
|
|
|
|
|
|
|
r.NoError(err)
|
|
|
|
r.IsType(&loginapi.TokenCredentialRequest{}, response)
|
|
|
|
|
|
|
|
expires := response.(*loginapi.TokenCredentialRequest).Status.Credential.ExpirationTimestamp
|
|
|
|
r.NotNil(expires)
|
|
|
|
r.InDelta(time.Now().Add(1*time.Hour).Unix(), expires.Unix(), 5)
|
|
|
|
response.(*loginapi.TokenCredentialRequest).Status.Credential.ExpirationTimestamp = metav1.Time{}
|
|
|
|
|
|
|
|
r.Equal(response, &loginapi.TokenCredentialRequest{
|
|
|
|
Status: loginapi.TokenCredentialRequestStatus{
|
|
|
|
Credential: &loginapi.ClusterCredential{
|
|
|
|
ExpirationTimestamp: metav1.Time{},
|
|
|
|
ClientCertificateData: "test-cert",
|
|
|
|
ClientKeyData: "test-key",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2020-09-21 16:37:54 +00:00
|
|
|
requireOneLogStatement(r, logger, `"success" userID:test-user-uid,authenticated:true`)
|
2020-09-16 19:57:18 +00:00
|
|
|
})
|
|
|
|
|
2020-08-06 22:14:30 +00:00
|
|
|
it("CreateFailsWithValidTokenWhenCertIssuerFails", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
req := validCredentialRequest()
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
|
|
|
|
Return(&user.DefaultInfo{
|
|
|
|
Name: "test-user",
|
|
|
|
Groups: []string{"test-group-1", "test-group-2"},
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
issuer := credentialrequestmocks.NewMockCertIssuer(ctrl)
|
2020-08-06 22:14:30 +00:00
|
|
|
issuer.EXPECT().
|
|
|
|
IssuePEM(gomock.Any(), gomock.Any(), gomock.Any()).
|
|
|
|
Return(nil, nil, fmt.Errorf("some certificate authority error"))
|
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(requestAuthenticator, issuer)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := callCreate(context.Background(), storage, req)
|
2020-08-06 22:14:30 +00:00
|
|
|
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
|
|
|
|
requireOneLogStatement(r, logger, `"failure" failureType:cert issuer,msg:some certificate authority error`)
|
|
|
|
})
|
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
it("CreateSucceedsWithAnUnauthenticatedStatusWhenGivenATokenAndTheWebhookReturnsNilUser", func() {
|
|
|
|
req := validCredentialRequest()
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).Return(nil, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(requestAuthenticator, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := callCreate(context.Background(), storage, req)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
|
|
|
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
|
2020-09-21 16:37:54 +00:00
|
|
|
requireOneLogStatement(r, logger, `"success" userID:<none>,authenticated:false`)
|
2020-08-06 22:14:30 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookFails", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
req := validCredentialRequest()
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
|
|
|
|
Return(nil, errors.New("some webhook error"))
|
2020-09-11 20:08:54 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(requestAuthenticator, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := callCreate(context.Background(), storage, req)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
|
|
|
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
|
2020-09-21 16:37:54 +00:00
|
|
|
requireOneLogStatement(r, logger, `"failure" failureType:webhook authentication,msg:some webhook error`)
|
2020-08-06 22:14:30 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateSucceedsWithAnUnauthenticatedStatusWhenWebhookReturnsAnEmptyUsername", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
req := validCredentialRequest()
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req).
|
|
|
|
Return(&user.DefaultInfo{Name: ""}, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(requestAuthenticator, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := callCreate(context.Background(), storage, req)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requireSuccessfulResponseWithAuthenticationFailureMessage(t, err, response)
|
|
|
|
requireOneLogStatement(r, logger, `"success" userID:,authenticated:false`)
|
2020-08-06 22:14:30 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateFailsWhenGivenTheWrongInputType", func() {
|
2020-08-14 14:11:14 +00:00
|
|
|
notACredentialRequest := runtime.Unknown{}
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := NewREST(nil, nil).Create(
|
2020-08-06 22:14:30 +00:00
|
|
|
genericapirequest.NewContext(),
|
2020-08-14 14:11:14 +00:00
|
|
|
¬ACredentialRequest,
|
2020-08-06 22:14:30 +00:00
|
|
|
rest.ValidateAllObjectFunc,
|
|
|
|
&metav1.CreateOptions{})
|
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
requireAPIError(t, response, err, apierrors.IsBadRequest, "not a TokenCredentialRequest")
|
|
|
|
requireOneLogStatement(r, logger, `"failure" failureType:request validation,msg:not a TokenCredentialRequest`)
|
2020-08-06 22:14:30 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateFailsWhenTokenValueIsEmptyInRequest", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(nil, nil)
|
2020-09-18 22:15:04 +00:00
|
|
|
response, err := callCreate(context.Background(), storage, credentialRequest(loginapi.TokenCredentialRequestSpec{
|
|
|
|
Token: "",
|
2020-08-06 22:14:30 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
requireAPIError(t, response, err, apierrors.IsInvalid,
|
2020-08-20 17:54:15 +00:00
|
|
|
`.pinniped.dev "request name" is invalid: spec.token.value: Required value: token must be supplied`)
|
2020-08-06 22:14:30 +00:00
|
|
|
requireOneLogStatement(r, logger, `"failure" failureType:request validation,msg:token must be supplied`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateFailsWhenValidationFails", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(nil, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
response, err := storage.Create(
|
|
|
|
context.Background(),
|
2020-08-14 14:11:14 +00:00
|
|
|
validCredentialRequest(),
|
2020-08-06 22:14:30 +00:00
|
|
|
func(ctx context.Context, obj runtime.Object) error {
|
|
|
|
return fmt.Errorf("some validation error")
|
|
|
|
},
|
|
|
|
&metav1.CreateOptions{})
|
|
|
|
r.Nil(response)
|
|
|
|
r.EqualError(err, "some validation error")
|
|
|
|
requireOneLogStatement(r, logger, `"failure" failureType:validation webhook,msg:some validation error`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateDoesNotAllowValidationFunctionToMutateRequest", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
req := validCredentialRequest()
|
|
|
|
|
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req.DeepCopy()).
|
|
|
|
Return(&user.DefaultInfo{Name: "test-user"}, nil)
|
|
|
|
|
|
|
|
storage := NewREST(requestAuthenticator, successfulIssuer(ctrl))
|
2020-08-06 22:14:30 +00:00
|
|
|
response, err := storage.Create(
|
|
|
|
context.Background(),
|
2020-09-21 16:37:54 +00:00
|
|
|
req,
|
2020-08-06 22:14:30 +00:00
|
|
|
func(ctx context.Context, obj runtime.Object) error {
|
2020-09-18 22:15:04 +00:00
|
|
|
credentialRequest, _ := obj.(*loginapi.TokenCredentialRequest)
|
|
|
|
credentialRequest.Spec.Token = "foobaz"
|
2020-08-06 22:14:30 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
&metav1.CreateOptions{})
|
|
|
|
r.NoError(err)
|
|
|
|
r.NotEmpty(response)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateDoesNotAllowValidationFunctionToSeeTheActualRequestToken", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
req := validCredentialRequest()
|
|
|
|
|
|
|
|
requestAuthenticator := credentialrequestmocks.NewMockTokenCredentialRequestAuthenticator(ctrl)
|
|
|
|
requestAuthenticator.EXPECT().AuthenticateTokenCredentialRequest(gomock.Any(), req.DeepCopy()).
|
|
|
|
Return(&user.DefaultInfo{Name: "test-user"}, nil)
|
2020-08-06 22:14:30 +00:00
|
|
|
|
2020-09-21 16:37:54 +00:00
|
|
|
storage := NewREST(requestAuthenticator, successfulIssuer(ctrl))
|
2020-08-06 22:14:30 +00:00
|
|
|
validationFunctionWasCalled := false
|
|
|
|
var validationFunctionSawTokenValue string
|
|
|
|
response, err := storage.Create(
|
|
|
|
context.Background(),
|
2020-09-21 16:37:54 +00:00
|
|
|
req,
|
2020-08-06 22:14:30 +00:00
|
|
|
func(ctx context.Context, obj runtime.Object) error {
|
2020-09-18 22:15:04 +00:00
|
|
|
credentialRequest, _ := obj.(*loginapi.TokenCredentialRequest)
|
2020-08-06 22:14:30 +00:00
|
|
|
validationFunctionWasCalled = true
|
2020-09-18 22:15:04 +00:00
|
|
|
validationFunctionSawTokenValue = credentialRequest.Spec.Token
|
2020-08-06 22:14:30 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
&metav1.CreateOptions{})
|
|
|
|
r.NoError(err)
|
|
|
|
r.NotEmpty(response)
|
|
|
|
r.True(validationFunctionWasCalled)
|
|
|
|
r.Empty(validationFunctionSawTokenValue)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("CreateFailsWhenRequestOptionsDryRunIsNotEmpty", func() {
|
2020-09-21 16:37:54 +00:00
|
|
|
response, err := NewREST(nil, nil).Create(
|
2020-08-06 22:14:30 +00:00
|
|
|
genericapirequest.NewContext(),
|
2020-08-14 14:11:14 +00:00
|
|
|
validCredentialRequest(),
|
2020-08-06 22:14:30 +00:00
|
|
|
rest.ValidateAllObjectFunc,
|
|
|
|
&metav1.CreateOptions{
|
|
|
|
DryRun: []string{"some dry run flag"},
|
|
|
|
})
|
|
|
|
|
|
|
|
requireAPIError(t, response, err, apierrors.IsInvalid,
|
2020-08-20 17:54:15 +00:00
|
|
|
`.pinniped.dev "request name" is invalid: dryRun: Unsupported value: []string{"some dry run flag"}`)
|
2020-08-06 22:14:30 +00:00
|
|
|
requireOneLogStatement(r, logger, `"failure" failureType:request validation,msg:dryRun not supported`)
|
|
|
|
})
|
|
|
|
}, spec.Sequential())
|
|
|
|
}
|
|
|
|
|
|
|
|
func requireOneLogStatement(r *require.Assertions, logger *testutil.TranscriptLogger, messageContains string) {
|
2020-08-19 18:21:07 +00:00
|
|
|
transcript := logger.Transcript()
|
|
|
|
r.Len(transcript, 1)
|
|
|
|
r.Equal("info", transcript[0].Level)
|
|
|
|
r.Contains(transcript[0].Message, messageContains)
|
2020-08-06 22:14:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-16 19:57:18 +00:00
|
|
|
func callCreate(ctx context.Context, storage *REST, obj runtime.Object) (runtime.Object, error) {
|
2020-07-23 15:05:21 +00:00
|
|
|
return storage.Create(
|
|
|
|
ctx,
|
2020-09-16 19:57:18 +00:00
|
|
|
obj,
|
2020-07-23 15:05:21 +00:00
|
|
|
rest.ValidateAllObjectFunc,
|
|
|
|
&metav1.CreateOptions{
|
|
|
|
DryRun: []string{},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-18 22:15:04 +00:00
|
|
|
func validCredentialRequest() *loginapi.TokenCredentialRequest {
|
2020-08-14 14:11:14 +00:00
|
|
|
return validCredentialRequestWithToken("some token")
|
2020-07-23 23:01:55 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 22:15:04 +00:00
|
|
|
func validCredentialRequestWithToken(token string) *loginapi.TokenCredentialRequest {
|
|
|
|
return credentialRequest(loginapi.TokenCredentialRequestSpec{Token: token})
|
2020-07-23 15:05:21 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 22:15:04 +00:00
|
|
|
func credentialRequest(spec loginapi.TokenCredentialRequestSpec) *loginapi.TokenCredentialRequest {
|
|
|
|
return &loginapi.TokenCredentialRequest{
|
2020-07-23 15:05:21 +00:00
|
|
|
TypeMeta: metav1.TypeMeta{},
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "request name",
|
|
|
|
},
|
|
|
|
Spec: spec,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 16:50:23 +00:00
|
|
|
func requireAPIError(t *testing.T, response runtime.Object, err error, expectedErrorTypeChecker func(err error) bool, expectedErrorMessage string) {
|
|
|
|
t.Helper()
|
|
|
|
require.Nil(t, response)
|
|
|
|
require.True(t, expectedErrorTypeChecker(err))
|
|
|
|
var status apierrors.APIStatus
|
|
|
|
errors.As(err, &status)
|
|
|
|
require.Contains(t, status.Status().Message, expectedErrorMessage)
|
|
|
|
}
|
|
|
|
|
2020-07-24 18:00:29 +00:00
|
|
|
func requireSuccessfulResponseWithAuthenticationFailureMessage(t *testing.T, err error, response runtime.Object) {
|
2020-08-06 22:14:30 +00:00
|
|
|
t.Helper()
|
2020-07-24 18:00:29 +00:00
|
|
|
require.NoError(t, err)
|
2020-09-18 22:15:04 +00:00
|
|
|
require.Equal(t, response, &loginapi.TokenCredentialRequest{
|
|
|
|
Status: loginapi.TokenCredentialRequestStatus{
|
2020-07-24 18:00:29 +00:00
|
|
|
Credential: nil,
|
2020-08-14 13:18:31 +00:00
|
|
|
Message: stringPtr("authentication failed"),
|
2020-07-24 18:00:29 +00:00
|
|
|
},
|
|
|
|
})
|
2020-07-24 15:21:36 +00:00
|
|
|
}
|
|
|
|
|
2020-07-27 13:08:39 +00:00
|
|
|
func successfulIssuer(ctrl *gomock.Controller) CertIssuer {
|
2020-09-21 16:37:54 +00:00
|
|
|
issuer := credentialrequestmocks.NewMockCertIssuer(ctrl)
|
2020-07-27 13:08:39 +00:00
|
|
|
issuer.EXPECT().
|
|
|
|
IssuePEM(gomock.Any(), gomock.Any(), gomock.Any()).
|
|
|
|
Return([]byte("test-cert"), []byte("test-key"), nil)
|
|
|
|
return issuer
|
|
|
|
}
|
2020-08-14 13:18:31 +00:00
|
|
|
|
|
|
|
func stringPtr(s string) *string {
|
|
|
|
return &s
|
|
|
|
}
|