Repository navigation
Expand file tree
/
Copy pathauth.go
More file actions
102 lines (94 loc) · 3.18 KB
/
Copy pathauth.go
File metadata and controls
102 lines (94 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package main
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"github.com/bradleyfalzon/ghinstallation/v2"
"golang.org/x/oauth2"
)
// AuthConfig is how the plugin authenticates to GitHub: a token (personal access token or
// any other bearer token) or a GitHub App installation. A token wins when both are set.
type AuthConfig struct {
Token string `mapstructure:"token"`
// GitHub App authentication. The plugin exchanges the app's private key for short-lived
// installation tokens, renewed as they expire.
AppID string `mapstructure:"app_id"`
InstallationID string `mapstructure:"installation_id"`
// The PEM private key, from a file (preferred: keeps the key out of the config) or inline,
// base64-encoded or as plain PEM.
PrivateKeyFile string `mapstructure:"private_key_file"`
PrivateKey string `mapstructure:"private_key"`
}
// validateAuth requires a token or a complete set of GitHub App credentials.
func (c *AuthConfig) validateAuth() error {
if c.Token != "" {
return nil
}
if c.AppID == "" && c.InstallationID == "" && c.PrivateKeyFile == "" && c.PrivateKey == "" {
return errors.New("token or GitHub App credentials (app_id, installation_id and private_key_file or private_key) are required")
}
var missing []string
if c.AppID == "" {
missing = append(missing, "app_id")
}
if c.InstallationID == "" {
missing = append(missing, "installation_id")
}
if c.PrivateKeyFile == "" && c.PrivateKey == "" {
missing = append(missing, "private_key_file or private_key")
}
if len(missing) > 0 {
return fmt.Errorf("GitHub App authentication needs %s", strings.Join(missing, ", "))
}
if c.PrivateKeyFile != "" && c.PrivateKey != "" {
return errors.New("only one of private_key_file or private_key may be set")
}
return nil
}
// httpClient returns an HTTP client that authenticates every request to GitHub (REST and
// GraphQL alike).
func (c *AuthConfig) httpClient(ctx context.Context) (*http.Client, error) {
if c.Token != "" {
return oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Token})), nil
}
appID, err := strconv.ParseInt(c.AppID, 10, 64)
if err != nil {
return nil, fmt.Errorf("parsing app_id: %w", err)
}
installationID, err := strconv.ParseInt(c.InstallationID, 10, 64)
if err != nil {
return nil, fmt.Errorf("parsing installation_id: %w", err)
}
key, err := c.privateKey()
if err != nil {
return nil, err
}
transport, err := ghinstallation.New(http.DefaultTransport, appID, installationID, key)
if err != nil {
return nil, fmt.Errorf("creating GitHub App installation transport: %w", err)
}
return &http.Client{Transport: transport}, nil
}
func (c *AuthConfig) privateKey() ([]byte, error) {
if c.PrivateKeyFile != "" {
key, err := os.ReadFile(c.PrivateKeyFile)
if err != nil {
return nil, fmt.Errorf("reading private_key_file: %w", err)
}
return key, nil
}
key := strings.TrimSpace(c.PrivateKey)
if strings.HasPrefix(key, "-----BEGIN") {
return []byte(key), nil
}
decoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return nil, fmt.Errorf("decoding private_key (expected PEM or base64-encoded PEM): %w", err)
}
return decoded, nil
}