Skip to content

Commit 9ca6d95

Browse files
authored
Add new utility functions for authenticating as a Github App (#68)
* Add new utility functions for authenticating as a Github App * Fix build * Use org instead of user
1 parent 0cdfa1a commit 9ca6d95

File tree

5 files changed

+153
-26
lines changed

5 files changed

+153
-26
lines changed

.circleci/config.yml

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,16 @@
11
defaults: &defaults
2-
machine:
3-
enabled: true
4-
image: ubuntu-2004:202111-02
5-
env: &env
2+
docker:
3+
- image: 087285199408.dkr.ecr.us-east-1.amazonaws.com/circle-ci-test-image-base:go1.17-tf1.2-tg37.4-pck1.8-ci50.1
64
environment:
7-
GRUNTWORK_INSTALLER_VERSION: v0.0.36
8-
TERRATEST_LOG_PARSER_VERSION: v0.40.1
9-
TERRAFORM_AWS_CI_VERSION: v0.41.1
10-
TFENV_VERSION: NONE
11-
TERRAFORM_VERSION: 1.0.11
12-
TERRAGRUNT_VERSION: NONE
13-
PACKER_VERSION: NONE
14-
GOLANG_VERSION: 1.17
155
GO111MODULE: auto
166
version: 2.1
177
jobs:
188
test:
199
<<: *defaults
20-
<<: *env
2110
steps:
2211
- checkout
23-
- run:
24-
name: install gruntwork utils
25-
command: |
26-
curl -Ls https://raw.githubusercontent.com/gruntwork-io/gruntwork-installer/master/bootstrap-gruntwork-installer.sh | bash /dev/stdin --version "${GRUNTWORK_INSTALLER_VERSION}"
27-
gruntwork-install --module-name "gruntwork-module-circleci-helpers" --repo "https://github.com/gruntwork-io/terraform-aws-ci" --tag "$TERRAFORM_AWS_CI_VERSION"
28-
gruntwork-install --binary-name "terratest_log_parser" --repo "https://github.com/gruntwork-io/terratest" --tag "${TERRATEST_LOG_PARSER_VERSION}"
29-
configure-environment-for-gruntwork-module \
30-
--tfenv-version ${TFENV_VERSION} \
31-
--terraform-version ${TERRAFORM_VERSION} \
32-
--terragrunt-version ${TERRAGRUNT_VERSION} \
33-
--packer-version ${PACKER_VERSION} \
34-
--go-version ${GOLANG_VERSION}
12+
- setup_remote_docker:
13+
version: 20.10.14
3514
- run: run-go-tests
3615
workflows:
3716
version: 2
@@ -43,3 +22,4 @@ workflows:
4322
only: /^v.*/
4423
context:
4524
- Gruntwork Admin
25+
- Gruntwork Clients Test Github App

github/auth.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
8+
"net/http"
9+
10+
"github.com/bradleyfalzon/ghinstallation"
11+
"github.com/gruntwork-io/go-commons/entrypoint"
12+
"github.com/gruntwork-io/go-commons/errors"
13+
)
14+
15+
// GithubAppConfig represents configuration settings for a Github App that can be used to authenticate to the Github
16+
// API.
17+
type GithubAppConfig struct {
18+
// ID of the Github App to authenticate as.
19+
AppID int64 `json:"app_id"`
20+
21+
// ID of the Github App installation to authenticate as. This ID determines the Github Org that the App can access.
22+
AppInstallationID int64 `json:"app_installation_id"`
23+
24+
// The PEM encoded private key (base64 encoded) that can be used to obtain an installation token to access the
25+
// Github API.
26+
PrivateKeyPEMBase64 string `json:"private_key_pem"`
27+
}
28+
29+
// GetInstallationToken uses the configured GitHub App credentials to obtain an installation token that can be used to
30+
// access Github using both the API and Git CLI. This token works the same as an Oauth token, or Personal Access Token.
31+
func (config *GithubAppConfig) GetInstallationToken() (string, error) {
32+
if config == nil {
33+
return "", errors.WithStackTrace(fmt.Errorf("GithubAppConfig is nil"))
34+
}
35+
36+
// Decode private key PEM, which should be base64 encoded to allow easy handling of newlines.
37+
privateKeyPEM, err := base64.StdEncoding.DecodeString(config.PrivateKeyPEMBase64)
38+
if err != nil {
39+
return "", err
40+
}
41+
42+
// Retrieve a valid installation token that can be used to authenticate requests to GitHub, or clone repos over
43+
// HTTPS.
44+
itr, err := ghinstallation.New(
45+
http.DefaultTransport,
46+
config.AppID,
47+
config.AppInstallationID,
48+
privateKeyPEM,
49+
)
50+
if err != nil {
51+
return "", errors.WithStackTrace(err)
52+
}
53+
token, err := itr.Token(context.Background())
54+
return token, errors.WithStackTrace(err)
55+
}
56+
57+
// LoadGithubAppConfigFromEnv will load a Github App Configuration from the given environment variable, assuming it is
58+
// encoded in JSON format.
59+
func LoadGithubAppConfigFromEnv(envVarName string) (*GithubAppConfig, error) {
60+
githubAppConfigJSON, err := entrypoint.EnvironmentVarRequiredE(envVarName)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
var githubAppConfig *GithubAppConfig
66+
jsonLoadErr := json.Unmarshal([]byte(githubAppConfigJSON), githubAppConfig)
67+
return githubAppConfig, errors.WithStackTrace(jsonLoadErr)
68+
}

github/auth_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"os"
6+
"strconv"
7+
"testing"
8+
9+
"github.com/google/go-github/v44/github"
10+
"github.com/gruntwork-io/terratest/modules/environment"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
"golang.org/x/oauth2"
14+
)
15+
16+
const (
17+
ghAppIDEnv = "GITHUB_APP_ID"
18+
ghAppInstallationIDEnv = "GITHUB_APP_INSTALLATION_ID"
19+
ghAppPrivateKeyBase64Env = "GITHUB_APP_PRIVATE_KEY_PEM_BASE64"
20+
)
21+
22+
func TestGithubAppConfig(t *testing.T) {
23+
t.Parallel()
24+
25+
environment.RequireEnvVar(t, ghAppIDEnv)
26+
environment.RequireEnvVar(t, ghAppInstallationIDEnv)
27+
environment.RequireEnvVar(t, ghAppPrivateKeyBase64Env)
28+
29+
ghAppConfig := getGitHubAppConfig(t)
30+
token, err := ghAppConfig.GetInstallationToken()
31+
require.NoError(t, err)
32+
33+
gh := newGithubClient(token)
34+
org, _, err := gh.Organizations.Get(context.Background(), "gruntwork-clients")
35+
require.NoError(t, err)
36+
require.NotNil(t, org.Login)
37+
assert.Equal(t, "gruntwork-clients", *org.Login)
38+
}
39+
40+
func getGitHubAppConfig(t *testing.T) *GithubAppConfig {
41+
appIDStr := os.Getenv(ghAppIDEnv)
42+
appID, err := strconv.ParseInt(appIDStr, 10, 64)
43+
require.NoError(t, err)
44+
45+
appInstallationIDStr := os.Getenv(ghAppInstallationIDEnv)
46+
appInstallationID, err := strconv.ParseInt(appInstallationIDStr, 10, 64)
47+
require.NoError(t, err)
48+
49+
return &GithubAppConfig{
50+
AppID: appID,
51+
AppInstallationID: appInstallationID,
52+
PrivateKeyPEMBase64: os.Getenv(ghAppPrivateKeyBase64Env),
53+
}
54+
}
55+
56+
func newGithubClient(token string) *github.Client {
57+
tokenSource := oauth2.StaticTokenSource(
58+
&oauth2.Token{AccessToken: token},
59+
)
60+
61+
tokenClient := oauth2.NewClient(context.Background(), tokenSource)
62+
client := github.NewClient(tokenClient)
63+
return client
64+
}

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@ require (
1010
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.1
1111
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.13
1212
github.com/bgentry/speakeasy v0.1.0
13+
github.com/bradleyfalzon/ghinstallation v1.1.1
1314
github.com/fatih/color v1.13.0
1415
github.com/go-errors/errors v1.4.2
16+
github.com/google/go-github/v44 v44.1.0
1517
github.com/gruntwork-io/terratest v0.40.17
1618
github.com/hashicorp/go-multierror v1.1.1
1719
github.com/mattn/go-zglob v0.0.3
1820
github.com/sirupsen/logrus v1.8.1
1921
github.com/stretchr/testify v1.8.0
2022
github.com/urfave/cli/v2 v2.10.3
2123
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
24+
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
2225
)
2326

2427
require (
@@ -44,13 +47,16 @@ require (
4447
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
4548
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
4649
github.com/davecgh/go-spew v1.1.1 // indirect
50+
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
4751
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
4852
github.com/go-logr/logr v0.2.0 // indirect
4953
github.com/go-sql-driver/mysql v1.4.1 // indirect
5054
github.com/gogo/protobuf v1.3.2 // indirect
5155
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
5256
github.com/golang/protobuf v1.5.2 // indirect
5357
github.com/golang/snappy v0.0.3 // indirect
58+
github.com/google/go-github/v29 v29.0.2 // indirect
59+
github.com/google/go-querystring v1.1.0 // indirect
5460
github.com/google/gofuzz v1.1.0 // indirect
5561
github.com/google/uuid v1.2.0 // indirect
5662
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
@@ -87,7 +93,6 @@ require (
8793
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
8894
golang.org/x/mod v0.4.2 // indirect
8995
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
90-
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
9196
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
9297
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
9398
golang.org/x/text v0.3.7 // indirect

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
108108
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
109109
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
110110
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
111+
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
112+
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
111113
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
112114
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
113115
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -123,6 +125,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
123125
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
124126
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
125127
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
128+
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
126129
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
127130
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
128131
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
@@ -218,6 +221,13 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
218221
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
219222
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
220223
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
224+
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
225+
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
226+
github.com/google/go-github/v44 v44.1.0 h1:shWPaufgdhr+Ad4eo/pZv9ORTxFpsxPEPEuuXAKIQGA=
227+
github.com/google/go-github/v44 v44.1.0/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw=
228+
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
229+
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
230+
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
221231
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
222232
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
223233
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=

0 commit comments

Comments
 (0)