mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
github auth: use org id to verify creds (#13332)
* github auth: use org id to verify creds * add check for required org param; add test case * update UTs * add nil check for org * add changelog * fix typo in ut * set org ID if it is unset; add more ut coverage * add optional organization_id * move client instantiation * refactor parse URL; add UT for setting org ID * fix comment in UT * add nil check * don't update org name on change; return warning * refactor verifyCredentials * error when unable to fetch org ID on config write; add warnings * fix bug in log message * update UT and small refactor * update comments and log msg * use getter for org ID
This commit is contained in:
committed by
GitHub
parent
9b86b8cf90
commit
524ded982b
214
builtin/credential/github/path_config_test.go
Normal file
214
builtin/credential/github/path_config_test.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
|
||||
t.Helper()
|
||||
config := logical.TestBackendConfig()
|
||||
config.StorageView = &logical.InmemStorage{}
|
||||
|
||||
b := Backend()
|
||||
if b == nil {
|
||||
t.Fatalf("failed to create backend")
|
||||
}
|
||||
err := b.Backend.Setup(context.Background(), config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return b, config.StorageView
|
||||
}
|
||||
|
||||
// setupTestServer configures httptest server to intercept and respond to the
|
||||
// request to base_url
|
||||
func setupTestServer(t *testing.T) *httptest.Server {
|
||||
t.Helper()
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var resp string
|
||||
if strings.Contains(r.URL.String(), "/user/orgs") {
|
||||
resp = string(listOrgResponse)
|
||||
} else if strings.Contains(r.URL.String(), "/user/teams") {
|
||||
resp = string(listUserTeamsResponse)
|
||||
} else if strings.Contains(r.URL.String(), "/user") {
|
||||
resp = getUserResponse
|
||||
} else if strings.Contains(r.URL.String(), "/orgs/") {
|
||||
resp = getOrgResponse
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintln(w, resp)
|
||||
}))
|
||||
}
|
||||
|
||||
// TestGitHub_WriteReadConfig tests that we can successfully read and write
|
||||
// the github auth config
|
||||
func TestGitHub_WriteReadConfig(t *testing.T) {
|
||||
b, s := createBackendWithStorage(t)
|
||||
|
||||
// use a test server to return our mock GH org info
|
||||
ts := setupTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
// Write the config
|
||||
resp, err := b.HandleRequest(context.Background(), &logical.Request{
|
||||
Path: "config",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"organization": "foo-org",
|
||||
"base_url": ts.URL, // base_url will call the test server
|
||||
},
|
||||
Storage: s,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, resp)
|
||||
assert.NoError(t, resp.Error())
|
||||
|
||||
// Read the config
|
||||
resp, err = b.HandleRequest(context.Background(), &logical.Request{
|
||||
Path: "config",
|
||||
Operation: logical.ReadOperation,
|
||||
Storage: s,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Error())
|
||||
|
||||
// the ID should be set, we grab it from the GET /orgs API
|
||||
assert.Equal(t, int64(12345), resp.Data["organization_id"])
|
||||
assert.Equal(t, "foo-org", resp.Data["organization"])
|
||||
}
|
||||
|
||||
// TestGitHub_WriteReadConfig_OrgID tests that we can successfully read and
|
||||
// write the github auth config with an organization_id param
|
||||
func TestGitHub_WriteReadConfig_OrgID(t *testing.T) {
|
||||
b, s := createBackendWithStorage(t)
|
||||
|
||||
// Write the config and pass in organization_id
|
||||
resp, err := b.HandleRequest(context.Background(), &logical.Request{
|
||||
Path: "config",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"organization": "foo-org",
|
||||
"organization_id": 98765,
|
||||
},
|
||||
Storage: s,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, resp)
|
||||
assert.NoError(t, resp.Error())
|
||||
|
||||
// Read the config
|
||||
resp, err = b.HandleRequest(context.Background(), &logical.Request{
|
||||
Path: "config",
|
||||
Operation: logical.ReadOperation,
|
||||
Storage: s,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Error())
|
||||
|
||||
// the ID should be set to what was written in the config
|
||||
assert.Equal(t, int64(98765), resp.Data["organization_id"])
|
||||
assert.Equal(t, "foo-org", resp.Data["organization"])
|
||||
}
|
||||
|
||||
// TestGitHub_ErrorNoOrgID tests that an error is returned when we cannot fetch
|
||||
// the org ID for the given org name
|
||||
func TestGitHub_ErrorNoOrgID(t *testing.T) {
|
||||
b, s := createBackendWithStorage(t)
|
||||
// use a test server to return our mock GH org info
|
||||
ts := func() *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
resp := `{ "id": 0 }`
|
||||
fmt.Fprintln(w, resp)
|
||||
}))
|
||||
}
|
||||
|
||||
defer ts().Close()
|
||||
|
||||
// Write the config
|
||||
resp, err := b.HandleRequest(context.Background(), &logical.Request{
|
||||
Path: "config",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"organization": "foo-org",
|
||||
"base_url": ts().URL, // base_url will call the test server
|
||||
},
|
||||
Storage: s,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, resp)
|
||||
assert.Equal(t, errors.New(
|
||||
"unable to fetch the organization_id, you must manually set it in the config: organization_id not found for foo-org",
|
||||
), err)
|
||||
}
|
||||
|
||||
// TestGitHub_WriteConfig_ErrorNoOrg tests that an error is returned when the
|
||||
// required "organization" parameter is not provided
|
||||
func TestGitHub_WriteConfig_ErrorNoOrg(t *testing.T) {
|
||||
b, s := createBackendWithStorage(t)
|
||||
|
||||
// Write the config
|
||||
resp, err := b.HandleRequest(context.Background(), &logical.Request{
|
||||
Path: "config",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{},
|
||||
Storage: s,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Error(t, resp.Error())
|
||||
assert.Equal(t, errors.New("organization is a required parameter"), resp.Error())
|
||||
}
|
||||
|
||||
// https://docs.github.com/en/rest/reference/users#get-the-authenticated-user
|
||||
// Note: many of the fields have been omitted
|
||||
var getUserResponse = `
|
||||
{
|
||||
"login": "user-foo",
|
||||
"id": 6789,
|
||||
"description": "A great user. The very best user.",
|
||||
"name": "foo name",
|
||||
"company": "foo-company",
|
||||
"type": "User"
|
||||
}
|
||||
`
|
||||
|
||||
// https://docs.github.com/en/rest/reference/orgs#get-an-organization
|
||||
// Note: many of the fields have been omitted, we only care about 'login' and 'id'
|
||||
var getOrgResponse = `
|
||||
{
|
||||
"login": "foo-org",
|
||||
"id": 12345,
|
||||
"description": "A great org. The very best org.",
|
||||
"name": "foo-display-name",
|
||||
"company": "foo-company",
|
||||
"type": "Organization"
|
||||
}
|
||||
`
|
||||
|
||||
// https://docs.github.com/en/rest/reference/orgs#list-organizations-for-the-authenticated-user
|
||||
var listOrgResponse = []byte(fmt.Sprintf(`[%v]`, getOrgResponse))
|
||||
|
||||
// https://docs.github.com/en/rest/reference/teams#list-teams-for-the-authenticated-user
|
||||
// Note: many of the fields have been omitted
|
||||
var listUserTeamsResponse = []byte(fmt.Sprintf(`[
|
||||
{
|
||||
"id": 1,
|
||||
"node_id": "MDQ6VGVhbTE=",
|
||||
"name": "Foo team",
|
||||
"slug": "foo-team",
|
||||
"description": "A great team. The very best team.",
|
||||
"permission": "admin",
|
||||
"organization": %v
|
||||
}
|
||||
]`, getOrgResponse))
|
||||
Reference in New Issue
Block a user