11 Commits

Author SHA1 Message Date
Florian Wagner
b4b91e9993 Merge pull request #60 from actions/h2floh/ghae_internal_repo
Some checks failed
goreleaser / goreleaser (push) Has been cancelled
Pass visibility flag on repository creation
2022-11-04 15:21:02 +09:00
Florian Wagner
e30fad8050 adding missing error handling for GET repo
Signed-off-by: Florian Wagner <h2floh@github.com>
2022-11-04 06:15:14 +00:00
Florian Wagner
d7e1ea845e exclude gocyclo lint in E2E mock API server
Signed-off-by: Florian Wagner <h2floh@github.com>
2022-11-04 04:52:59 +00:00
Florian Wagner
51dff542d6 add e2e test for impersonations and visibility
Signed-off-by: Florian Wagner <h2floh@github.com>
2022-11-04 04:44:35 +00:00
Florian Wagner
ef922b8e3b refactor repository retrieval/creation
Signed-off-by: Florian Wagner <h2floh@github.com>
2022-11-04 04:43:03 +00:00
Florian Wagner
6d732db1cf add push flag to indicate target it GHAE
Signed-off-by: Florian Wagner <h2floh@github.com>
2022-11-03 06:56:42 +00:00
Florian Wagner
86c578bdf2 set visibility to internal for GHAE
Signed-off-by: Florian Wagner <h2floh@github.com>
2022-11-03 06:15:23 +00:00
Ajay
621a30f411 Merge pull request #52 from actions/ajaykn/fix-token-error
Some checks failed
goreleaser / goreleaser (push) Has been cancelled
Fix the impersonation token error when site_admin scope is missing
2022-09-26 13:58:39 +09:00
Ajaykn-mac
0ae35c149f fix go lint 2022-09-26 13:28:19 +09:00
Ajaykn-mac
4f5ae77ae9 update readme with moving optional argument 2022-09-26 13:12:54 +09:00
Ajaykn-mac
db79671e1d Fix impersonation token error in case of missing site_admin scope 2022-09-26 12:59:57 +09:00
4 changed files with 103 additions and 27 deletions

View File

@@ -24,8 +24,6 @@ When there are machines which have access to both the public internet and the GH
**Arguments:**
- `actions-admin-user` _(optional)_
The name of the Actions admin user, which will be used for updating the chosen action. To use the default user, pass `actions-admin`. If not set, the impersonation is disabled. Note that `site_admin` scope is required in the token for the impersonation to work.
- `cache-dir` _(required)_
The directory in which to cache repositories as they are synced. This speeds up re-syncing.
- `destination-url` _(required)_
@@ -38,6 +36,8 @@ When there are machines which have access to both the public internet and the GH
A comma-separated list of repositories to be synced. Each entry follows the format of `repo-name`.
- `repo-name-list-file` _(optional)_
A path to a file containing a newline separated list of repositories to be synced. Each entry follows the format of `repo-name`.
- `actions-admin-user` _(optional)_
The name of the Actions admin user, which will be used for updating the chosen action. To use the default user, pass `actions-admin`. If not set, the impersonation is disabled. Note that `site_admin` scope is required in the token for the impersonation to work.
**Example Usage:**
@@ -86,8 +86,6 @@ When no machine has access to both the public internet and the GHES instance:
**Arguments:**
- `actions-admin-user` _(optional)_
The name of the Actions admin user, which will be used for updating the chosen action. To use the default user, pass `actions-admin`. If not set, the impersonation is disabled. Note that `site_admin` scope is required in the token for the impersonation to work.
- `cache-dir` _(required)_
The directory containing the repositories fetched using the `pull` command.
- `destination-url` _(required)_
@@ -96,6 +94,8 @@ When no machine has access to both the public internet and the GHES instance:
A personal access token to authenticate against the GHES instance when uploading repositories. See [Destination token scopes](#destination-token-scopes) below.
- `repo-name`, `repo-name-list` or `repo-name-list-file` _(optional)_
Limit push to specific repositories in the cache directory.
- `actions-admin-user` _(optional)_
The name of the Actions admin user, which will be used for updating the chosen action. To use the default user, pass `actions-admin`. If not set, the impersonation is disabled. Note that `site_admin` scope is required in the token for the impersonation to work.
**Example Usage:**

View File

@@ -123,6 +123,16 @@ function test_push() {
push "pushing to authenticated user's account"
assert_dest_sha "monalisa/new-repo" "heads/main" "e9009d51dd6da2c363d1d14779c53dd27fcb0c52" "updating monalisa/new-repo"
# Push to GHAE with impersonation
setup_cache "org-already-exists/ghae-repo:heads/main:e9009d51dd6da2c363d1d14779c53dd27fcb0c52"
setup_dest "org-already-exists/ghae-repo:heads/main:a5984bb887dd2fcdc2892cd906d6f004844d1142"
push_impersonation "ghae-admin" "pushing to GHAE repo"
# Push to GHES with impersonation
setup_cache "org-already-exists/ghes-repo:heads/main:e9009d51dd6da2c363d1d14779c53dd27fcb0c52"
setup_dest "org-already-exists/ghes-repo:heads/main:a5984bb887dd2fcdc2892cd906d6f004844d1142"
push_impersonation "ghes-admin" "pushing to GHES repo"
echo "all push tests passed successfully"
}
@@ -313,6 +323,17 @@ function push2args() {
fail $3
}
function push_impersonation() {
bin/actions-sync push \
--cache-dir "test/tmp/cache" \
--disable-push-git-auth \
--destination-token "token" \
--destination-url "http://localhost:$DEST_API_PORT" \
--actions-admin-user $1 \
&> $OUTPUT ||
fail "$2"
}
function sync() {
bin/actions-sync sync \
--cache-dir "test/tmp/cache" \

View File

@@ -82,7 +82,7 @@ func GetImpersonationToken(ctx context.Context, flags *PushFlags) (string, error
fmt.Printf("these are the scopes we have for the current token `%s` ...\n", scopesHeader)
if !strings.Contains(scopesHeader, "site_admin") {
return "", errors.Wrap(err, "the current token doesn't have the `site_admin` scope, the impersonation function requires the `site_admin` permission to be able to impersonate.")
return "", errors.New("the current token doesn't have the `site_admin` scope, the impersonation function requires the `site_admin` permission to be able to impersonate")
}
isAE := rootResponse.Header.Get(enterpriseVersionHeaderKey) == enterpriseAegisVersionHeaderValue
@@ -179,21 +179,16 @@ func PushWithGitImpl(ctx context.Context, flags *PushFlags, repoName string, ghC
}
func getOrCreateGitHubRepo(ctx context.Context, client *github.Client, repoName, ownerName string) (*github.Repository, error) {
repo := &github.Repository{
Name: github.String(repoName),
HasIssues: github.Bool(false),
HasWiki: github.Bool(false),
HasPages: github.Bool(false),
HasProjects: github.Bool(false),
}
currentUser, _, err := client.Users.Get(ctx, "")
// retrieve user associated to authentication credentials provided
currentUser, userResponse, err := client.Users.Get(ctx, "")
if err != nil {
return nil, errors.Wrap(err, "error retrieving authenticated user")
}
if currentUser == nil || currentUser.Login == nil {
return nil, errors.New("error retrieving authenticated user's login name")
}
// checking if we talk to GHAE
isAE := userResponse.Header.Get(enterpriseVersionHeaderKey) == enterpriseAegisVersionHeaderValue
// check if the owner refers to the authenticated user or an organization.
var createRepoOrgName string
@@ -209,15 +204,36 @@ func getOrCreateGitHubRepo(ctx context.Context, client *github.Client, repoName,
}
}
ghRepo, resp, err := client.Repositories.Create(ctx, createRepoOrgName, repo)
// check if repository already exists
ghRepo, resp, err := client.Repositories.Get(ctx, ownerName, repoName)
if err == nil {
fmt.Printf("Created repo `%s/%s`\n", ownerName, repoName)
} else if resp != nil && resp.StatusCode == 422 {
ghRepo, _, err = client.Repositories.Get(ctx, ownerName, repoName)
}
if err != nil {
fmt.Printf("Existing repo `%s/%s`\n", ownerName, repoName)
} else if resp != nil && resp.StatusCode == 404 {
// repo not existing yet - try to create
visibility := github.String("public")
if isAE {
visibility = github.String("internal")
}
repo := &github.Repository{
Name: github.String(repoName),
HasIssues: github.Bool(false),
HasWiki: github.Bool(false),
HasPages: github.Bool(false),
HasProjects: github.Bool(false),
Visibility: visibility,
}
ghRepo, _, err = client.Repositories.Create(ctx, createRepoOrgName, repo)
if err == nil {
fmt.Printf("Created repo `%s/%s`\n", ownerName, repoName)
} else {
return nil, errors.Wrapf(err, "error creating repository %s/%s", ownerName, repoName)
}
} else if err != nil {
return nil, errors.Wrapf(err, "error creating repository %s/%s", ownerName, repoName)
}
if ghRepo == nil {
return nil, errors.New("error repository is nil")
}

View File

@@ -8,15 +8,20 @@ import (
"io/ioutil"
"net/http"
"path"
"strings"
"github.com/google/go-github/v43/github"
"github.com/gorilla/mux"
)
var authenticatedLogin string = "monalisa"
var existingOrg string = "org-already-exists"
var existingRepo string = "repo-already-exists"
const existingOrg string = "org-already-exists"
const existingRepo string = "repo-already-exists"
const ghaeRepo string = "ghae-repo"
const xOAuthScopesHeader = "X-OAuth-Scopes"
//nolint:gocyclo
func main() {
var port, gitDaemonURL string
flag.StringVar(&port, "p", "", "")
@@ -28,9 +33,14 @@ func main() {
r.HandleFunc("/api/v3", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("x-github-enterprise-version", "GitHub AE")
w.Header().Set(xOAuthScopesHeader, "site_admin")
})
r.HandleFunc("/api/v3/user", func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if strings.Contains(token, "ghaetoken") {
w.Header().Set("x-github-enterprise-version", "GitHub AE")
}
currentUser := github.User{Login: &authenticatedLogin}
b, _ := json.Marshal(currentUser)
_, err := w.Write(b)
@@ -39,8 +49,7 @@ func main() {
}
})
r.HandleFunc("/api/v3/admin/users/actions-admin/authorizations", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("x-github-enterprise-version", "GitHub AE")
r.HandleFunc("/api/v3/admin/users/ghes-admin/authorizations", func(w http.ResponseWriter, r *http.Request) {
token := "token"
auth := github.Authorization{Token: &token}
b, _ := json.Marshal(auth)
@@ -50,6 +59,17 @@ func main() {
}
}).Methods("POST")
r.HandleFunc("/api/v3/admin/users/ghae-admin/authorizations", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("x-github-enterprise-version", "GitHub AE")
token := "ghaetoken"
auth := github.Authorization{Token: &token}
b, _ := json.Marshal(auth)
_, err := w.Write(b)
if err != nil {
panic(err)
}
}).Methods("POST")
r.HandleFunc("/api/v3/admin/organizations", func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
@@ -114,16 +134,35 @@ func main() {
panic(err)
}
var repoReq struct {
Name string `json:"name,omitempty"`
Name string `json:"name,omitempty"`
Visibility string `json:"visibility,omitempty"`
}
err = json.Unmarshal(b, &repoReq)
if err != nil {
panic(err)
}
if repoReq.Name == "repo-already-exists" {
var errString string = ""
// check visibility requirements
if repoReq.Name == ghaeRepo {
if repoReq.Visibility != "internal" {
errString = fmt.Sprintf("Provided repo visibility %s for GHAE must be internal", repoReq.Visibility)
}
} else {
if repoReq.Visibility != "public" {
errString = fmt.Sprintf("Provided repo visibility %s for GHES must be public", repoReq.Visibility)
}
}
// check if we are testing existing Repo
if repoReq.Name == existingRepo {
errString = fmt.Sprintf("Repo %s already exists", html.EscapeString(repoReq.Name))
}
// if there is an error throw it back
if errString != "" {
w.WriteHeader(http.StatusUnprocessableEntity)
_, err := w.Write([]byte(fmt.Sprintf("Repo %s already exists", html.EscapeString(repoReq.Name))))
_, err := w.Write([]byte(errString))
if err != nil {
panic(err)
}