Skip to content

Add new utility functions for authenticating as a Github App #68

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

Merged
merged 3 commits into from
Jul 14, 2022
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
30 changes: 5 additions & 25 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
defaults: &defaults
machine:
enabled: true
image: ubuntu-2004:202111-02
env: &env
docker:
- image: 087285199408.dkr.ecr.us-east-1.amazonaws.com/circle-ci-test-image-base:go1.17-tf1.2-tg37.4-pck1.8-ci50.1
environment:
GRUNTWORK_INSTALLER_VERSION: v0.0.36
TERRATEST_LOG_PARSER_VERSION: v0.40.1
TERRAFORM_AWS_CI_VERSION: v0.41.1
TFENV_VERSION: NONE
TERRAFORM_VERSION: 1.0.11
TERRAGRUNT_VERSION: NONE
PACKER_VERSION: NONE
GOLANG_VERSION: 1.17
GO111MODULE: auto
version: 2.1
jobs:
test:
<<: *defaults
<<: *env
steps:
- checkout
- run:
name: install gruntwork utils
command: |
curl -Ls https://raw.githubusercontent.com/gruntwork-io/gruntwork-installer/master/bootstrap-gruntwork-installer.sh | bash /dev/stdin --version "${GRUNTWORK_INSTALLER_VERSION}"
gruntwork-install --module-name "gruntwork-module-circleci-helpers" --repo "https://github.com/gruntwork-io/terraform-aws-ci" --tag "$TERRAFORM_AWS_CI_VERSION"
gruntwork-install --binary-name "terratest_log_parser" --repo "https://github.com/gruntwork-io/terratest" --tag "${TERRATEST_LOG_PARSER_VERSION}"
configure-environment-for-gruntwork-module \
--tfenv-version ${TFENV_VERSION} \
--terraform-version ${TERRAFORM_VERSION} \
--terragrunt-version ${TERRAGRUNT_VERSION} \
--packer-version ${PACKER_VERSION} \
--go-version ${GOLANG_VERSION}
- setup_remote_docker:
version: 20.10.14
- run: run-go-tests
workflows:
version: 2
Expand All @@ -43,3 +22,4 @@ workflows:
only: /^v.*/
context:
- Gruntwork Admin
- Gruntwork Clients Test Github App
68 changes: 68 additions & 0 deletions github/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package github

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"

"github.com/bradleyfalzon/ghinstallation"
"github.com/gruntwork-io/go-commons/entrypoint"
"github.com/gruntwork-io/go-commons/errors"
)

// GithubAppConfig represents configuration settings for a Github App that can be used to authenticate to the Github
// API.
type GithubAppConfig struct {
// ID of the Github App to authenticate as.
AppID int64 `json:"app_id"`

// ID of the Github App installation to authenticate as. This ID determines the Github Org that the App can access.
AppInstallationID int64 `json:"app_installation_id"`

// The PEM encoded private key (base64 encoded) that can be used to obtain an installation token to access the
// Github API.
PrivateKeyPEMBase64 string `json:"private_key_pem"`
}

// GetInstallationToken uses the configured GitHub App credentials to obtain an installation token that can be used to
// access Github using both the API and Git CLI. This token works the same as an Oauth token, or Personal Access Token.
func (config *GithubAppConfig) GetInstallationToken() (string, error) {
if config == nil {
return "", errors.WithStackTrace(fmt.Errorf("GithubAppConfig is nil"))
}

// Decode private key PEM, which should be base64 encoded to allow easy handling of newlines.
privateKeyPEM, err := base64.StdEncoding.DecodeString(config.PrivateKeyPEMBase64)
if err != nil {
return "", err
}

// Retrieve a valid installation token that can be used to authenticate requests to GitHub, or clone repos over
// HTTPS.
itr, err := ghinstallation.New(
http.DefaultTransport,
config.AppID,
config.AppInstallationID,
privateKeyPEM,
)
if err != nil {
return "", errors.WithStackTrace(err)
}
token, err := itr.Token(context.Background())
return token, errors.WithStackTrace(err)
}

// LoadGithubAppConfigFromEnv will load a Github App Configuration from the given environment variable, assuming it is
// encoded in JSON format.
func LoadGithubAppConfigFromEnv(envVarName string) (*GithubAppConfig, error) {
githubAppConfigJSON, err := entrypoint.EnvironmentVarRequiredE(envVarName)
if err != nil {
return nil, err
}

var githubAppConfig *GithubAppConfig
jsonLoadErr := json.Unmarshal([]byte(githubAppConfigJSON), githubAppConfig)
return githubAppConfig, errors.WithStackTrace(jsonLoadErr)
}
64 changes: 64 additions & 0 deletions github/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package github

import (
"context"
"os"
"strconv"
"testing"

"github.com/google/go-github/v44/github"
"github.com/gruntwork-io/terratest/modules/environment"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
)

const (
ghAppIDEnv = "GITHUB_APP_ID"
ghAppInstallationIDEnv = "GITHUB_APP_INSTALLATION_ID"
ghAppPrivateKeyBase64Env = "GITHUB_APP_PRIVATE_KEY_PEM_BASE64"
)

func TestGithubAppConfig(t *testing.T) {
t.Parallel()

environment.RequireEnvVar(t, ghAppIDEnv)
environment.RequireEnvVar(t, ghAppInstallationIDEnv)
environment.RequireEnvVar(t, ghAppPrivateKeyBase64Env)

ghAppConfig := getGitHubAppConfig(t)
token, err := ghAppConfig.GetInstallationToken()
require.NoError(t, err)

gh := newGithubClient(token)
org, _, err := gh.Organizations.Get(context.Background(), "gruntwork-clients")
require.NoError(t, err)
require.NotNil(t, org.Login)
assert.Equal(t, "gruntwork-clients", *org.Login)
}

func getGitHubAppConfig(t *testing.T) *GithubAppConfig {
appIDStr := os.Getenv(ghAppIDEnv)
appID, err := strconv.ParseInt(appIDStr, 10, 64)
require.NoError(t, err)

appInstallationIDStr := os.Getenv(ghAppInstallationIDEnv)
appInstallationID, err := strconv.ParseInt(appInstallationIDStr, 10, 64)
require.NoError(t, err)

return &GithubAppConfig{
AppID: appID,
AppInstallationID: appInstallationID,
PrivateKeyPEMBase64: os.Getenv(ghAppPrivateKeyBase64Env),
}
}

func newGithubClient(token string) *github.Client {
tokenSource := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)

tokenClient := oauth2.NewClient(context.Background(), tokenSource)
client := github.NewClient(tokenClient)
return client
}
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.1
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.13
github.com/bgentry/speakeasy v0.1.0
github.com/bradleyfalzon/ghinstallation v1.1.1
github.com/fatih/color v1.13.0
github.com/go-errors/errors v1.4.2
github.com/google/go-github/v44 v44.1.0
github.com/gruntwork-io/terratest v0.40.17
github.com/hashicorp/go-multierror v1.1.1
github.com/mattn/go-zglob v0.0.3
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.8.0
github.com/urfave/cli/v2 v2.10.3
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
)

require (
Expand All @@ -44,13 +47,16 @@ require (
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/go-logr/logr v0.2.0 // indirect
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/go-github/v29 v29.0.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
Expand Down Expand Up @@ -87,7 +93,6 @@ require (
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand All @@ -123,6 +125,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
Expand Down Expand Up @@ -218,6 +221,13 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-github/v44 v44.1.0 h1:shWPaufgdhr+Ad4eo/pZv9ORTxFpsxPEPEuuXAKIQGA=
github.com/google/go-github/v44 v44.1.0/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down