Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added output structure to hold oidc token verifier output data. #12540

Merged
merged 3 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ packages:
config:
all: True
dir: "{{.InterfaceDir}}/mocks"
outpkg: "{{.PackageName}}mocks"
outpkg: "{{.PackageName}}mocks"
github.com/kyma-project/test-infra/cmd/oidc-token-verifier:
config:
dir: "{{.InterfaceDir}}/mocks"
outpkg: "{{.PackageName}}mocks"
interfaces:
TrustedIssuerProvider:
88 changes: 84 additions & 4 deletions cmd/oidc-token-verifier/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/json"
"fmt"
"os"

Expand All @@ -23,6 +24,7 @@ type options struct {
token string
debug bool
oidcTokenExpirationTime int // OIDC token expiration time in minutes
outputPath string
}

var (
Expand All @@ -41,6 +43,7 @@ func NewRootCmd() *cobra.Command {
rootCmd.PersistentFlags().StringVarP(&opts.token, "token", "t", "", "OIDC token to verify")
rootCmd.PersistentFlags().BoolVarP(&opts.debug, "debug", "d", false, "Enable debug mode")
rootCmd.PersistentFlags().IntVarP(&opts.oidcTokenExpirationTime, "oidc-token-expiration-time", "e", 10, "OIDC token expiration time in minutes")
rootCmd.PersistentFlags().StringVarP(&opts.outputPath, "output-path", "o", "/oidc-verifier-output.json", "Path to the file where the output data will be saved")
return rootCmd
}

Expand All @@ -65,6 +68,67 @@ func init() {
rootCmd.AddCommand(verifyCmd)
}

type TrustedIssuerProvider interface {
GetIssuer() tioidc.Issuer
}

// output is a struct that holds the output values that are printed to the file.
// The data provided in this struct is relevant for the component that uses the OIDC token verifier.
// The output values are printed to the file in the json format.
type output struct {
GithubURL string `json:"github_url" yaml:"github_url"`
ClientID string `json:"client_id" yaml:"client_id"`
}

// setGithubURLOutput sets the Github URL value to the output struct.
// The Github URL value is read from the TokenProcessor trusted issuer.
func (output *output) setGithubURLOutput(logger Logger, issuerProvider TrustedIssuerProvider) error {
var githubURL string

if githubURL = issuerProvider.GetIssuer().GetGithubURL(); githubURL == "" {
return fmt.Errorf("github URL not found in the tokenProcessor trusted issuer: %s", issuerProvider.GetIssuer())
}

output.GithubURL = githubURL

logger.Debugw("Set output Github URL value", "githubURL", output.GithubURL)

return nil
}

// setClientIDOutput sets the client ID value to the output struct.
// The client ID value is read from the TokenProcessor trusted issuer.
func (output *output) setClientIDOutput(logger Logger, issuerProvider TrustedIssuerProvider) error {
var clientID string
if clientID = issuerProvider.GetIssuer().ClientID; clientID == "" {
return fmt.Errorf("client ID not found in the tokenProcessor trusted issuer: %s", issuerProvider.GetIssuer())
}

output.ClientID = clientID

logger.Debugw("Set output client ID value", "clientID", output.ClientID)

return nil
}

// writeOutputFile writes the output values to the json file.
// The file path is specified by the --output-path flag.
func (output *output) writeOutputFile(logger Logger, path string) error {
outputFile, err := os.Create(path)
if err != nil {
return err
}

err = json.NewEncoder(outputFile).Encode(output)
if err != nil {
return err
}

logger.Debugw("Output values written to the file", "path", path, "output", output)

return nil
}

// isTokenProvided checks if the token flag is set.
// If not, check if AUTHORIZATION environment variable is set.
// If neither is set, return an error.
Expand Down Expand Up @@ -121,9 +185,6 @@ func (opts *options) verifyToken() error {

logger.Infow("Token processor created for trusted issuer", "issuer", tokenProcessor.Issuer())

// TODO (dekiel): implement writing output data to the file. This will give us separated clear output for a data and logs.
fmt.Printf("GITHUB_URL=%s\n", tokenProcessor.GetIssuer().GetGithubURL())

// Create a new verifier config that will be used to verify the token.
// The standard expiration check is skipped.
// We use custom expiration time check to allow longer token expiration time than the value in the token.
Expand Down Expand Up @@ -177,14 +238,33 @@ func (opts *options) verifyToken() error {
if err != nil {
return err
}

logger.Infow("Token claims expectations verified successfully")
logger.Infow("All token checks passed successfully")

outputData := output{}
err = outputData.setGithubURLOutput(logger, &tokenProcessor)
if err != nil {
return err
}

err = outputData.setClientIDOutput(logger, &tokenProcessor)
if err != nil {
return err
}

err = outputData.writeOutputFile(logger, opts.outputPath)
if err != nil {
return err
}

logger.Infow("Output data written to the file", "path", opts.outputPath)

return nil
}

func main() {
if err := rootCmd.Execute(); err != nil {
panic(err)
}
}
}
129 changes: 129 additions & 0 deletions cmd/oidc-token-verifier/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package main

import (
"encoding/json"
"fmt"
"os"

"github.com/kyma-project/test-infra/cmd/oidc-token-verifier/mocks"
tioidc "github.com/kyma-project/test-infra/pkg/oidc"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"go.uber.org/zap"
)

var _ = Describe("Output", func() {
var (
logger Logger
issuerProvider *mainmocks.MockTrustedIssuerProvider
issuer tioidc.Issuer
out output
)

BeforeEach(func() {
logger = zap.NewNop().Sugar()
issuerProvider = &mainmocks.MockTrustedIssuerProvider{}
issuer = tioidc.Issuer{
Name: "test-issuer",
IssuerURL: "https://test-issuer.com",
JWKSURL: "https://test-issuer.com/jwks",
ExpectedJobWorkflowRef: "test-workflow",
GithubURL: "https://github-test.com",
ClientID: "test-client-id",
}
out = output{}
})

Describe("setGithubURLOutput", func() {
Context("when the Github URL is found in the tokenProcessor trusted issuer", func() {
It("should set the Github URL in the output struct", func() {
issuerProvider.On("GetIssuer").Return(issuer)

err := out.setGithubURLOutput(logger, issuerProvider)
Expect(err).NotTo(HaveOccurred(), "Expected no error, but got: %v", err)
Expect(out.GithubURL).To(Equal(issuer.GithubURL), "Expected Github URL to be %s, but got %s", issuer.GithubURL, out.GithubURL)
})
})

Context("when the Github URL is not found in the tokenProcessor trusted issuer", func() {
It("should return an error", func() {
issuer.GithubURL = ""
issuerProvider.On("GetIssuer").Return(issuer)

err := out.setGithubURLOutput(logger, issuerProvider)
Expect(err).To(HaveOccurred(), "Expected an error, but got none")
Expect(err).To(MatchError(fmt.Errorf("github URL not found in the tokenProcessor trusted issuer: %s", issuerProvider.GetIssuer())), "Expected error message to be 'github URL not found in the tokenProcessor trusted issuer', but got %s", err)
Expect(out.GithubURL).To(BeEmpty(), "Expected Github URL to be empty, but got %v", out.GithubURL)
})
})
})

Describe("setClientIDOutput", func() {
Context("when the Client ID is found in the tokenProcessor trusted issuer", func() {
It("should set the Client ID in the output struct", func() {
issuerProvider.On("GetIssuer").Return(issuer)

err := out.setClientIDOutput(logger, issuerProvider)
Expect(err).NotTo(HaveOccurred(), "Expected no error, but got: %v", err)
Expect(out.ClientID).To(Equal(issuer.ClientID), "Expected Client ID to be %s, but got %s", issuer.ClientID, out.ClientID)
})
})

Context("when the Client ID is not found in the tokenProcessor trusted issuer", func() {
It("should return an error", func() {
issuer.ClientID = ""
issuerProvider.On("GetIssuer").Return(issuer)

err := out.setClientIDOutput(logger, issuerProvider)
Expect(err).To(HaveOccurred(), "Expected an error, but got none")
Expect(err).To(MatchError(fmt.Errorf("client ID not found in the tokenProcessor trusted issuer: %s", issuerProvider.GetIssuer())), "Expected error message to be 'client ID not found in the tokenProcessor trusted issuer', but got %s", err)
Expect(out.ClientID).To(BeEmpty(), "Expected Client ID to be empty, but got %v", out.ClientID)
})
})
})

Describe("writeOutputFile", func() {
var filePath = "./output.json"

BeforeEach(func() {
// Verify if the path exists and is writable
file, err := os.Create(filePath)
Expect(err).NotTo(HaveOccurred(), "Expected no error creating the file, but got: %v", err)
file.Close()
})

AfterEach(func() {
// Remove created artifacts
err := os.Remove(filePath)
Expect(err).NotTo(HaveOccurred(), "Expected no error removing the file, but got: %v", err)
})

Context("when the output file is successfully written", func() {
It("should write the output values to the json file", func() {
out.GithubURL = issuer.GithubURL
out.ClientID = issuer.ClientID

err := out.writeOutputFile(logger, filePath)
Expect(err).NotTo(HaveOccurred(), "Expected no error, but got: %v", err)

file, err := os.Open(filePath)
Expect(err).NotTo(HaveOccurred(), "Expected no error opening the file, but got: %v", err)
defer file.Close()

var writtenOutput output
err = json.NewDecoder(file).Decode(&writtenOutput)
Expect(err).NotTo(HaveOccurred(), "Expected no error decoding the file, but got: %v", err)
Expect(writtenOutput).To(Equal(out), "Expected written output to be %v, but got %v", out, writtenOutput)
})
})

Context("when there is an error creating the output file", func() {
It("should return an error", func() {
filePath := "/invalid-path/output.json"

err := out.writeOutputFile(logger, filePath)
Expect(err).To(HaveOccurred(), "Expected an error, but got none")
})
})
})
})
80 changes: 80 additions & 0 deletions cmd/oidc-token-verifier/mocks/mock_TrustedIssuerProvider.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions cmd/oidc-token-verifier/oidc_token_verifier_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestOidcTokenVerifier(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "OidcTokenVerifier Suite")
}
Loading