Skip to content

Commit

Permalink
moving embed logic to embed function and test
Browse files Browse the repository at this point in the history
Signed-off-by: Javan lacerda <javanlacerda@google.com>
  • Loading branch information
javanlacerda committed Jun 13, 2024
1 parent 7fc1d04 commit 93545f0
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 54 deletions.
3 changes: 0 additions & 3 deletions pkg/challenges/challenges.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/fulcio/pkg/identity/buildkite"
"github.com/sigstore/fulcio/pkg/identity/ciprovider"
"github.com/sigstore/fulcio/pkg/identity/email"
"github.com/sigstore/fulcio/pkg/identity/github"
"github.com/sigstore/fulcio/pkg/identity/gitlabcom"
Expand Down Expand Up @@ -60,8 +59,6 @@ func PrincipalFromIDToken(ctx context.Context, tok *oidc.IDToken) (identity.Prin
var principal identity.Principal
var err error
switch iss.Type {
case config.IssuerTypeCiProvider:
principal, err = ciprovider.WorkflowPrincipalFromIDToken(ctx, tok)
case config.IssuerTypeBuildkiteJob:
principal, err = buildkite.JobPrincipalFromIDToken(ctx, tok)
case config.IssuerTypeGitLabPipeline:
Expand Down
1 change: 0 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ const (
IssuerTypeSpiffe = "spiffe"
IssuerTypeURI = "uri"
IssuerTypeUsername = "username"
IssuerTypeCiProvider = "ci-provider"
)

func parseConfig(b []byte) (cfg *FulcioConfig, err error) {
Expand Down
90 changes: 44 additions & 46 deletions pkg/identity/ciprovider/principal.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"gopkg.in/yaml.v3"
)

type RootYaml struct {
type Config struct {
Providers map[config.IssuerType]Provider
}
type Provider struct {
Expand All @@ -41,23 +41,32 @@ type Provider struct {
Defaults map[string]string
OIDCIssuers []config.OIDCIssuer `yaml:"oidc-issuers,omitempty"`
MetaIssuers []config.OIDCIssuer `yaml:"meta-issuers,omitempty"`
Claims map[string]interface{}
}

func readYaml() RootYaml {
var obj RootYaml
func readConfig() Config {
var obj Config

yamlFile, err := os.ReadFile("config.yaml")
configFile, err := os.ReadFile("config.yaml")
if err != nil {
fmt.Printf("yamlFile.Get err #%v ", err)
}
err = yaml.Unmarshal(yamlFile, &obj)
err = yaml.Unmarshal(configFile, &obj)
if err != nil {
fmt.Printf("Unmarshal: %v", err)
}

return obj
}

func claimsToString(claims map[string]interface{}) map[string]string {
stringClaims := make(map[string]string)
for k, v := range claims {
stringClaims[k] = v.(string)
}
return stringClaims
}

func ApplyTemplate(path string, data map[string]string, defaultData map[string]string) string {

// Here we merge the data from was claimed by the id token with the
Expand Down Expand Up @@ -93,16 +102,40 @@ func WorkflowPrincipalFromIDToken(ctx context.Context, token *oidc.IDToken) (ide
if !ok {
return nil, fmt.Errorf("configuration can not be loaded for issuer %v", token.Issuer)
}
var claims map[string]string
var claims map[string]interface{}
if err := token.Claims(&claims); err != nil {
return nil, err
}
configYaml := readYaml()
configYaml := readConfig()
provider := configYaml.Providers[iss.Type]
e := provider.Extensions
defaults := provider.Defaults
provider.Claims = claims
provider.Subject = token.Subject
return provider, nil
}

func (p Provider) Name(_ context.Context) string {
return p.Subject
}

func (p Provider) Embed(_ context.Context, cert *x509.Certificate) error {

finalExtensions := certificate.Extensions{
e := p.Extensions
defaults := p.Defaults
claims := claimsToString(p.Claims)
uris := make([]*url.URL, len(p.Uris))
for _, value := range p.Uris {
url, err := url.Parse(ApplyTemplate(value, claims, defaults))
if err != nil {
panic(err)
}
uris = append(uris, url)
}
// Set workflow ref URL to SubjectAlternativeName on certificate
cert.URIs = uris

var err error
// Embed additional information into custom extensions
cert.ExtraExtensions, err = certificate.Extensions{
Issuer: ApplyTemplate(e.Issuer, claims, defaults),
GithubWorkflowTrigger: ApplyTemplate(e.GithubWorkflowTrigger, claims, defaults),
GithubWorkflowSHA: ApplyTemplate(e.GithubWorkflowSHA, claims, defaults),
Expand All @@ -123,42 +156,7 @@ func WorkflowPrincipalFromIDToken(ctx context.Context, token *oidc.IDToken) (ide
BuildTrigger: ApplyTemplate(e.BuildTrigger, claims, defaults),
RunInvocationURI: ApplyTemplate(e.RunInvocationURI, claims, defaults),
SourceRepositoryVisibilityAtSigning: ApplyTemplate(e.SourceRepositoryVisibilityAtSigning, claims, defaults),
}
finalUris := make([]string, len(provider.Uris)-1)
for _, val := range provider.Uris {
finalUris = append(finalUris, ApplyTemplate(val, claims, defaults))
}

return &Provider{
Subject: token.Subject,
Extensions: finalExtensions,
Uris: finalUris,
OIDCIssuers: provider.OIDCIssuers,
MetaIssuers: provider.MetaIssuers,
}, nil

}

func (p Provider) Name(_ context.Context) string {
return p.Subject
}

func (p Provider) Embed(_ context.Context, cert *x509.Certificate) error {

uris := make([]*url.URL, len(p.Uris))
for _, value := range p.Uris {
url, err := url.Parse(value)
if err != nil {
panic(err)
}
uris = append(uris, url)
}
// Set workflow ref URL to SubjectAlternativeName on certificate
cert.URIs = uris

var err error
// Embed additional information into custom extensions
cert.ExtraExtensions, err = p.Extensions.Render()
}.Render()
if err != nil {
return err
}
Expand Down
142 changes: 141 additions & 1 deletion pkg/identity/ciprovider/principal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@
package ciprovider

import (
"bytes"
"context"
"crypto/x509"
"encoding/asn1"
"encoding/json"
"errors"
"fmt"
"reflect"
"testing"
"unsafe"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/sigstore/fulcio/pkg/certificate"
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
)

// TO BE IMPLEMENTED. Just kept as a guide
Expand Down Expand Up @@ -109,6 +116,139 @@ func TestName(t *testing.T) {
}
}

func TestEmbed(_ *testing.T) {
func TestEmbed(t *testing.T) {
tests := map[string]struct {
Principal identity.Principal
WantErr bool
WantFacts map[string]func(x509.Certificate) error
}{
`Github workflow challenge should have all Github workflow extensions and issuer set`: {
Principal: &Provider{
Extensions: certificate.Extensions{
Issuer: "issuer",
GithubWorkflowTrigger: "event_name",
GithubWorkflowSHA: "sha",
GithubWorkflowName: "workflow",
GithubWorkflowRepository: "repository",
GithubWorkflowRef: "ref",
BuildSignerURI: "{{ .url }}/{{ .job_workflow_ref }}",
BuildSignerDigest: "job_workflow_sha",
RunnerEnvironment: "runner_environment",
SourceRepositoryURI: "{{ .url }}/{{ .repository }}",
SourceRepositoryDigest: "sha",
SourceRepositoryRef: "ref",
SourceRepositoryIdentifier: "repository_id",
SourceRepositoryOwnerURI: "{{ .url }}/{{ .repository_owner }}",
SourceRepositoryOwnerIdentifier: "repository_owner_id",
BuildConfigURI: "{{ .url }}/{{ .workflow_ref }}",
BuildConfigDigest: "workflow_sha",
BuildTrigger: "event_name",
RunInvocationURI: "{{ .url }}/{{ .repository }}/actions/runs/{{ .run_id }}/attempts/{{ .run_attempt }}",
SourceRepositoryVisibilityAtSigning: "repository_visibility",
},
Claims: map[string]interface{}{
"issuer": "https://token.actions.githubusercontent.com",
"event_name": "trigger",
"sha": "sha",
"workflow": "workflowname",
"repository": "repository",
"ref": "ref",
"job_workflow_sha": "jobWorkflowSha",
"job_workflow_ref": "jobWorkflowRef",
"runner_environment": "runnerEnv",
"repository_id": "repoID",
"repository_owner": "repoOwner",
"repository_owner_id": "repoOwnerID",
"workflow_ref": "workflowRef",
"workflow_sha": "workflowSHA",
"run_id": "runID",
"run_attempt": "runAttempt",
"repository_visibility": "public",
},
Defaults: map[string]string{
"url": "https://github.com",
},
},
WantErr: false,
WantFacts: map[string]func(x509.Certificate) error{
`Certifificate should have correct issuer`: factDeprecatedExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}, "https://token.actions.githubusercontent.com"),
`Certificate has correct trigger extension`: factDeprecatedExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 2}, "trigger"),
`Certificate has correct SHA extension`: factDeprecatedExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 3}, "sha"),
`Certificate has correct workflow extension`: factDeprecatedExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 4}, "workflowname"),
`Certificate has correct repository extension`: factDeprecatedExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 5}, "repository"),
`Certificate has correct ref extension`: factDeprecatedExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 6}, "ref"),
`Certificate has correct issuer (v2) extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 8}, "https://token.actions.githubusercontent.com"),
`Certificate has correct builder signer URI extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 9}, "https://github.com/jobWorkflowRef"),
`Certificate has correct builder signer digest extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 10}, "jobWorkflowSha"),
`Certificate has correct runner environment extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 11}, "runnerEnv"),
`Certificate has correct source repo URI extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 12}, "https://github.com/repository"),
`Certificate has correct source repo digest extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 13}, "sha"),
`Certificate has correct source repo ref extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 14}, "ref"),
`Certificate has correct source repo ID extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 15}, "repoID"),
`Certificate has correct source repo owner URI extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 16}, "https://github.com/repoOwner"),
`Certificate has correct source repo owner ID extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 17}, "repoOwnerID"),
`Certificate has correct build config URI extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 18}, "https://github.com/workflowRef"),
`Certificate has correct build config digest extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 19}, "workflowSHA"),
`Certificate has correct build trigger extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 20}, "trigger"),
`Certificate has correct run invocation ID extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 21}, "https://github.com/repository/actions/runs/runID/attempts/runAttempt"),
`Certificate has correct source repository visibility extension`: factExtensionIs(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 22}, "public"),
},
},
`Github workflow value with bad URL fails`: {
Principal: &Provider{},
WantErr: true,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
var cert x509.Certificate
err := test.Principal.Embed(context.TODO(), &cert)
if err != nil {
if !test.WantErr {
t.Error(err)
}
return
} else if test.WantErr {
t.Error("expected error")
}
for factName, fact := range test.WantFacts {
t.Run(factName, func(t *testing.T) {
if err := fact(cert); err != nil {
t.Error(err)
}
})
}
})
}
}

func factExtensionIs(oid asn1.ObjectIdentifier, value string) func(x509.Certificate) error {
return func(cert x509.Certificate) error {
for _, ext := range cert.ExtraExtensions {
if ext.Id.Equal(oid) {
var strVal string
_, _ = asn1.Unmarshal(ext.Value, &strVal)
if value != strVal {
return fmt.Errorf("expected oid %v to be %s, but got %s", oid, value, strVal)
}
return nil
}
}
return errors.New("extension not set")
}
}

func factDeprecatedExtensionIs(oid asn1.ObjectIdentifier, value string) func(x509.Certificate) error {
return func(cert x509.Certificate) error {
for _, ext := range cert.ExtraExtensions {
if ext.Id.Equal(oid) {
if !bytes.Equal(ext.Value, []byte(value)) {
return fmt.Errorf("expected oid %v to be %s, but got %s", oid, value, ext.Value)
}
return nil
}
}
return errors.New("extension not set")
}
}
3 changes: 0 additions & 3 deletions pkg/server/issuer_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/fulcio/pkg/identity/buildkite"
"github.com/sigstore/fulcio/pkg/identity/ciprovider"
"github.com/sigstore/fulcio/pkg/identity/codefresh"
"github.com/sigstore/fulcio/pkg/identity/email"
"github.com/sigstore/fulcio/pkg/identity/github"
Expand Down Expand Up @@ -53,8 +52,6 @@ func getIssuer(meta string, i config.OIDCIssuer) identity.Issuer {
issuerURL = meta
}
switch i.Type {
case config.IssuerTypeCiProvider:
return ciprovider.Issuer(issuerURL)
case config.IssuerTypeEmail:
return email.Issuer(issuerURL)
case config.IssuerTypeGithubWorkflow:
Expand Down

0 comments on commit 93545f0

Please sign in to comment.