[cleanup] Remove old GitHub App code (#50933)

This commit is contained in:
Petri-Johan Last 2023-04-21 05:27:27 +00:00 committed by GitHub
parent a0e3cbf7c6
commit 1eee46e679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 104 additions and 1532 deletions

View File

@ -24,7 +24,6 @@ go_library(
"//internal/env",
"//internal/extsvc",
"//internal/extsvc/crates",
"//internal/extsvc/github",
"//internal/extsvc/gomodproxy",
"//internal/extsvc/npm",
"//internal/extsvc/pypi",
@ -43,12 +42,10 @@ go_library(
"//internal/requestclient",
"//internal/service",
"//internal/trace",
"//internal/types",
"//lib/errors",
"//schema",
"@com_github_json_iterator_go//:go",
"@com_github_sourcegraph_log//:log",
"@com_github_tidwall_gjson//:gjson",
"@org_golang_x_sync//semaphore",
"@org_golang_x_time//rate",
],
@ -66,16 +63,10 @@ go_test(
"//cmd/gitserver/server",
"//internal/api",
"//internal/codeintel/dependencies",
"//internal/conf",
"//internal/database",
"//internal/extsvc",
"//internal/extsvc/github",
"//internal/types",
"//lib/errors",
"//schema",
"@com_github_sourcegraph_log//:log",
"@com_github_sourcegraph_log//logtest",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
],
)

View File

@ -7,7 +7,6 @@ import (
"database/sql"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"path/filepath"
@ -16,7 +15,6 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/sourcegraph/log"
"github.com/tidwall/gjson"
"golang.org/x/sync/semaphore"
"golang.org/x/time/rate"
@ -33,7 +31,6 @@ import (
"github.com/sourcegraph/sourcegraph/internal/env"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/crates"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/extsvc/gomodproxy"
"github.com/sourcegraph/sourcegraph/internal/extsvc/npm"
"github.com/sourcegraph/sourcegraph/internal/extsvc/pypi"
@ -52,7 +49,6 @@ import (
"github.com/sourcegraph/sourcegraph/internal/requestclient"
"github.com/sourcegraph/sourcegraph/internal/service"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/sourcegraph/schema"
)
@ -141,7 +137,7 @@ func Main(ctx context.Context, observationCtx *observation.Context, ready servic
ReposDir: config.ReposDir,
DesiredPercentFree: wantPctFree2,
GetRemoteURLFunc: func(ctx context.Context, repo api.RepoName) (string, error) {
return getRemoteURLFunc(ctx, externalServiceStore, repoStore, nil, repo)
return getRemoteURLFunc(ctx, externalServiceStore, repoStore, repo)
},
GetVCSSyncer: func(ctx context.Context, repo api.RepoName) (server.VCSSyncer, error) {
return getVCSSyncer(ctx, externalServiceStore, repoStore, dependenciesSvc, repo, config.ReposDir, config.CoursierCacheDir)
@ -314,7 +310,6 @@ func getRemoteURLFunc(
ctx context.Context,
externalServiceStore database.ExternalServiceStore,
repoStore database.RepoStore,
cli httpcli.Doer,
repo api.RepoName,
) (string, error) {
r, err := repoStore.GetByName(ctx, repo)
@ -338,89 +333,11 @@ func getRemoteURLFunc(
continue
}
if svc.Kind == extsvc.KindGitHub {
config, err := conf.GitHubAppConfig()
if err != nil {
return "", err
}
if config.Configured() {
rawConfig, err := svc.Config.Decrypt(ctx)
if err != nil {
return "", err
}
installationID := gjson.Get(rawConfig, "githubAppInstallationID").Int()
if installationID > 0 {
rawConfig, err = editGitHubAppExternalServiceConfigToken(ctx, externalServiceStore, svc, rawConfig, config.PrivateKey, config.AppID, installationID, cli)
if err != nil {
return "", errors.Wrap(err, "edit GitHub App external service config token")
}
svc.Config.Set(rawConfig)
}
}
}
return repos.EncryptableCloneURL(ctx, log.Scoped("repos.CloneURL", ""), svc.Kind, svc.Config, r)
}
return "", errors.Errorf("no sources for %q", repo)
}
// editGitHubAppExternalServiceConfigToken updates the "token" field of the given
// external service config through GitHub App and returns a new copy of the
// config ensuring the token is always valid.
func editGitHubAppExternalServiceConfigToken(
ctx context.Context,
externalServiceStore database.ExternalServiceStore,
svc *types.ExternalService,
rawConfig string,
privateKey []byte,
appID string,
installationID int64,
cli httpcli.Doer,
) (string, error) {
logger := log.Scoped("editGitHubAppExternalServiceConfigToken", "updates the 'token' field of the given external service")
baseURL, err := url.Parse(gjson.Get(rawConfig, "url").String())
if err != nil {
return "", errors.Wrap(err, "parse base URL")
}
apiURL, githubDotCom := github.APIRoot(baseURL)
if !githubDotCom {
return "", errors.Errorf("only GitHub App on GitHub.com is supported, but got %q", baseURL)
}
var c schema.GitHubConnection
if err := jsonc.Unmarshal(rawConfig, &c); err != nil {
return "", nil
}
appAuther, err := github.NewGitHubAppAuthenticator(appID, privateKey)
if err != nil {
return "", errors.Wrap(err, "new authenticator with GitHub App")
}
scopedLogger := logger.Scoped("app", "github client for github app").With(log.String("appID", appID))
appClient := github.NewV3Client(scopedLogger, svc.URN(), apiURL, appAuther, cli)
token, err := appClient.CreateAppInstallationAccessToken(ctx, installationID)
if err != nil {
return "", errors.Wrap(err, "get or renew GitHub App installation access token")
}
// NOTE: Use `json.Marshal` breaks the actual external service config that fails
// validation with missing "repos" property when no repository has been selected,
// due to generated JSON tag of ",omitempty".
config, err := jsonc.Edit(rawConfig, token.Token, "token")
if err != nil {
return "", errors.Wrap(err, "edit token")
}
err = externalServiceStore.Update(ctx, conf.Get().AuthProviders, svc.ID,
&database.ExternalServiceUpdate{
Config: &config,
TokenExpiresAt: token.ExpiresAt,
})
return config, err
}
func getVCSSyncer(
ctx context.Context,
externalServiceStore database.ExternalServiceStore,

View File

@ -1,31 +1,22 @@
package shared
import (
"bytes"
"context"
"flag"
"io"
"net/http"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/sourcegraph/log"
"github.com/sourcegraph/log/logtest"
"github.com/sourcegraph/sourcegraph/cmd/gitserver/server"
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/codeintel/dependencies"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/lib/errors"
"github.com/sourcegraph/sourcegraph/schema"
)
func TestMain(m *testing.M) {
@ -71,78 +62,6 @@ func (c *mockDoer) Do(r *http.Request) (*http.Response, error) {
return c.do(r)
}
func TestGetRemoteURLFunc_GitHubApp(t *testing.T) {
externalServiceStore := database.NewMockExternalServiceStore()
externalServiceStore.GetByIDFunc.SetDefaultReturn(
&types.ExternalService{
ID: 1,
Kind: extsvc.KindGitHub,
Config: extsvc.NewUnencryptedConfig(`
{
"url": "https://github.com",
"githubAppInstallationID": "21994992",
"repos": [],
"authorization": {},
}`),
},
nil,
)
repoStore := database.NewMockRepoStore()
repoStore.GetByNameFunc.SetDefaultReturn(
&types.Repo{
ID: 1,
Name: "test-repo-1",
Sources: map[string]*types.SourceInfo{
"extsvc:github:1": {
ID: "extsvc:github:1",
CloneURL: "https://github.com/sgtest/test-repo-1",
},
},
Metadata: &github.Repository{
URL: "https://github.com/sgtest/test-repo-1",
},
},
nil,
)
doer := &mockDoer{
do: func(r *http.Request) (*http.Response, error) {
want := "http://github-proxy/app/installations/21994992/access_tokens"
if r.URL.String() != want {
return nil, errors.Errorf("URL: want %q but got %q", want, r.URL)
}
body := `{"token": "mock-installtion-access-token"}`
return &http.Response{
Status: http.StatusText(http.StatusOK),
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader([]byte(body))),
}, nil
},
}
const bogusKey = `LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCUEFJQkFBSkJBUEpIaWprdG1UMUlLYUd0YTVFZXAzQVo5Q2VPZUw4alBESUZUN3dRZ0tabXQzRUZxRGhCCk93bitRVUhKdUs5Zm92UkROSmVWTDJvWTVCT0l6NHJ3L0cwQ0F3RUFBUUpCQU1BK0o5Mks0d2NQVllsbWMrM28KcHU5NmlKTkNwMmp5Nm5hK1pEQlQzK0VvSUo1VFJGdnN3R2kvTHUzZThYUWwxTDNTM21ub0xPSlZNcTF0bUxOMgpIY0VDSVFEK3daeS83RlYxUEFtdmlXeWlYVklETzJnNWJOaUJlbmdKQ3hFa3Nia1VtUUloQVBOMlZaczN6UFFwCk1EVG9vTlJXcnl0RW1URERkamdiOFpzTldYL1JPRGIxQWlCZWNKblNVQ05TQllLMXJ5VTFmNURTbitoQU9ZaDkKWDFBMlVnTDE3bWhsS1FJaEFPK2JMNmRDWktpTGZORWxmVnRkTUtxQnFjNlBIK01heFU2VzlkVlFvR1dkQWlFQQptdGZ5cE9zYTFiS2hFTDg0blovaXZFYkJyaVJHalAya3lERHYzUlg0V0JrPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=`
conf.Mock(&conf.Unified{
SiteConfiguration: schema.SiteConfiguration{
GitHubApp: &schema.GitHubApp{
AppID: "404",
PrivateKey: bogusKey,
Slug: "test-app",
ClientID: "Iv1.deb0cd1048cf1040",
ClientSecret: "c6d0ed049217a89825c457898c701c30324f873b",
},
},
})
defer conf.Mock(nil)
got, err := getRemoteURLFunc(context.Background(), externalServiceStore, repoStore, doer, "test-repo-1")
require.NoError(t, err)
want := "https://x-access-token:mock-installtion-access-token@github.com/sgtest/test-repo-1"
assert.Equal(t, want, got)
}
func TestGetVCSSyncer(t *testing.T) {
tempReposDir, err := os.MkdirTemp("", "TestGetVCSSyncer")
if err != nil {

View File

@ -1,48 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "app",
srcs = ["app.go"],
importpath = "github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/app",
visibility = ["//enterprise/cmd/frontend:__subpackages__"],
deps = [
"//cmd/frontend/enterprise",
"//cmd/frontend/envvar",
"//enterprise/internal/codeintel",
"//internal/actor",
"//internal/conf",
"//internal/conf/conftypes",
"//internal/database",
"//internal/extsvc",
"//internal/extsvc/github",
"//internal/jsonc",
"//internal/observation",
"//internal/types",
"//lib/errors",
"@com_github_google_go_github_v41//github",
"@com_github_inconshreveable_log15//:log15",
"@com_github_sourcegraph_log//:log",
],
)
go_test(
name = "app_test",
timeout = "short",
srcs = [
"app_test.go",
"mocks_test.go",
],
embed = [":app"],
deps = [
"//internal/actor",
"//internal/conf",
"//internal/database",
"//internal/extsvc",
"//internal/types",
"//schema",
"@com_github_derision_test_go_mockgen//testutil/require",
"@com_github_google_go_github_v41//github",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
],
)

View File

@ -1,229 +0,0 @@
package app
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"strconv"
gogithub "github.com/google/go-github/v41/github"
"github.com/inconshreveable/log15"
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/cmd/frontend/enterprise"
"github.com/sourcegraph/sourcegraph/cmd/frontend/envvar"
"github.com/sourcegraph/sourcegraph/enterprise/internal/codeintel"
"github.com/sourcegraph/sourcegraph/internal/actor"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/conf/conftypes"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/jsonc"
"github.com/sourcegraph/sourcegraph/internal/observation"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
// Init initializes the app endpoints.
func Init(
ctx context.Context,
observationCtx *observation.Context,
db database.DB,
codeIntelServices codeintel.Services,
_ conftypes.UnifiedWatchable,
enterpriseServices *enterprise.Services,
) error {
config, err := conf.GitHubAppConfig()
if err != nil {
return errors.Wrap(err, "getting GitHubApp config")
}
if !config.Configured() {
enterpriseServices.NewGitHubAppSetupHandler = func() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte("Sourcegraph GitHub App setup is not enabled"))
})
}
return nil
}
auther, err := github.NewGitHubAppAuthenticator(config.AppID, config.PrivateKey)
if err != nil {
return errors.Wrap(err, "new authenticator with GitHub App")
}
apiURL, err := url.Parse("https://github.com")
if err != nil {
return errors.Wrap(err, "parse github.com")
}
client := github.NewV3Client(log.Scoped("app.github.v3", "github v3 client for frontend app"), extsvc.URNGitHubApp, apiURL, auther, nil)
enterpriseServices.NewGitHubAppSetupHandler = func() http.Handler {
return newGitHubAppSetupHandler(db, apiURL, client)
}
return nil
}
type githubClient interface {
GetAppInstallation(ctx context.Context, installationID int64) (*gogithub.Installation, error)
}
func isSetupActionValid(setupAction string) bool {
for _, a := range []string{"install", "request"} {
if setupAction == a {
return true
}
}
return false
}
// DescryptWithPrivatekey decrypts a message using a provided private key byte string in PEM format.
func DecryptWithPrivateKey(encodedMsg string, privateKey []byte) (string, error) {
block, _ := pem.Decode(privateKey)
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", errors.Wrap(err, "parse private key")
}
hash := sha256.New()
plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, key, []byte(encodedMsg), nil)
if err != nil {
return "", errors.Wrap(err, "decrypt message")
}
return string(plaintext), nil
}
// EncryptWithPrivatekey encrypts a message using a provided private key byte string in PEM format.
// The public key used for encryption is derived from the provided private key.
func EncryptWithPrivateKey(msg string, privateKey []byte) ([]byte, error) {
block, _ := pem.Decode(privateKey)
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, errors.Wrap(err, "parse private key")
}
hash := sha256.New()
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, &key.PublicKey, []byte(msg), nil)
if err != nil {
return nil, errors.Wrap(err, "encrypt message")
}
return ciphertext, nil
}
func newGitHubAppSetupHandler(db database.DB, apiURL *url.URL, client githubClient) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
responseServerError := func(statusCode int, msg string, err error) {
w.WriteHeader(statusCode)
_, _ = w.Write([]byte(msg))
log15.Error(msg, "error", err)
}
setupAction := r.URL.Query().Get("setup_action")
if !isSetupActionValid(setupAction) {
err := errors.Newf("Invalid setup action %q", setupAction)
responseServerError(http.StatusBadRequest, err.Error(), err)
return
}
// TODO: Double check that this code still works as intended with the
// removal org org-based installations on dotcom
a := actor.FromContext(r.Context())
if !a.IsAuthenticated() || !envvar.SourcegraphDotComMode() {
if setupAction == "install" {
gitHubAppConfig := conf.SiteConfig().GitHubApp
privateKey, err := base64.StdEncoding.DecodeString(gitHubAppConfig.PrivateKey)
if err != nil {
responseServerError(http.StatusBadRequest, "Error while decoding encryption key", err)
return
}
installationID, err := strconv.ParseInt(r.URL.Query().Get("installation_id"), 10, 64)
if err != nil {
responseServerError(http.StatusBadRequest, `The "installation_id" is not a valid integer`, err)
return
}
ins, err := client.GetAppInstallation(r.Context(), installationID)
if err != nil {
responseServerError(http.StatusInternalServerError, `Failed to get the installation information using the "installation_id"`, err)
return
}
var displayName string
if ins.Account.Login != nil {
displayName = fmt.Sprintf("GitHub (%s)", *ins.Account.Login)
}
err = createCodeHostConnectionForInstallation(r.Context(), db, installationID, displayName, apiURL.String())
if err != nil {
responseServerError(http.StatusInternalServerError, "Failed to create code host connection", err)
return
}
encryptedInstallationID, err := EncryptWithPrivateKey(r.URL.Query().Get("installation_id"), privateKey)
if err != nil {
responseServerError(http.StatusBadRequest, "Error while encrypting installation ID", err)
return
}
base64InstallationID := base64.StdEncoding.EncodeToString(encryptedInstallationID)
http.Redirect(w, r, "/install-github-app-success?installation_id="+url.QueryEscape(base64InstallationID), http.StatusFound)
return
}
if setupAction == "request" {
http.Redirect(w, r, "/install-github-app-request", http.StatusFound)
return
}
}
state := r.URL.Query().Get("state")
if state == "" && setupAction == "install" {
http.Redirect(w, r, "/settings", http.StatusFound)
return
}
})
}
func createCodeHostConnectionForInstallation(ctx context.Context, db database.DB, installationID int64, displayName, apiURL string) error {
if displayName == "" {
displayName = "GitHub App"
}
config := extsvc.NewUnencryptedConfig(fmt.Sprintf(`{"url": "%s", "repos": []} `, apiURL))
svc := &types.ExternalService{
Kind: extsvc.KindGitHub,
DisplayName: displayName,
Config: config,
}
currentConfig, err := svc.Config.Decrypt(context.Background())
if err != nil {
return err
}
newConfig, err := jsonc.Edit(currentConfig, strconv.FormatInt(installationID, 10), "githubAppInstallationID")
if err != nil {
return err
}
newConfig, err = jsonc.Edit(newConfig, false, "pending")
if err != nil {
return err
}
svc.Config = extsvc.NewUnencryptedConfig(newConfig)
return db.ExternalServices().Upsert(ctx, svc)
}

View File

@ -1,122 +0,0 @@
package app
import (
"context"
"encoding/base64"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
mockrequire "github.com/derision-test/go-mockgen/testutil/require"
gogithub "github.com/google/go-github/v41/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sgactor "github.com/sourcegraph/sourcegraph/internal/actor"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/schema"
)
func TestNewGitHubAppSetupHandler(t *testing.T) {
const bogusKey = `LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlDWHdJQkFBS0JnUUMvemZJMXRqSlUzbHIxQlFIUHMxYzFvbUNrMFJ0RVVQYXpKTTRYaXEvTmo5ZW13cXhnCmdseVNraEgrU0tKa1hJeXdzTjlBc2hpWm9EOFF1UEtKdy9pQkwrQXNNemU2VmlEa0hoMFMza0hqdGxNVWRlQTMKanMwVFluNnh2TXh1Z3lwTVdKV3BBaS9pdm5Ta3pYNmdtRStjVU4rbDl4aUlWNkx0bGl4M0hla3Nyd0lEQVFBQgpBb0dCQUt3bFp6SVY2RzZMY3c5ZUF4WXJYQ1pqS21KQzJ6b2hnSW1naXVoT0xTTk42cnRkRmVFNG4yVmRmSkRCCkdCOERnYkpEek52Ly9GeEZtdFNqYWV1RDI5QnBBVThvUnQzczBsOXo2K1hkaG5XRzhoNHdDOW83MUJiVTcyUVcKVkIyL0hCTkJMSzBSY1BqV2lvWnp5a3lhQ0dKYnhSemRNV3hMME8xcjJ0MmRtZWRCQWtFQTQ5RmoxVWlWWER5dApKcDVBdkJudk1WUHdjdlI3UnpRNko0RmdydlcwQWRlMzRjSVVPcCtuZm1vaTlZN0dNdGpzS2ZPSWJtZjdnZ3pxCllSWDl1bkQwNXdKQkFOZUlFaDlGSzV3L05lbUpRaXY5bzB6YW9RUXV6WGE3QzdaU3F6RExsaCttWUhVNXBBRFUKalZHS056TnJEaUp6c1NrOWNwb1d0Nk5FdmVHVFNtWkdTUGtDUVFDWFhkQ1BMYUxQbmlFTnY2Z1RVc2Z5Wm1zawpkZnhTMndpb3B2V3VTZUpJTnlRZUErMmM1ZWRMdndsclRtbXg3eDg2NEd5TnJ0a1ZGNi9Dd2ZITHByR1JBa0VBCmxvYnUrUzNxL2szYlRrWlJrNzJwN2tRSERvL05hYTNLeVVSRlVXZnVhaDVkNGFFbkhIbFdWV3R0a0JpbG40UWoKYUFVRlkvNlh0SXlPL050TXE4OU1xUUpCQUpzZ0U4UmlCZXh1aEtLcjZCVjVsSzBMdjU2QlFDaGpkUS84TFFqZAppQWYwYlJ4RE1IS0lzVHFHSW15UzMwVTNvdVkrekxqSVQxb3Fibm0rTFY5VEdtcz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0=`
conf.Mock(&conf.Unified{SiteConfiguration: schema.SiteConfiguration{GitHubApp: &schema.GitHubApp{PrivateKey: bogusKey}}})
defer conf.Mock(nil)
users := database.NewMockUserStore()
users.GetByCurrentAuthUserFunc.SetDefaultReturn(&types.User{}, nil)
externalServices := database.NewMockExternalServiceStore()
db := database.NewMockDB()
db.UsersFunc.SetDefaultReturn(users)
db.ExternalServicesFunc.SetDefaultReturn(externalServices)
apiURL, err := url.Parse("https://github.com")
require.NoError(t, err)
client := NewMockGithubClient()
client.GetAppInstallationFunc.SetDefaultReturn(
&gogithub.Installation{
Account: &gogithub.User{
Login: gogithub.String("abc-org"),
},
},
nil,
)
req, err := http.NewRequest(http.MethodGet, "/.setup/github-app-cloud?installation_id=21994992&setup_action=install&state=T3JnOjE%3D", nil)
require.Nil(t, err)
h := newGitHubAppSetupHandler(db, apiURL, client)
t.Run("user not logged in (no actor in context)", func(t *testing.T) {
resp := httptest.NewRecorder()
h.ServeHTTP(resp, req)
assert.Equal(t, http.StatusFound, resp.Code)
uri, err := url.ParseRequestURI(resp.Header().Get("Location"))
require.Nil(t, err)
queryVals := uri.Query()
installationID := queryVals.Get("installation_id")
decodedKey, err := base64.StdEncoding.DecodeString(bogusKey)
require.Nil(t, err)
installationIDBytes, err := base64.StdEncoding.DecodeString(installationID)
require.Nil(t, err)
decryptedID, err := DecryptWithPrivateKey(string(installationIDBytes), decodedKey)
require.Nil(t, err)
assert.Equal(t, decryptedID, "21994992")
})
t.Run("invalid setup action", func(t *testing.T) {
resp := httptest.NewRecorder()
badReq, err := http.NewRequest(http.MethodGet, "/.setup/github-app-cloud?installation_id=21994992&setup_action=incorrect&state=T3JnOjE%3D", nil)
require.Nil(t, err)
h.ServeHTTP(resp, badReq)
assert.Equal(t, http.StatusBadRequest, resp.Code)
assert.Equal(t, `Invalid setup action "incorrect"`, resp.Body.String())
})
ctx := sgactor.WithActor(req.Context(), &sgactor.Actor{UID: 1})
req = req.WithContext(ctx)
t.Run("create new", func(t *testing.T) {
externalServices.UpsertFunc.SetDefaultHook(func(ctx context.Context, svcs ...*types.ExternalService) error {
require.Len(t, svcs, 1)
svc := svcs[0]
assert.Equal(t, extsvc.KindGitHub, svc.Kind)
assert.Equal(t, "GitHub (abc-org)", svc.DisplayName)
wantConfig := extsvc.NewUnencryptedConfig(`{
"url": "https://github.com",
"repos": [],
"githubAppInstallationID": "21994992",
"pending": false
} `)
assert.Equal(t, wantConfig, svc.Config)
return nil
})
resp := httptest.NewRecorder()
h.ServeHTTP(resp, req)
assert.Equal(t, http.StatusFound, resp.Code)
assert.True(t, strings.Contains(resp.Header().Get("Location"), "/install-github-app-success?installation_id="))
mockrequire.Called(t, externalServices.UpsertFunc)
})
}

View File

@ -1,177 +0,0 @@
// Code generated by go-mockgen 1.3.7; DO NOT EDIT.
//
// This file was generated by running `sg generate` (or `go-mockgen`) at the root of
// this repository. To add additional mocks to this or another package, add a new entry
// to the mockgen.yaml file in the root of this repository.
package app
import (
"context"
"sync"
github "github.com/google/go-github/v41/github"
)
// MockGithubClient is a mock implementation of the githubClient interface
// (from the package
// github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/app)
// used for unit testing.
type MockGithubClient struct {
// GetAppInstallationFunc is an instance of a mock function object
// controlling the behavior of the method GetAppInstallation.
GetAppInstallationFunc *GithubClientGetAppInstallationFunc
}
// NewMockGithubClient creates a new mock of the githubClient interface. All
// methods return zero values for all results, unless overwritten.
func NewMockGithubClient() *MockGithubClient {
return &MockGithubClient{
GetAppInstallationFunc: &GithubClientGetAppInstallationFunc{
defaultHook: func(context.Context, int64) (r0 *github.Installation, r1 error) {
return
},
},
}
}
// NewStrictMockGithubClient creates a new mock of the githubClient
// interface. All methods panic on invocation, unless overwritten.
func NewStrictMockGithubClient() *MockGithubClient {
return &MockGithubClient{
GetAppInstallationFunc: &GithubClientGetAppInstallationFunc{
defaultHook: func(context.Context, int64) (*github.Installation, error) {
panic("unexpected invocation of MockGithubClient.GetAppInstallation")
},
},
}
}
// surrogateMockGithubClient is a copy of the githubClient interface (from
// the package
// github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/app).
// It is redefined here as it is unexported in the source package.
type surrogateMockGithubClient interface {
GetAppInstallation(context.Context, int64) (*github.Installation, error)
}
// NewMockGithubClientFrom creates a new mock of the MockGithubClient
// interface. All methods delegate to the given implementation, unless
// overwritten.
func NewMockGithubClientFrom(i surrogateMockGithubClient) *MockGithubClient {
return &MockGithubClient{
GetAppInstallationFunc: &GithubClientGetAppInstallationFunc{
defaultHook: i.GetAppInstallation,
},
}
}
// GithubClientGetAppInstallationFunc describes the behavior when the
// GetAppInstallation method of the parent MockGithubClient instance is
// invoked.
type GithubClientGetAppInstallationFunc struct {
defaultHook func(context.Context, int64) (*github.Installation, error)
hooks []func(context.Context, int64) (*github.Installation, error)
history []GithubClientGetAppInstallationFuncCall
mutex sync.Mutex
}
// GetAppInstallation delegates to the next hook function in the queue and
// stores the parameter and result values of this invocation.
func (m *MockGithubClient) GetAppInstallation(v0 context.Context, v1 int64) (*github.Installation, error) {
r0, r1 := m.GetAppInstallationFunc.nextHook()(v0, v1)
m.GetAppInstallationFunc.appendCall(GithubClientGetAppInstallationFuncCall{v0, v1, r0, r1})
return r0, r1
}
// SetDefaultHook sets function that is called when the GetAppInstallation
// method of the parent MockGithubClient instance is invoked and the hook
// queue is empty.
func (f *GithubClientGetAppInstallationFunc) SetDefaultHook(hook func(context.Context, int64) (*github.Installation, error)) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// GetAppInstallation method of the parent MockGithubClient instance invokes
// the hook at the front of the queue and discards it. After the queue is
// empty, the default hook function is invoked for any future action.
func (f *GithubClientGetAppInstallationFunc) PushHook(hook func(context.Context, int64) (*github.Installation, error)) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *GithubClientGetAppInstallationFunc) SetDefaultReturn(r0 *github.Installation, r1 error) {
f.SetDefaultHook(func(context.Context, int64) (*github.Installation, error) {
return r0, r1
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *GithubClientGetAppInstallationFunc) PushReturn(r0 *github.Installation, r1 error) {
f.PushHook(func(context.Context, int64) (*github.Installation, error) {
return r0, r1
})
}
func (f *GithubClientGetAppInstallationFunc) nextHook() func(context.Context, int64) (*github.Installation, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *GithubClientGetAppInstallationFunc) appendCall(r0 GithubClientGetAppInstallationFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of GithubClientGetAppInstallationFuncCall
// objects describing the invocations of this function.
func (f *GithubClientGetAppInstallationFunc) History() []GithubClientGetAppInstallationFuncCall {
f.mutex.Lock()
history := make([]GithubClientGetAppInstallationFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// GithubClientGetAppInstallationFuncCall is an object that describes an
// invocation of method GetAppInstallation on an instance of
// MockGithubClient.
type GithubClientGetAppInstallationFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 int64
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 *github.Installation
// Result1 is the value of the 2nd result returned from this method
// invocation.
Result1 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c GithubClientGetAppInstallationFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c GithubClientGetAppInstallationFuncCall) Results() []interface{} {
return []interface{}{c.Result0, c.Result1}
}

View File

@ -16,7 +16,6 @@ go_library(
"//cmd/frontend/auth/providers",
"//cmd/frontend/external/session",
"//cmd/frontend/globals",
"//enterprise/cmd/frontend/internal/app",
"//internal/actor",
"//internal/conf",
"//internal/cookie",

View File

@ -3,8 +3,6 @@ package oauth
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
@ -20,13 +18,9 @@ import (
"github.com/sourcegraph/sourcegraph/cmd/frontend/auth"
"github.com/sourcegraph/sourcegraph/cmd/frontend/auth/providers"
"github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/app"
"github.com/sourcegraph/sourcegraph/internal/actor"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/env"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/httpcli"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/lib/errors"
@ -111,75 +105,6 @@ func newOAuthFlowHandler(serviceType string) http.Handler {
}
p.Callback(p.OAuth2Config()).ServeHTTP(w, req)
}))
mux.Handle("/install-github-app", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if !conf.GitHubAppEnabled() {
http.NotFound(w, req)
return
}
http.Redirect(w, req, "/install-github-app-success", http.StatusFound)
}))
mux.Handle("/get-github-app-installation", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
logger := log.Scoped("get-github-app-installation", "handler for getting github app installations")
var privateKey []byte
var appID string
var err error
gitHubAppConfig := conf.SiteConfig().GitHubApp
privateKey, err = base64.StdEncoding.DecodeString(gitHubAppConfig.PrivateKey)
if err != nil {
logger.Error("Unexpected error while decoding GitHub App private key.", log.Error(err))
http.Error(w, "Unexpected error while fetching installation data.", http.StatusBadRequest)
return
}
appID = gitHubAppConfig.AppID
installationIDQueryUnecoded := req.URL.Query().Get("installation_id")
installationIDParam, err := base64.StdEncoding.DecodeString(installationIDQueryUnecoded)
if err != nil {
logger.Error("Unexpected error while decoding base64 encoded installation ID.", log.Error(err))
http.Error(w, "Unexpected error while fetching installation data.", http.StatusBadRequest)
return
}
installationIDDecoded, err := app.DecryptWithPrivateKey(string(installationIDParam), privateKey)
if err != nil {
logger.Error("Unexpected error while decrypting installation ID.", log.Error(err))
http.Error(w, "Unexpected error while fetching installation data.", http.StatusBadRequest)
return
}
installationID, err := strconv.ParseInt(installationIDDecoded, 10, 64)
if err != nil {
logger.Error("Unexpected error while creating parsing installation ID.", log.Error(err))
http.Error(w, "Unexpected error while fetching installation data.", http.StatusBadRequest)
return
}
auther, err := github.NewGitHubAppAuthenticator(appID, privateKey)
if err != nil {
logger.Error("Unexpected error while creating Auth token.", log.Error(err))
http.Error(w, "Unexpected error while fetching installation data.", http.StatusBadRequest)
return
}
client := github.NewV3Client(logger,
extsvc.URNGitHubApp, &url.URL{Host: "github.com"}, auther, nil)
installation, err := client.GetAppInstallation(req.Context(), installationID)
if err != nil {
logger.Error("Unexpected error while fetching installation.", log.Error(err))
http.Error(w, "Unexpected error while fetching installation data.", http.StatusBadRequest)
return
}
err = json.NewEncoder(w).Encode(installation)
if err != nil {
logger.Error("Failed to encode installation data.", log.Error(err))
}
}))
return mux
}

View File

@ -106,7 +106,7 @@ func TestChangesetCountsOverTimeIntegration(t *testing.T) {
t.Fatalf("Failed to Upsert external service: %s", err)
}
githubSrc, err := repos.NewGithubSource(ctx, logger, db.ExternalServices(), githubExtSvc, cf)
githubSrc, err := repos.NewGithubSource(ctx, logger, githubExtSvc, cf)
if err != nil {
t.Fatal(t)
}

View File

@ -79,7 +79,7 @@ func testGitHubWebhook(db database.DB, userID int32) func(*testing.T) {
t.Fatal(err)
}
githubSrc, err := repos.NewGithubSource(ctx, logtest.Scoped(t), db.ExternalServices(), extSvc, cf)
githubSrc, err := repos.NewGithubSource(ctx, logtest.Scoped(t), extSvc, cf)
if err != nil {
t.Fatal(t)
}

View File

@ -12,7 +12,6 @@ go_library(
"//cmd/frontend/enterprise",
"//cmd/frontend/registry/api",
"//cmd/frontend/shared",
"//enterprise/cmd/frontend/internal/app",
"//enterprise/cmd/frontend/internal/auth",
"//enterprise/cmd/frontend/internal/authz",
"//enterprise/cmd/frontend/internal/batches",

View File

@ -13,7 +13,6 @@ import (
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/cmd/frontend/enterprise"
"github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/app"
"github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/auth"
"github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/authz"
"github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/batches"
@ -46,7 +45,6 @@ import (
type EnterpriseInitializer = func(context.Context, *observation.Context, database.DB, codeintel.Services, conftypes.UnifiedWatchable, *enterprise.Services) error
var initFunctions = map[string]EnterpriseInitializer{
"app": app.Init,
"authz": authz.Init,
"batches": batches.Init,
"codeintel": codeintelinit.Init,

View File

@ -161,7 +161,7 @@ func ProvidersFromConfig(
enableGithubInternalRepoVisibility = ef.EnableGithubInternalRepoVisibility
}
initResult := github.NewAuthzProviders(db, gitHubConns, cfg.SiteConfig().AuthProviders, enableGithubInternalRepoVisibility)
initResult := github.NewAuthzProviders(gitHubConns, cfg.SiteConfig().AuthProviders, enableGithubInternalRepoVisibility)
initResult.Append(gitlab.NewAuthzProviders(db, cfg.SiteConfig(), gitLabConns))
initResult.Append(bitbucketserver.NewAuthzProviders(bitbucketServerConns))
initResult.Append(perforce.NewAuthzProviders(perforceConns))

View File

@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "github",
srcs = [
"app.go",
"authz.go",
"cache.go",
"client.go",
@ -15,12 +14,10 @@ go_library(
"//enterprise/internal/authz/types",
"//enterprise/internal/licensing",
"//internal/authz",
"//internal/conf",
"//internal/database",
"//internal/extsvc",
"//internal/extsvc/auth",
"//internal/extsvc/github",
"//internal/httpcli",
"//internal/rcache",
"//internal/types",
"//lib/errors",
@ -34,7 +31,6 @@ go_test(
name = "github_test",
timeout = "short",
srcs = [
"app_test.go",
"authz_test.go",
"github_test.go",
"mocks_test.go",
@ -44,8 +40,6 @@ go_test(
"//enterprise/internal/licensing",
"//internal/api",
"//internal/authz",
"//internal/conf",
"//internal/database",
"//internal/extsvc",
"//internal/extsvc/auth",
"//internal/extsvc/github",
@ -53,7 +47,6 @@ go_test(
"//lib/errors",
"//schema",
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_github_v31//github",
"@com_github_gregjones_httpcache//:httpcache",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",

View File

@ -1,53 +0,0 @@
package github
import (
"net/url"
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/auth"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/httpcli"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
// newAppProvider creates a new authz Provider for GitHub App.
func newAppProvider(
db database.DB,
svc *types.ExternalService,
urn string,
baseURL *url.URL,
appID string,
privateKey []byte,
installationID int64,
cli httpcli.Doer,
) (*Provider, error) {
apiURL, _ := github.APIRoot(baseURL)
var installationAuther auth.AuthenticatorWithRefresh
if db != nil { // should only be nil when called by ValidateAuthz
var err error
installationAuther, err = database.BuildGitHubAppInstallationAuther(db.ExternalServices(), appID, privateKey, urn, apiURL, cli, installationID, svc)
if err != nil {
return nil, errors.Wrap(err, "new GitHub App installation auther")
}
}
return &Provider{
urn: urn,
codeHost: extsvc.NewCodeHost(baseURL, extsvc.TypeGitHub),
client: func() (client, error) {
logger := log.Scoped("installation", "github client for installation").
With(log.String("appID", appID), log.Int64("installationID", installationID))
return &ClientAdapter{
V3Client: github.NewV3Client(logger, urn, apiURL, installationAuther, cli),
}, nil
},
InstallationID: &installationID,
db: db,
}, nil
}

View File

@ -1,118 +0,0 @@
package github
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"io"
"net/http"
"net/url"
"testing"
"time"
"github.com/google/go-github/v31/github"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/schema"
)
type mockDoer struct {
do func(*http.Request) (*http.Response, error)
}
func (c *mockDoer) Do(r *http.Request) (*http.Response, error) {
return c.do(r)
}
func TestNewAppProvider(t *testing.T) {
srvHit := false
doer := &mockDoer{
do: func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/app/installations/1234/access_tokens" {
tokenString := "app-token"
tokenExpiry := time.Now()
token := github.InstallationToken{
Token: &tokenString,
ExpiresAt: &tokenExpiry,
}
respJSON, err := json.Marshal(token)
require.NoError(t, err)
return &http.Response{
Status: http.StatusText(http.StatusCreated),
StatusCode: http.StatusCreated,
Body: io.NopCloser(bytes.NewReader(respJSON)),
}, nil
}
if r.Header.Get("Authorization") != "Bearer app-token" {
return &http.Response{
Status: http.StatusText(http.StatusUnauthorized),
StatusCode: http.StatusUnauthorized,
Body: io.NopCloser(bytes.NewReader([]byte(`invalid access token`))),
}, nil
}
srvHit = true
assert.Equal(t, "Bearer app-token", r.Header.Get("Authorization"))
return &http.Response{
Status: http.StatusText(http.StatusOK),
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader([]byte(`[]`))),
}, nil
},
}
config, err := json.Marshal(
&schema.GitHubConnection{
Url: schema.DefaultGitHubURL,
Authorization: &schema.GitHubAuthorization{},
GithubAppInstallationID: "1234",
},
)
require.NoError(t, err)
encodedBogusKey := `LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCUEFJQkFBSkJBUEpIaWprdG1UMUlLYUd0YTVFZXAzQVo5Q2VPZUw4alBESUZUN3dRZ0tabXQzRUZxRGhCCk93bitRVUhKdUs5Zm92UkROSmVWTDJvWTVCT0l6NHJ3L0cwQ0F3RUFBUUpCQU1BK0o5Mks0d2NQVllsbWMrM28KcHU5NmlKTkNwMmp5Nm5hK1pEQlQzK0VvSUo1VFJGdnN3R2kvTHUzZThYUWwxTDNTM21ub0xPSlZNcTF0bUxOMgpIY0VDSVFEK3daeS83RlYxUEFtdmlXeWlYVklETzJnNWJOaUJlbmdKQ3hFa3Nia1VtUUloQVBOMlZaczN6UFFwCk1EVG9vTlJXcnl0RW1URERkamdiOFpzTldYL1JPRGIxQWlCZWNKblNVQ05TQllLMXJ5VTFmNURTbitoQU9ZaDkKWDFBMlVnTDE3bWhsS1FJaEFPK2JMNmRDWktpTGZORWxmVnRkTUtxQnFjNlBIK01heFU2VzlkVlFvR1dkQWlFQQptdGZ5cE9zYTFiS2hFTDg0blovaXZFYkJyaVJHalAya3lERHYzUlg0V0JrPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=`
bogusKey, err := base64.StdEncoding.DecodeString(encodedBogusKey)
require.NoError(t, err)
baseURL, err := url.Parse(schema.DefaultGitHubURL)
require.NoError(t, err)
db := database.NewMockDB()
db.ExternalServicesFunc.SetDefaultReturn(database.NewMockExternalServiceStore())
t.Run("with non-nil service", func(t *testing.T) {
svc := &types.ExternalService{
ID: 1,
Kind: extsvc.KindGitHub,
Config: extsvc.NewUnencryptedConfig(string(config)),
}
provider, err := newAppProvider(db, svc, "", baseURL, "1234", bogusKey, 1234, doer)
require.NoError(t, err)
cli, err := provider.client()
require.NoError(t, err)
// call any endpoint so that test server can check Authorization header
_, _, err = cli.ListTeamMembers(context.Background(), "anyOwner", "anyTeam", 0)
require.NoError(t, err)
assert.True(t, srvHit, "hit server endpoint")
})
t.Run("with nil service for validation", func(t *testing.T) {
var svc *types.ExternalService
// just validate that a new provider can be created for validation if svc is nil
_, err = newAppProvider(db, svc, "", baseURL, "1234", bogusKey, 1234, doer)
require.NoError(t, err)
})
}

View File

@ -3,13 +3,10 @@ package github
import (
"fmt"
"net/url"
"strconv"
"time"
atypes "github.com/sourcegraph/sourcegraph/enterprise/internal/authz/types"
"github.com/sourcegraph/sourcegraph/enterprise/internal/licensing"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/lib/errors"
@ -33,7 +30,6 @@ type ExternalConnection struct {
// desired, callers should use `(*Provider).ValidateConnection` directly to get warnings related
// to connection issues.
func NewAuthzProviders(
db database.DB,
conns []*ExternalConnection,
authProviders []schema.AuthProviders,
enableGithubInternalRepoVisibility bool,
@ -59,7 +55,7 @@ func NewAuthzProviders(
for _, c := range conns {
// Initialize authz (permissions) provider.
p, err := newAuthzProvider(db, c)
p, err := newAuthzProvider(c)
if err != nil {
initResults.InvalidConnections = append(initResults.InvalidConnections, extsvc.TypeGitHub)
initResults.Problems = append(initResults.Problems, err.Error())
@ -104,7 +100,6 @@ func NewAuthzProviders(
// newAuthzProvider instantiates a provider, or returns nil if authorization is disabled.
// Errors returned are "serious problems".
func newAuthzProvider(
db database.DB,
c *ExternalConnection,
) (*Provider, error) {
if c.Authorization == nil {
@ -120,23 +115,6 @@ func newAuthzProvider(
return nil, errors.Errorf("could not parse URL for GitHub instance %q: %s", c.Url, err)
}
if c.GithubAppInstallationID != "" {
installationID, err := strconv.ParseInt(c.GithubAppInstallationID, 10, 64)
if err != nil {
return nil, errors.Wrap(err, "parse installation ID")
}
config, err := conf.GitHubAppConfig()
if err != nil {
return nil, err
}
if !config.Configured() {
return nil, errors.Errorf("connection contains an GitHub App installation ID while GitHub App for Sourcegraph is not enabled")
}
return newAppProvider(db, c.ExternalService, c.GitHubConnection.URN, baseURL, config.AppID, config.PrivateKey, installationID, nil)
}
// Disable by default for now
if c.Authorization.GroupsCacheTTL <= 0 {
c.Authorization.GroupsCacheTTL = -1
@ -153,6 +131,6 @@ func newAuthzProvider(
// ValidateAuthz validates the authorization fields of the given GitHub external
// service config.
func ValidateAuthz(c *types.GitHubConnection) error {
_, err := newAuthzProvider(nil, &ExternalConnection{GitHubConnection: c})
_, err := newAuthzProvider(&ExternalConnection{GitHubConnection: c})
return err
}

View File

@ -8,20 +8,14 @@ import (
"github.com/stretchr/testify/require"
"github.com/sourcegraph/sourcegraph/enterprise/internal/licensing"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/schema"
)
func TestNewAuthzProviders(t *testing.T) {
db := database.NewMockDB()
db.ExternalServicesFunc.SetDefaultReturn(database.NewMockExternalServiceStore())
t.Run("no authorization", func(t *testing.T) {
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
GitHubConnection: &types.GitHubConnection{
@ -48,7 +42,6 @@ func TestNewAuthzProviders(t *testing.T) {
t.Run("no matching auth provider", func(t *testing.T) {
t.Cleanup(licensing.TestingSkipFeatureChecks())
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
GitHubConnection: &types.GitHubConnection{
@ -82,7 +75,6 @@ func TestNewAuthzProviders(t *testing.T) {
t.Run("default case", func(t *testing.T) {
t.Cleanup(licensing.TestingSkipFeatureChecks())
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
GitHubConnection: &types.GitHubConnection{
@ -112,7 +104,6 @@ func TestNewAuthzProviders(t *testing.T) {
t.Run("license does not have ACLs feature", func(t *testing.T) {
t.Cleanup(licensing.MockCheckFeatureError("failed"))
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
GitHubConnection: &types.GitHubConnection{
@ -141,7 +132,6 @@ func TestNewAuthzProviders(t *testing.T) {
t.Run("groups cache enabled, but not allowGroupsPermissionsSync", func(t *testing.T) {
t.Cleanup(licensing.TestingSkipFeatureChecks())
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
GitHubConnection: &types.GitHubConnection{
@ -180,10 +170,7 @@ func TestNewAuthzProviders(t *testing.T) {
github.MockGetAuthenticatedOAuthScopes = func(context.Context) ([]string, error) {
return []string{"read:org"}, nil
}
db := database.NewMockDB()
db.ExternalServicesFunc.SetDefaultReturn(database.NewMockExternalServiceStore())
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
GitHubConnection: &types.GitHubConnection{
@ -214,60 +201,5 @@ func TestNewAuthzProviders(t *testing.T) {
assert.Empty(t, initResults.Warnings)
assert.Empty(t, initResults.InvalidConnections)
})
t.Run("github app installation id available", func(t *testing.T) {
t.Cleanup(licensing.TestingSkipFeatureChecks())
const bogusKey = `LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlCUEFJQkFBSkJBUEpIaWprdG1UMUlLYUd0YTVFZXAzQVo5Q2VPZUw4alBESUZUN3dRZ0tabXQzRUZxRGhCCk93bitRVUhKdUs5Zm92UkROSmVWTDJvWTVCT0l6NHJ3L0cwQ0F3RUFBUUpCQU1BK0o5Mks0d2NQVllsbWMrM28KcHU5NmlKTkNwMmp5Nm5hK1pEQlQzK0VvSUo1VFJGdnN3R2kvTHUzZThYUWwxTDNTM21ub0xPSlZNcTF0bUxOMgpIY0VDSVFEK3daeS83RlYxUEFtdmlXeWlYVklETzJnNWJOaUJlbmdKQ3hFa3Nia1VtUUloQVBOMlZaczN6UFFwCk1EVG9vTlJXcnl0RW1URERkamdiOFpzTldYL1JPRGIxQWlCZWNKblNVQ05TQllLMXJ5VTFmNURTbitoQU9ZaDkKWDFBMlVnTDE3bWhsS1FJaEFPK2JMNmRDWktpTGZORWxmVnRkTUtxQnFjNlBIK01heFU2VzlkVlFvR1dkQWlFQQptdGZ5cE9zYTFiS2hFTDg0blovaXZFYkJyaVJHalAya3lERHYzUlg0V0JrPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=`
conf.Mock(&conf.Unified{
SiteConfiguration: schema.SiteConfiguration{
GitHubApp: &schema.GitHubApp{
AppID: "1234",
ClientID: "1234",
ClientSecret: "1234",
Slug: "test-app",
PrivateKey: bogusKey,
},
},
})
defer conf.Mock(nil)
db := database.NewMockDB()
db.ExternalServicesFunc.SetDefaultReturn(database.NewMockExternalServiceStore())
initResults := NewAuthzProviders(
db,
[]*ExternalConnection{
{
ExternalService: &types.ExternalService{
ID: 1,
Kind: extsvc.KindGitHub,
Config: extsvc.NewEmptyConfig(),
},
GitHubConnection: &types.GitHubConnection{
URN: "extsvc:github:1",
GitHubConnection: &schema.GitHubConnection{
Url: schema.DefaultGitHubURL,
Authorization: &schema.GitHubAuthorization{
GroupsCacheTTL: 72,
},
GithubAppInstallationID: "1234",
},
},
},
},
[]schema.AuthProviders{{
// falls back to schema.DefaultGitHubURL
Github: &schema.GitHubAuthProvider{},
}},
false,
)
require.Len(t, initResults.Providers, 1, "expect exactly one provider")
assert.NotNil(t, initResults.Providers[0])
assert.Empty(t, initResults.Problems)
assert.Empty(t, initResults.Warnings)
assert.Empty(t, initResults.InvalidConnections)
})
})
}

View File

@ -37,8 +37,6 @@ type Provider struct {
// become the default behaviour.
enableGithubInternalRepoVisibility bool
InstallationID *int64
db database.DB
}
@ -346,12 +344,6 @@ func (p *Provider) FetchUserPerms(ctx context.Context, account *extsvc.Account,
Expiry: tok.Expiry,
}
if p.InstallationID != nil && p.db != nil {
// Only used if created by newAppProvider
oauthToken.RefreshFunc = database.GetAccountRefreshAndStoreOAuthTokenFunc(p.db, account.ID, github.GetOAuthContext(p.codeHost.BaseURL.String()))
oauthToken.NeedsRefreshBuffer = 5
}
return p.fetchUserPermsByToken(ctx, extsvc.AccountID(account.AccountID), oauthToken, opts)
}
@ -439,9 +431,6 @@ func (p *Provider) FetchRepoPerms(ctx context.Context, repo *extsvc.Repository,
for _, u := range users {
userID := strconv.FormatInt(u.DatabaseID, 10)
if p.InstallationID != nil {
userID = strconv.FormatInt(*p.InstallationID, 10) + "/" + userID
}
addUserToRepoPerms(extsvc.AccountID(userID))
}

View File

@ -20,7 +20,7 @@ type EnterpriseDB interface {
Perms() PermsStore
SubRepoPerms() SubRepoPermsStore
Codeowners() CodeownersStore
GithubApps() gha.GithubAppsStore
GitHubApps() gha.GitHubAppsStore
}
func NewEnterpriseDB(db database.DB) EnterpriseDB {
@ -54,8 +54,8 @@ func (edb *enterpriseDB) Codeowners() CodeownersStore {
return CodeownersWith(basestore.NewWithHandle(edb.Handle()))
}
func (edb *enterpriseDB) GithubApps() gha.GithubAppsStore {
return gha.GithubAppsWith(basestore.NewWithHandle(edb.Handle()))
func (edb *enterpriseDB) GitHubApps() gha.GitHubAppsStore {
return gha.GitHubAppsWith(basestore.NewWithHandle(edb.Handle()))
}
type InsightsDB interface {

View File

@ -666,11 +666,11 @@ func TestValidateExternalServiceConfig(t *testing.T) {
},
{
kind: extsvc.KindGitHub,
desc: "without url, token, githubAppInstallationID, repositoryQuery, repos nor orgs",
desc: "without url, token, repositoryQuery, repos nor orgs",
config: `{}`,
assert: includes(
"url is required",
"at least one of token or githubAppInstallationID must be set",
"token must be set",
"at least one of repositoryQuery, repos or orgs must be set",
),
},
@ -685,17 +685,6 @@ func TestValidateExternalServiceConfig(t *testing.T) {
}`,
assert: equals(`<nil>`),
},
{
kind: extsvc.KindGitHub,
desc: "with url, githubAppInstallationID, repos",
config: `
{
"url": "https://github.corp.com",
"githubAppInstallationID": "21994992",
"repos": [],
}`,
assert: equals(`<nil>`),
},
{
kind: extsvc.KindGitHub,
desc: "with url, token, repos",

View File

@ -7881,9 +7881,9 @@ type MockEnterpriseDB struct {
// FeatureFlagsFunc is an instance of a mock function object controlling
// the behavior of the method FeatureFlags.
FeatureFlagsFunc *EnterpriseDBFeatureFlagsFunc
// GithubAppsFunc is an instance of a mock function object controlling
// the behavior of the method GithubApps.
GithubAppsFunc *EnterpriseDBGithubAppsFunc
// GitHubAppsFunc is an instance of a mock function object controlling
// the behavior of the method GitHubApps.
GitHubAppsFunc *EnterpriseDBGitHubAppsFunc
// GitserverLocalCloneFunc is an instance of a mock function object
// controlling the behavior of the method GitserverLocalClone.
GitserverLocalCloneFunc *EnterpriseDBGitserverLocalCloneFunc
@ -8080,8 +8080,8 @@ func NewMockEnterpriseDB() *MockEnterpriseDB {
return
},
},
GithubAppsFunc: &EnterpriseDBGithubAppsFunc{
defaultHook: func() (r0 store.GithubAppsStore) {
GitHubAppsFunc: &EnterpriseDBGitHubAppsFunc{
defaultHook: func() (r0 store.GitHubAppsStore) {
return
},
},
@ -8362,9 +8362,9 @@ func NewStrictMockEnterpriseDB() *MockEnterpriseDB {
panic("unexpected invocation of MockEnterpriseDB.FeatureFlags")
},
},
GithubAppsFunc: &EnterpriseDBGithubAppsFunc{
defaultHook: func() store.GithubAppsStore {
panic("unexpected invocation of MockEnterpriseDB.GithubApps")
GitHubAppsFunc: &EnterpriseDBGitHubAppsFunc{
defaultHook: func() store.GitHubAppsStore {
panic("unexpected invocation of MockEnterpriseDB.GitHubApps")
},
},
GitserverLocalCloneFunc: &EnterpriseDBGitserverLocalCloneFunc{
@ -8617,8 +8617,8 @@ func NewMockEnterpriseDBFrom(i EnterpriseDB) *MockEnterpriseDB {
FeatureFlagsFunc: &EnterpriseDBFeatureFlagsFunc{
defaultHook: i.FeatureFlags,
},
GithubAppsFunc: &EnterpriseDBGithubAppsFunc{
defaultHook: i.GithubApps,
GitHubAppsFunc: &EnterpriseDBGitHubAppsFunc{
defaultHook: i.GitHubApps,
},
GitserverLocalCloneFunc: &EnterpriseDBGitserverLocalCloneFunc{
defaultHook: i.GitserverLocalClone,
@ -10159,35 +10159,35 @@ func (c EnterpriseDBFeatureFlagsFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}
// EnterpriseDBGithubAppsFunc describes the behavior when the GithubApps
// EnterpriseDBGitHubAppsFunc describes the behavior when the GitHubApps
// method of the parent MockEnterpriseDB instance is invoked.
type EnterpriseDBGithubAppsFunc struct {
defaultHook func() store.GithubAppsStore
hooks []func() store.GithubAppsStore
history []EnterpriseDBGithubAppsFuncCall
type EnterpriseDBGitHubAppsFunc struct {
defaultHook func() store.GitHubAppsStore
hooks []func() store.GitHubAppsStore
history []EnterpriseDBGitHubAppsFuncCall
mutex sync.Mutex
}
// GithubApps delegates to the next hook function in the queue and stores
// GitHubApps delegates to the next hook function in the queue and stores
// the parameter and result values of this invocation.
func (m *MockEnterpriseDB) GithubApps() store.GithubAppsStore {
r0 := m.GithubAppsFunc.nextHook()()
m.GithubAppsFunc.appendCall(EnterpriseDBGithubAppsFuncCall{r0})
func (m *MockEnterpriseDB) GitHubApps() store.GitHubAppsStore {
r0 := m.GitHubAppsFunc.nextHook()()
m.GitHubAppsFunc.appendCall(EnterpriseDBGitHubAppsFuncCall{r0})
return r0
}
// SetDefaultHook sets function that is called when the GithubApps method of
// SetDefaultHook sets function that is called when the GitHubApps method of
// the parent MockEnterpriseDB instance is invoked and the hook queue is
// empty.
func (f *EnterpriseDBGithubAppsFunc) SetDefaultHook(hook func() store.GithubAppsStore) {
func (f *EnterpriseDBGitHubAppsFunc) SetDefaultHook(hook func() store.GitHubAppsStore) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// GithubApps method of the parent MockEnterpriseDB instance invokes the
// GitHubApps method of the parent MockEnterpriseDB instance invokes the
// hook at the front of the queue and discards it. After the queue is empty,
// the default hook function is invoked for any future action.
func (f *EnterpriseDBGithubAppsFunc) PushHook(hook func() store.GithubAppsStore) {
func (f *EnterpriseDBGitHubAppsFunc) PushHook(hook func() store.GitHubAppsStore) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
@ -10195,20 +10195,20 @@ func (f *EnterpriseDBGithubAppsFunc) PushHook(hook func() store.GithubAppsStore)
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *EnterpriseDBGithubAppsFunc) SetDefaultReturn(r0 store.GithubAppsStore) {
f.SetDefaultHook(func() store.GithubAppsStore {
func (f *EnterpriseDBGitHubAppsFunc) SetDefaultReturn(r0 store.GitHubAppsStore) {
f.SetDefaultHook(func() store.GitHubAppsStore {
return r0
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *EnterpriseDBGithubAppsFunc) PushReturn(r0 store.GithubAppsStore) {
f.PushHook(func() store.GithubAppsStore {
func (f *EnterpriseDBGitHubAppsFunc) PushReturn(r0 store.GitHubAppsStore) {
f.PushHook(func() store.GitHubAppsStore {
return r0
})
}
func (f *EnterpriseDBGithubAppsFunc) nextHook() func() store.GithubAppsStore {
func (f *EnterpriseDBGitHubAppsFunc) nextHook() func() store.GitHubAppsStore {
f.mutex.Lock()
defer f.mutex.Unlock()
@ -10221,40 +10221,40 @@ func (f *EnterpriseDBGithubAppsFunc) nextHook() func() store.GithubAppsStore {
return hook
}
func (f *EnterpriseDBGithubAppsFunc) appendCall(r0 EnterpriseDBGithubAppsFuncCall) {
func (f *EnterpriseDBGitHubAppsFunc) appendCall(r0 EnterpriseDBGitHubAppsFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of EnterpriseDBGithubAppsFuncCall objects
// History returns a sequence of EnterpriseDBGitHubAppsFuncCall objects
// describing the invocations of this function.
func (f *EnterpriseDBGithubAppsFunc) History() []EnterpriseDBGithubAppsFuncCall {
func (f *EnterpriseDBGitHubAppsFunc) History() []EnterpriseDBGitHubAppsFuncCall {
f.mutex.Lock()
history := make([]EnterpriseDBGithubAppsFuncCall, len(f.history))
history := make([]EnterpriseDBGitHubAppsFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// EnterpriseDBGithubAppsFuncCall is an object that describes an invocation
// of method GithubApps on an instance of MockEnterpriseDB.
type EnterpriseDBGithubAppsFuncCall struct {
// EnterpriseDBGitHubAppsFuncCall is an object that describes an invocation
// of method GitHubApps on an instance of MockEnterpriseDB.
type EnterpriseDBGitHubAppsFuncCall struct {
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 store.GithubAppsStore
Result0 store.GitHubAppsStore
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c EnterpriseDBGithubAppsFuncCall) Args() []interface{} {
func (c EnterpriseDBGitHubAppsFuncCall) Args() []interface{} {
return []interface{}{}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c EnterpriseDBGithubAppsFuncCall) Results() []interface{} {
func (c EnterpriseDBGitHubAppsFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}

View File

@ -13,8 +13,8 @@ import (
"github.com/sourcegraph/sourcegraph/lib/errors"
)
// Store handles storing and retrieving GitHub Apps from the database.
type GithubAppsStore interface {
// GitHubAppsStore handles storing and retrieving GitHub Apps from the database.
type GitHubAppsStore interface {
// Create inserts a new GitHub App into the database.
Create(ctx context.Context, app *types.GitHubApp) error
@ -34,28 +34,28 @@ type GithubAppsStore interface {
GetBySlug(ctx context.Context, slug string, baseURL string) (*types.GitHubApp, error)
// WithEncryptionKey sets encryption key on store. Returns a new GithubAppStore
WithEncryptionKey(key encryption.Key) GithubAppsStore
WithEncryptionKey(key encryption.Key) GitHubAppsStore
}
// githubAppStore handles storing and retrieving GitHub Apps from the database.
type githubAppsStore struct {
// gitHubAppStore handles storing and retrieving GitHub Apps from the database.
type gitHubAppsStore struct {
*basestore.Store
key encryption.Key
}
func GithubAppsWith(other *basestore.Store) GithubAppsStore {
return &githubAppsStore{
func GitHubAppsWith(other *basestore.Store) GitHubAppsStore {
return &gitHubAppsStore{
Store: basestore.NewWithHandle(other.Handle()),
}
}
// WithEncryptionKey sets encryption key on store. Returns a new GithubAppStore
func (s *githubAppsStore) WithEncryptionKey(key encryption.Key) GithubAppsStore {
return &githubAppsStore{Store: s.Store, key: key}
func (s *gitHubAppsStore) WithEncryptionKey(key encryption.Key) GitHubAppsStore {
return &gitHubAppsStore{Store: s.Store, key: key}
}
func (s *githubAppsStore) getEncryptionKey() encryption.Key {
func (s *gitHubAppsStore) getEncryptionKey() encryption.Key {
if s.key != nil {
return s.key
}
@ -63,7 +63,7 @@ func (s *githubAppsStore) getEncryptionKey() encryption.Key {
}
// Create inserts a new GitHub App into the database.
func (s *githubAppsStore) Create(ctx context.Context, app *types.GitHubApp) error {
func (s *gitHubAppsStore) Create(ctx context.Context, app *types.GitHubApp) error {
key := s.getEncryptionKey()
clientSecret, _, err := encryption.MaybeEncrypt(ctx, key, app.ClientSecret)
if err != nil {
@ -82,7 +82,7 @@ func (s *githubAppsStore) Create(ctx context.Context, app *types.GitHubApp) erro
}
// Delete removes a GitHub App from the database by ID.
func (s *githubAppsStore) Delete(ctx context.Context, id int) error {
func (s *gitHubAppsStore) Delete(ctx context.Context, id int) error {
query := sqlf.Sprintf(`DELETE FROM github_apps WHERE id = %s`, id)
return s.Exec(ctx, query)
}
@ -106,7 +106,7 @@ var scanGithubApp = basestore.NewFirstScanner(func(s dbutil.Scanner) (*types.Git
return &app, err
})
func (s *githubAppsStore) decrypt(ctx context.Context, app *types.GitHubApp) (*types.GitHubApp, error) {
func (s *gitHubAppsStore) decrypt(ctx context.Context, app *types.GitHubApp) (*types.GitHubApp, error) {
key := s.getEncryptionKey()
var cs, pk string
@ -125,7 +125,7 @@ func (s *githubAppsStore) decrypt(ctx context.Context, app *types.GitHubApp) (*t
}
// Update updates a GitHub App in the database and returns the updated struct.
func (s *githubAppsStore) Update(ctx context.Context, id int, app *types.GitHubApp) (*types.GitHubApp, error) {
func (s *gitHubAppsStore) Update(ctx context.Context, id int, app *types.GitHubApp) (*types.GitHubApp, error) {
key := s.getEncryptionKey()
clientSecret, _, err := encryption.MaybeEncrypt(ctx, key, app.ClientSecret)
if err != nil {
@ -151,7 +151,7 @@ func (s *githubAppsStore) Update(ctx context.Context, id int, app *types.GitHubA
return s.decrypt(ctx, app)
}
func (s *githubAppsStore) get(ctx context.Context, where *sqlf.Query) (*types.GitHubApp, error) {
func (s *gitHubAppsStore) get(ctx context.Context, where *sqlf.Query) (*types.GitHubApp, error) {
var selectQuery = `SELECT
id,
app_id,
@ -181,16 +181,16 @@ func (s *githubAppsStore) get(ctx context.Context, where *sqlf.Query) (*types.Gi
}
// GetByID retrieves a GitHub App from the database by ID.
func (s *githubAppsStore) GetByID(ctx context.Context, id int) (*types.GitHubApp, error) {
func (s *gitHubAppsStore) GetByID(ctx context.Context, id int) (*types.GitHubApp, error) {
return s.get(ctx, sqlf.Sprintf(`id = %s`, id))
}
// GetByAppID retrieves a GitHub App from the database by appID and base url
func (s *githubAppsStore) GetByAppID(ctx context.Context, appID int, baseURL string) (*types.GitHubApp, error) {
func (s *gitHubAppsStore) GetByAppID(ctx context.Context, appID int, baseURL string) (*types.GitHubApp, error) {
return s.get(ctx, sqlf.Sprintf(`app_id = %s AND base_url = %s`, appID, baseURL))
}
// GetBySlug retrieves a GitHub App from the database by slug and base url
func (s *githubAppsStore) GetBySlug(ctx context.Context, slug string, baseURL string) (*types.GitHubApp, error) {
func (s *gitHubAppsStore) GetBySlug(ctx context.Context, slug string, baseURL string) (*types.GitHubApp, error) {
return s.get(ctx, sqlf.Sprintf(`slug = %s AND base_url = %s`, slug, baseURL))
}

View File

@ -21,7 +21,7 @@ func TestCreateGitHubApp(t *testing.T) {
}
logger := logtest.Scoped(t)
db := database.NewDB(logger, dbtest.NewDB(logger, t))
store := &githubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
store := &gitHubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
ctx := context.Background()
app := &types.GitHubApp{
@ -60,7 +60,7 @@ func TestDeleteGitHubApp(t *testing.T) {
}
logger := logtest.Scoped(t)
db := database.NewDB(logger, dbtest.NewDB(logger, t))
store := githubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
store := gitHubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
ctx := context.Background()
app := &types.GitHubApp{
@ -95,7 +95,7 @@ func TestUpdateGitHubApp(t *testing.T) {
}
logger := logtest.Scoped(t)
db := database.NewDB(logger, dbtest.NewDB(logger, t))
store := githubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
store := gitHubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
ctx := context.Background()
app := &types.GitHubApp{
@ -149,7 +149,7 @@ func TestGetByID(t *testing.T) {
}
logger := logtest.Scoped(t)
db := database.NewDB(logger, dbtest.NewDB(logger, t))
store := &githubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
store := &gitHubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
ctx := context.Background()
app1 := &types.GitHubApp{
@ -207,7 +207,7 @@ func TestGetByAppID(t *testing.T) {
}
logger := logtest.Scoped(t)
db := database.NewDB(logger, dbtest.NewDB(logger, t))
store := &githubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
store := &gitHubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
ctx := context.Background()
app1 := &types.GitHubApp{
@ -266,7 +266,7 @@ func TestGetBySlug(t *testing.T) {
}
logger := logtest.Scoped(t)
db := database.NewDB(logger, dbtest.NewDB(logger, t))
store := &githubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
store := &gitHubAppsStore{Store: basestore.NewWithHandle(db.Handle())}
ctx := context.Background()
app1 := &types.GitHubApp{

View File

@ -22,7 +22,6 @@ go_library(
"external_services.go",
"feature_flags.go",
"gen.go",
"github_app_helper.go",
"gitserver_localclone_jobs.go",
"gitserver_repos.go",
"global_state.go",

View File

@ -445,8 +445,8 @@ func validateGitHubConnection(githubValidators []func(*types.GitHubConnection) e
)
}
if c.Token == "" && c.GithubAppInstallationID == "" {
err = errors.Append(err, errors.New("at least one of token or githubAppInstallationID must be set"))
if c.Token == "" {
err = errors.Append(err, errors.New("token must be set"))
}
if c.Repos == nil && c.RepositoryQuery == nil && c.Orgs == nil {
err = errors.Append(err, errors.New("at least one of repositoryQuery, repos or orgs must be set"))

View File

@ -76,9 +76,9 @@ func TestExternalServicesListOptions_sqlConditions(t *testing.T) {
},
{
name: "has after updated_at",
updatedAfter: time.Date(2013, 04, 19, 0, 0, 0, 0, time.UTC),
updatedAfter: time.Date(2013, 0o4, 19, 0, 0, 0, 0, time.UTC),
wantQuery: "deleted_at IS NULL AND updated_at > $1",
wantArgs: []any{time.Date(2013, 04, 19, 0, 0, 0, 0, time.UTC)},
wantArgs: []any{time.Date(2013, 0o4, 19, 0, 0, 0, 0, time.UTC)},
},
{
name: "has OnlyCloudDefault",
@ -148,7 +148,7 @@ func TestExternalServicesStore_ValidateConfig(t *testing.T) {
name: "2 errors",
kind: extsvc.KindGitHub,
config: `{"url": "https://github.com", "repositoryQuery": ["none"], "token": ""}`,
wantErr: "2 errors occurred:\n\t* token: String length must be greater than or equal to 1\n\t* at least one of token or githubAppInstallationID must be set",
wantErr: "2 errors occurred:\n\t* token: String length must be greater than or equal to 1\n\t* token must be set",
},
{
name: "no conflicting rate limit",
@ -1801,7 +1801,6 @@ func TestExternalServicesStore_Upsert(t *testing.T) {
t.Fatalf("Wanted an error")
}
})
})
t.Run("one external service", func(t *testing.T) {

View File

@ -1,100 +0,0 @@
package database
import (
"context"
"net/url"
"time"
"github.com/sourcegraph/sourcegraph/schema"
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/extsvc/auth"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
"github.com/sourcegraph/sourcegraph/internal/httpcli"
"github.com/sourcegraph/sourcegraph/internal/jsonc"
"github.com/sourcegraph/sourcegraph/internal/types"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
func getGitHubAppInstallationRefreshFunc(externalServiceStore ExternalServiceStore, installationID int64, svc *types.ExternalService, appClient *github.V3Client) func(context.Context, httpcli.Doer) (string, time.Time, error) {
return func(ctx context.Context, cli httpcli.Doer) (string, time.Time, error) {
token, err := appClient.CreateAppInstallationAccessToken(ctx, installationID)
if err != nil {
return "", time.Time{}, err
}
rawConfig, err := svc.Config.Decrypt(context.Background())
if err != nil {
return "", time.Time{}, err
}
rawConfig, err = jsonc.Edit(rawConfig, token.GetToken(), "token")
if err != nil {
return "", time.Time{}, err
}
externalServiceStore.Update(context.Background(),
conf.Get().AuthProviders,
svc.ID,
&ExternalServiceUpdate{
Config: &rawConfig,
TokenExpiresAt: token.ExpiresAt,
},
)
return *token.Token, token.GetExpiresAt(), nil
}
}
// BuildGitHubAppInstallationAuther builds a GitHub App Installation authenticator.
// First it creates a GitHub App Authenticator, as it is required to create App Installation
// Access Tokens. The App Authenticator is used in the refresh function of the
// Installation Authenticator, as installation tokens cannot be refreshed on their own.
// The App Authenticator is used to generate a new token once it expires.
func BuildGitHubAppInstallationAuther(
externalServiceStore ExternalServiceStore,
appID string,
pkey []byte,
urn string,
apiURL *url.URL,
cli httpcli.Doer,
installationID int64,
svc *types.ExternalService,
) (auth.AuthenticatorWithRefresh, error) {
if svc == nil {
return nil, nil
}
rawConfig, err := svc.Config.Decrypt(context.Background())
if err != nil {
return nil, errors.Errorf("external service id=%d config error: %s", svc.ID, err)
}
var c schema.GitHubConnection
if err := jsonc.Unmarshal(rawConfig, &c); err != nil {
return nil, errors.Errorf("external service id=%d config error: %s", svc.ID, err)
}
appAuther, err := github.NewGitHubAppAuthenticator(appID, pkey)
if err != nil {
return nil, errors.Wrap(err, "new authenticator with GitHub App")
}
appClient := github.NewV3Client(
log.Scoped("app", "github client for github app").
With(log.String("appID", appID)),
urn, apiURL, appAuther, cli)
expiry := time.Time{}
if svc.TokenExpiresAt != nil {
expiry = *svc.TokenExpiresAt
}
return github.NewGitHubAppInstallationAuthenticator(
installationID,
c.Token,
expiry,
getGitHubAppInstallationRefreshFunc(externalServiceStore, installationID, svc, appClient),
)
}

View File

@ -99,8 +99,8 @@ func NewRing(ctx context.Context, keyConfig *schema.EncryptionKeys) (*Ring, erro
}
}
if keyConfig.GithubAppKey != nil {
r.GithubAppKey, err = NewKey(ctx, keyConfig.GithubAppKey, keyConfig)
if keyConfig.GitHubAppKey != nil {
r.GithubAppKey, err = NewKey(ctx, keyConfig.GitHubAppKey, keyConfig)
if err != nil {
return nil, err
}

View File

@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "github",
srcs = [
"authenticators.go",
"common.go",
"doc.go",
"v3.go",
@ -25,7 +24,6 @@ go_library(
"//internal/ratelimit",
"//internal/trace/ot",
"//lib/errors",
"@com_github_golang_jwt_jwt_v4//:jwt",
"@com_github_google_go_github//github",
"@com_github_google_go_github_v41//github",
"@com_github_graphql_go_graphql//language/ast",

View File

@ -1,121 +0,0 @@
package github
import (
"context"
"crypto/rsa"
"crypto/sha256"
"encoding/hex"
"net/http"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/sourcegraph/sourcegraph/internal/extsvc/auth"
"github.com/sourcegraph/sourcegraph/internal/httpcli"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
// gitHubAppAuthenticator implements OAuth Bearer Token authentication for
// GitHub Apps.
type gitHubAppAuthenticator struct {
appID string
key *rsa.PrivateKey
rawKey []byte
}
// NewGitHubAppAuthenticator constructs a new OAuth Bearer Token
// authenticator for GitHub Apps using given appID and private key.
func NewGitHubAppAuthenticator(appID string, privateKey []byte) (auth.Authenticator, error) {
key, err := jwt.ParseRSAPrivateKeyFromPEM(privateKey)
if err != nil {
return nil, errors.Wrap(err, "parse private key")
}
return &gitHubAppAuthenticator{
appID: appID,
key: key,
rawKey: privateKey,
}, nil
}
// Authenticate is a modified version of
// https://github.com/bradleyfalzon/ghinstallation/blob/24e56b3fb7669f209134a01eff731d7e2ef72a5c/appsTransport.go#L66.
func (token *gitHubAppAuthenticator) Authenticate(r *http.Request) error {
// The payload computation is following GitHub App's Ruby example shown in
// https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-a-github-app.
//
// NOTE: GitHub rejects expiry and issue timestamps that are not an integer,
// while the jwt-go library serializes to fractional timestamps. Truncate them
// before passing to jwt-go.
iss := time.Now().Add(-time.Minute).Truncate(time.Second)
exp := iss.Add(10 * time.Minute)
claims := &jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(iss),
ExpiresAt: jwt.NewNumericDate(exp),
Issuer: token.appID,
}
bearer := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
signedString, err := bearer.SignedString(token.key)
if err != nil {
return errors.Wrap(err, "sign JWT")
}
r.Header.Set("Authorization", "Bearer "+signedString)
return nil
}
func (token *gitHubAppAuthenticator) Hash() string {
shaSum := sha256.Sum256(token.rawKey)
return hex.EncodeToString(shaSum[:])
}
// GitHubAppInstallationAuthenticator implements OAuth Bearer Token authentication for
// GitHub Apps.
type GitHubAppInstallationAuthenticator struct {
installationID int64
InstallationAccessToken string
Expiry time.Time
refreshFunc func(context.Context, httpcli.Doer) (string, time.Time, error)
}
// NewGitHubAppAuthenticator constructs a new OAuth Bearer Token
// authenticator for GitHub Apps using given appID and private key.
func NewGitHubAppInstallationAuthenticator(installationID int64, installationAccessToken string, expiry time.Time, refreshFunc func(context.Context, httpcli.Doer) (string, time.Time, error)) (auth.AuthenticatorWithRefresh, error) {
return &GitHubAppInstallationAuthenticator{
installationID: installationID,
InstallationAccessToken: installationAccessToken,
Expiry: expiry,
refreshFunc: refreshFunc,
}, nil
}
func (token *GitHubAppInstallationAuthenticator) Authenticate(r *http.Request) error {
r.Header.Set("Authorization", "Bearer "+token.InstallationAccessToken)
return nil
}
func (token *GitHubAppInstallationAuthenticator) Hash() string {
shaSum := sha256.Sum256([]byte(token.InstallationAccessToken))
return hex.EncodeToString(shaSum[:])
}
func (token *GitHubAppInstallationAuthenticator) NeedsRefresh() bool {
if !token.Expiry.IsZero() {
return time.Until(token.Expiry) < 5*time.Minute
}
// If no expiry is set we default to False
return false
}
func (token *GitHubAppInstallationAuthenticator) Refresh(ctx context.Context, cli httpcli.Doer) error {
newToken, newExpiry, err := token.refreshFunc(ctx, cli)
if err != nil {
return err
}
token.InstallationAccessToken = newToken
token.Expiry = newExpiry
return nil
}

View File

@ -219,11 +219,8 @@ func githubCloneURL(logger log.Logger, repo *github.Repository, cfg *schema.GitH
return repo.URL, nil
}
if cfg.GithubAppInstallationID != "" {
u.User = url.UserPassword("x-access-token", cfg.Token)
} else {
u.User = url.UserPassword("oauth2", cfg.Token)
}
u.User = url.UserPassword("oauth2", cfg.Token)
return u.String(), nil
}

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/url"
"strconv"
"strings"
"time"
@ -15,9 +14,7 @@ import (
"github.com/sourcegraph/log"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/conf/reposource"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/auth"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
@ -48,10 +45,6 @@ type GitHubSource struct {
// for an originalHostname of github.com).
originalHostname string
// useGitHubApp indicate whether clients are authenticated through GitHub App,
// which may need to hit different API endpoints from regular RESTful API.
useGitHubApp bool
logger log.Logger
}
@ -63,7 +56,7 @@ var (
)
// NewGithubSource returns a new GitHubSource from the given external service.
func NewGithubSource(ctx context.Context, logger log.Logger, externalServicesStore database.ExternalServiceStore, svc *types.ExternalService, cf *httpcli.Factory) (*GitHubSource, error) {
func NewGithubSource(ctx context.Context, logger log.Logger, svc *types.ExternalService, cf *httpcli.Factory) (*GitHubSource, error) {
rawConfig, err := svc.Config.Decrypt(ctx)
if err != nil {
return nil, errors.Errorf("external service id=%d config error: %s", svc.ID, err)
@ -72,7 +65,7 @@ func NewGithubSource(ctx context.Context, logger log.Logger, externalServicesSto
if err := jsonc.Unmarshal(rawConfig, &c); err != nil {
return nil, errors.Errorf("external service id=%d config error: %s", svc.ID, err)
}
return newGithubSource(logger, externalServicesStore, svc, &c, cf)
return newGithubSource(logger, svc, &c, cf)
}
var githubRemainingGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{
@ -88,7 +81,6 @@ var githubRatelimitWaitCounter = promauto.NewCounterVec(prometheus.CounterOpts{
func newGithubSource(
logger log.Logger,
externalServicesStore database.ExternalServiceStore,
svc *types.ExternalService,
c *schema.GitHubConnection,
cf *httpcli.Factory,
@ -166,27 +158,6 @@ func newGithubSource(
searchClient = github.NewV3SearchClient(searchClientLogger, urn, apiURL, token, cli)
)
useGitHubApp := false
config, err := conf.GitHubAppConfig()
if err != nil {
return nil, err
}
if c.GithubAppInstallationID != "" && config.Configured() {
installationID, err := strconv.ParseInt(c.GithubAppInstallationID, 10, 64)
if err != nil {
return nil, errors.Wrap(err, "parse installation ID")
}
installationAuther, err := database.BuildGitHubAppInstallationAuther(externalServicesStore, config.AppID, config.PrivateKey, urn, apiURL, cli, installationID, svc)
if err != nil {
return nil, errors.Wrap(err, "creating GitHub App installation authenticator")
}
v3Client = github.NewV3Client(v3ClientLogger, urn, apiURL, installationAuther, cli)
v4Client = github.NewV4Client(urn, apiURL, installationAuther, cli)
useGitHubApp = true
}
for resource, monitor := range map[string]*ratelimit.Monitor{
"rest": v3Client.ExternalRateLimiter(),
"graphql": v4Client.ExternalRateLimiter(),
@ -217,28 +188,17 @@ func newGithubSource(
v4Client: v4Client,
searchClient: searchClient,
originalHostname: originalHostname,
useGitHubApp: useGitHubApp,
logger: logger.With(
log.Object("GitHubSource",
log.Bool("excludeForks", excludeForks),
log.Bool("githubDotCom", githubDotCom),
log.String("originalHostname", originalHostname),
log.Bool("useGitHubApp", useGitHubApp),
),
),
}, nil
}
func (s *GitHubSource) WithAuthenticator(a auth.Authenticator) (Source, error) {
switch a.(type) {
case *auth.OAuthBearerToken,
*auth.OAuthBearerTokenWithSSH:
break
default:
return nil, newUnsupportedAuthenticatorError("GitHubSource", a)
}
sc := *s
sc.v3Client = sc.v3Client.WithAuthenticator(a)
sc.v4Client = sc.v4Client.WithAuthenticator(a)
@ -254,13 +214,7 @@ type githubResult struct {
func (s *GitHubSource) ValidateAuthenticator(ctx context.Context) error {
var err error
if s.config.GithubAppInstallationID != "" {
// GitHub App does not have an affiliated user, use another
// request instead.
_, err = s.v3Client.GetAuthenticatedOAuthScopes(ctx)
} else {
_, err = s.v3Client.GetAuthenticatedUser(ctx)
}
_, err = s.v3Client.GetAuthenticatedUser(ctx)
return err
}
@ -768,9 +722,6 @@ func (s *GitHubSource) listAffiliated(ctx context.Context, results chan *githubR
log.Duration("retryAfter", retry),
)
}()
if s.useGitHubApp {
return s.v3Client.ListInstallationRepositories(ctx, page)
}
return s.v3Client.ListAffiliatedRepositories(ctx, github.VisibilityAll, page, 100)
})
}
@ -1100,7 +1051,6 @@ func (q *repositoryQuery) doRecursively(ctx context.Context, results chan *githu
// to avoid hitting the GitHub search API 1000 results limit, which would cause
// use to miss matches.
func (s *repositoryQuery) Refine() bool {
if s.Created.Size() < 2*time.Second {
// Can't refine further than 1 second
return false
@ -1274,11 +1224,7 @@ func (s *GitHubSource) AffiliatedRepositories(ctx context.Context) ([]types.Code
}
var repos []*github.Repository
if s.useGitHubApp {
repos, hasNextPage, _, err = s.v3Client.ListInstallationRepositories(ctx, page)
} else {
repos, hasNextPage, _, err = s.v3Client.ListAffiliatedRepositories(ctx, github.VisibilityAll, page, 100)
}
repos, hasNextPage, _, err = s.v3Client.ListAffiliatedRepositories(ctx, github.VisibilityAll, page, 100)
if err != nil {
return nil, err
}

View File

@ -23,7 +23,6 @@ import (
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/conf"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/extsvc"
"github.com/sourcegraph/sourcegraph/internal/extsvc/auth"
"github.com/sourcegraph/sourcegraph/internal/extsvc/github"
@ -205,7 +204,7 @@ func TestGithubSource_GetRepo(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), database.NewMockExternalServiceStore(), svc, cf)
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), svc, cf)
if err != nil {
t.Fatal(err)
}
@ -315,7 +314,7 @@ func TestGithubSource_GetRepo_Enterprise(t *testing.T) {
defer save(t)
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), database.NewMockExternalServiceStore(), svc, cf)
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), svc, cf)
if err != nil {
t.Fatal(err)
}
@ -349,7 +348,7 @@ func TestMakeRepo_NullCharacter(t *testing.T) {
schema := &schema.GitHubConnection{
Url: "https://github.com",
}
s, err := newGithubSource(logtest.Scoped(t), database.NewMockExternalServiceStore(), &svc, schema, nil)
s, err := newGithubSource(logtest.Scoped(t), &svc, schema, nil)
require.NoError(t, err)
repo := s.makeRepo(r)
@ -404,8 +403,7 @@ func TestGithubSource_makeRepo(t *testing.T) {
for _, test := range tests {
test.name = "GithubSource_makeRepo_" + test.name
t.Run(test.name, func(t *testing.T) {
s, err := newGithubSource(logtest.Scoped(t), database.NewMockExternalServiceStore(), &svc, test.schema, nil)
s, err := newGithubSource(logtest.Scoped(t), &svc, test.schema, nil)
if err != nil {
t.Fatal(err)
}
@ -679,7 +677,7 @@ func TestGithubSource_ListRepos(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), database.NewMockExternalServiceStore(), svc, cf)
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), svc, cf)
if err != nil {
t.Fatal(err)
}
@ -715,7 +713,7 @@ func TestGithubSource_WithAuthenticator(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), database.NewMockExternalServiceStore(), svc, nil)
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), svc, nil)
if err != nil {
t.Fatal(err)
}
@ -732,26 +730,6 @@ func TestGithubSource_WithAuthenticator(t *testing.T) {
t.Error("unexpected nil Source")
}
})
t.Run("unsupported", func(t *testing.T) {
for name, tc := range map[string]auth.Authenticator{
"nil": nil,
"BasicAuth": &auth.BasicAuth{},
"OAuthClient": &auth.OAuthClient{},
} {
t.Run(name, func(t *testing.T) {
src, err := githubSrc.WithAuthenticator(tc)
if err == nil {
t.Error("unexpected nil error")
} else if !errors.HasType(err, UnsupportedAuthenticatorError{}) {
t.Errorf("unexpected error of type %T: %v", err, err)
}
if src != nil {
t.Errorf("expected non-nil Source: %v", src)
}
})
}
})
}
func TestGithubSource_excludes_disabledAndLocked(t *testing.T) {
@ -764,7 +742,7 @@ func TestGithubSource_excludes_disabledAndLocked(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), database.NewMockExternalServiceStore(), svc, nil)
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), svc, nil)
if err != nil {
t.Fatal(err)
}
@ -791,7 +769,7 @@ func TestGithubSource_GetVersion(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logger, database.NewMockExternalServiceStore(), svc, nil)
githubSrc, err := NewGithubSource(ctx, logger, svc, nil)
if err != nil {
t.Fatal(err)
}
@ -827,7 +805,7 @@ func TestGithubSource_GetVersion(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logger, database.NewMockExternalServiceStore(), svc, cf)
githubSrc, err := NewGithubSource(ctx, logger, svc, cf)
if err != nil {
t.Fatal(err)
}
@ -1150,7 +1128,7 @@ func TestGithubSource_SearchRepositories(t *testing.T) {
}
ctx := context.Background()
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), database.NewMockExternalServiceStore(), svc, cf)
githubSrc, err := NewGithubSource(ctx, logtest.Scoped(t), svc, cf)
if err != nil {
t.Fatal(err)
}

View File

@ -42,11 +42,9 @@ func NewSourcer(logger log.Logger, db database.DB, cf *httpcli.Factory, decs ...
// NewSource returns a repository yielding Source from the given ExternalService configuration.
func NewSource(ctx context.Context, logger log.Logger, db database.DB, svc *types.ExternalService, cf *httpcli.Factory) (Source, error) {
externalServicesStore := db.ExternalServices()
switch strings.ToUpper(svc.Kind) {
case extsvc.KindGitHub:
return NewGithubSource(ctx, logger.Scoped("GithubSource", "GitHub repo source"), externalServicesStore, svc, cf)
return NewGithubSource(ctx, logger.Scoped("GithubSource", "GitHub repo source"), svc, cf)
case extsvc.KindGitLab:
return NewGitLabSource(ctx, logger.Scoped("GitLabSource", "GitLab repo source"), svc, cf)
case extsvc.KindAzureDevOps:

View File

@ -81,10 +81,6 @@
- path: github.com/sourcegraph/sourcegraph/enterprise/cmd/executor/internal/util
interfaces:
- CmdRunner
- filename: enterprise/cmd/frontend/internal/app/mocks_test.go
path: github.com/sourcegraph/sourcegraph/enterprise/cmd/frontend/internal/app
interfaces:
- githubClient
- filename: enterprise/internal/codeintel/uploads/transport/http/mocks_test.go
path: github.com/sourcegraph/sourcegraph/internal/uploadhandler
interfaces:

View File

@ -173,7 +173,7 @@
}
},
"githubAppInstallationID": {
"description": "The installation ID of the GitHub App.",
"description": "DEPRECATED: The installation ID of the GitHub App.",
"type": "string"
},
"pending": {

View File

@ -676,7 +676,7 @@ type EncryptionKeys struct {
EnableCache bool `json:"enableCache,omitempty"`
ExecutorSecretKey *EncryptionKey `json:"executorSecretKey,omitempty"`
ExternalServiceKey *EncryptionKey `json:"externalServiceKey,omitempty"`
GithubAppKey *EncryptionKey `json:"githubAppKey,omitempty"`
GitHubAppKey *EncryptionKey `json:"gitHubAppKey,omitempty"`
OutboundWebhookKey *EncryptionKey `json:"outboundWebhookKey,omitempty"`
UserExternalAccountKey *EncryptionKey `json:"userExternalAccountKey,omitempty"`
WebhookKey *EncryptionKey `json:"webhookKey,omitempty"`
@ -989,7 +989,7 @@ type GitCommitDescription struct {
Version int `json:"version,omitempty"`
}
// GitHubApp description: The config options for Sourcegraph GitHub App.
// GitHubApp description: DEPRECATED: The config options for Sourcegraph GitHub App.
type GitHubApp struct {
// AppID description: The app ID of the GitHub App for Sourcegraph.
AppID string `json:"appID,omitempty"`
@ -1054,7 +1054,7 @@ type GitHubConnection struct {
//
// If "ssh", Sourcegraph will access GitHub repositories using Git URLs of the form git@github.com:myteam/myproject.git. See the documentation for how to provide SSH private keys and known_hosts: https://docs.sourcegraph.com/admin/repo/auth#repositories-that-need-http-s-or-ssh-authentication.
GitURLType string `json:"gitURLType,omitempty"`
// GithubAppInstallationID description: The installation ID of the GitHub App.
// GithubAppInstallationID description: DEPRECATED: The installation ID of the GitHub App.
GithubAppInstallationID string `json:"githubAppInstallationID,omitempty"`
// InitialRepositoryEnablement description: Deprecated and ignored field which will be removed entirely in the next release. GitHub repositories can no longer be enabled or disabled explicitly. Configure repositories to be mirrored via "repos", "exclude" and "repositoryQuery" instead.
InitialRepositoryEnablement bool `json:"initialRepositoryEnablement,omitempty"`
@ -2413,7 +2413,7 @@ type SiteConfiguration struct {
ExternalURL string `json:"externalURL,omitempty"`
// GitCloneURLToRepositoryName description: JSON array of configuration that maps from Git clone URL to repository name. Sourcegraph automatically resolves remote clone URLs to their proper code host. However, there may be non-remote clone URLs (e.g., in submodule declarations) that Sourcegraph cannot automatically map to a code host. In this case, use this field to specify the mapping. The mappings are tried in the order they are specified and take precedence over automatic mappings.
GitCloneURLToRepositoryName []*CloneURLToRepositoryName `json:"git.cloneURLToRepositoryName,omitempty"`
// GitHubApp description: The config options for Sourcegraph GitHub App.
// GitHubApp description: DEPRECATED: The config options for Sourcegraph GitHub App.
GitHubApp *GitHubApp `json:"gitHubApp,omitempty"`
// GitLongCommandTimeout description: Maximum number of seconds that a long Git command (e.g. clone or remote update) is allowed to execute. The default is 3600 seconds, or 1 hour.
GitLongCommandTimeout int `json:"gitLongCommandTimeout,omitempty"`

View File

@ -1577,7 +1577,7 @@
"group": "Sourcegraph Enterprise license"
},
"gitHubApp": {
"description": "The config options for Sourcegraph GitHub App.",
"description": "DEPRECATED: The config options for Sourcegraph GitHub App.",
"type": "object",
"properties": {
"slug": {
@ -1931,7 +1931,7 @@
"externalServiceKey": {
"$ref": "#/definitions/EncryptionKey"
},
"githubAppKey": {
"gitHubAppKey": {
"$ref": "#/definitions/EncryptionKey"
},
"outboundWebhookKey": {