1
0

Compare commits

...

2 Commits

Author SHA1 Message Date
7448f0e392 cleanup ui jwt/auth providers 2024-07-01 22:03:36 +03:00
28978d545b remove auth/github 2024-07-01 22:03:36 +03:00
28 changed files with 3 additions and 1747 deletions

View File

@ -1,129 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"net/url"
"github.com/google/go-github/github"
cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
"golang.org/x/oauth2"
)
const operationPrefixGithub = "github"
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
b := Backend()
if err := b.Setup(ctx, conf); err != nil {
return nil, err
}
return b, nil
}
func Backend() *backend {
var b backend
b.TeamMap = &framework.PolicyMap{
PathMap: framework.PathMap{
Name: "teams",
},
DefaultKey: "default",
}
teamMapPaths := b.TeamMap.Paths()
teamMapPaths[0].DisplayAttrs = &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
OperationSuffix: "teams",
}
teamMapPaths[1].DisplayAttrs = &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
OperationSuffix: "team-mapping",
}
b.UserMap = &framework.PolicyMap{
PathMap: framework.PathMap{
Name: "users",
},
DefaultKey: "default",
}
userMapPaths := b.UserMap.Paths()
userMapPaths[0].DisplayAttrs = &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
OperationSuffix: "users",
}
userMapPaths[1].DisplayAttrs = &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
OperationSuffix: "user-mapping",
}
allPaths := append(teamMapPaths, userMapPaths...)
b.Backend = &framework.Backend{
Help: backendHelp,
PathsSpecial: &logical.Paths{
Unauthenticated: []string{
"login",
},
},
Paths: append([]*framework.Path{pathConfig(&b), pathLogin(&b)}, allPaths...),
AuthRenew: b.pathLoginRenew,
BackendType: logical.TypeCredential,
}
return &b
}
type backend struct {
*framework.Backend
TeamMap *framework.PolicyMap
UserMap *framework.PolicyMap
}
// Client returns the GitHub client to communicate to GitHub via the
// configured settings.
func (b *backend) Client(token string) (*github.Client, error) {
tc := cleanhttp.DefaultClient()
if token != "" {
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, tc)
tc = oauth2.NewClient(ctx, &tokenSource{Value: token})
}
client := github.NewClient(tc)
emptyUrl, err := url.Parse("")
if err != nil {
return nil, err
}
client.UploadURL = emptyUrl
return client, nil
}
// tokenSource is an oauth2.TokenSource implementation.
type tokenSource struct {
Value string
}
func (t *tokenSource) Token() (*oauth2.Token, error) {
return &oauth2.Token{AccessToken: t.Value}, nil
}
const backendHelp = `
The GitHub credential provider allows authentication via GitHub.
Users provide a personal access token to log in, and the credential
provider verifies they're part of the correct organization and then
maps the user to a set of Vault policies according to the teams they're
part of.
After enabling the credential provider, use the "config" route to
configure it.
`

View File

@ -1,213 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"errors"
"fmt"
"os"
"strings"
"testing"
"time"
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
"github.com/hashicorp/vault/sdk/logical"
)
func TestBackend_Config(t *testing.T) {
defaultLeaseTTLVal := time.Hour * 24
maxLeaseTTLVal := time.Hour * 24 * 2
b, err := Factory(context.Background(), &logical.BackendConfig{
Logger: nil,
System: &logical.StaticSystemView{
DefaultLeaseTTLVal: defaultLeaseTTLVal,
MaxLeaseTTLVal: maxLeaseTTLVal,
},
})
if err != nil {
t.Fatalf("Unable to create backend: %s", err)
}
login_data := map[string]interface{}{
// This token has to be replaced with a working token for the test to work.
"token": os.Getenv("GITHUB_TOKEN"),
}
config_data1 := map[string]interface{}{
"organization": os.Getenv("GITHUB_ORG"),
"ttl": "",
"max_ttl": "",
}
expectedTTL1 := 24 * time.Hour
config_data2 := map[string]interface{}{
"organization": os.Getenv("GITHUB_ORG"),
"ttl": "1h",
"max_ttl": "2h",
}
expectedTTL2 := time.Hour
config_data3 := map[string]interface{}{
"organization": os.Getenv("GITHUB_ORG"),
"ttl": "50h",
"max_ttl": "50h",
}
expectedTTL3 := 48 * time.Hour
logicaltest.Test(t, logicaltest.TestCase{
PreCheck: func() { testAccPreCheck(t) },
CredentialBackend: b,
Steps: []logicaltest.TestStep{
testConfigWrite(t, config_data1),
testLoginWrite(t, login_data, expectedTTL1, false),
testConfigWrite(t, config_data2),
testLoginWrite(t, login_data, expectedTTL2, false),
testConfigWrite(t, config_data3),
testLoginWrite(t, login_data, expectedTTL3, true),
},
})
}
func testLoginWrite(t *testing.T, d map[string]interface{}, expectedTTL time.Duration, expectFail bool) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login",
ErrorOk: true,
Data: d,
Check: func(resp *logical.Response) error {
if resp == nil {
return errors.New("expected a response but got nil")
}
if resp.IsError() && expectFail {
return nil
}
actualTTL := resp.Auth.LeaseOptions.TTL
if actualTTL != expectedTTL {
return fmt.Errorf("TTL mismatched. Expected: %d Actual: %d", expectedTTL, resp.Auth.LeaseOptions.TTL)
}
return nil
},
}
}
func testConfigWrite(t *testing.T, d map[string]interface{}) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config",
Data: d,
}
}
func TestBackend_basic(t *testing.T) {
defaultLeaseTTLVal := time.Hour * 24
maxLeaseTTLVal := time.Hour * 24 * 32
b, err := Factory(context.Background(), &logical.BackendConfig{
Logger: nil,
System: &logical.StaticSystemView{
DefaultLeaseTTLVal: defaultLeaseTTLVal,
MaxLeaseTTLVal: maxLeaseTTLVal,
},
})
if err != nil {
t.Fatalf("Unable to create backend: %s", err)
}
logicaltest.Test(t, logicaltest.TestCase{
PreCheck: func() { testAccPreCheck(t) },
CredentialBackend: b,
Steps: []logicaltest.TestStep{
testAccStepConfig(t, false),
testAccMap(t, "default", "fakepol"),
testAccMap(t, "oWnErs", "fakepol"),
testAccLogin(t, []string{"default", "abc", "fakepol"}),
testAccStepConfig(t, true),
testAccMap(t, "default", "fakepol"),
testAccMap(t, "oWnErs", "fakepol"),
testAccLogin(t, []string{"default", "abc", "fakepol"}),
testAccStepConfigWithBaseURL(t),
testAccMap(t, "default", "fakepol"),
testAccMap(t, "oWnErs", "fakepol"),
testAccLogin(t, []string{"default", "abc", "fakepol"}),
testAccMap(t, "default", "fakepol"),
testAccStepConfig(t, true),
mapUserToPolicy(t, os.Getenv("GITHUB_USER"), "userpolicy"),
testAccLogin(t, []string{"default", "abc", "fakepol", "userpolicy"}),
},
})
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("GITHUB_TOKEN"); v == "" {
t.Skip("GITHUB_TOKEN must be set for acceptance tests")
}
if v := os.Getenv("GITHUB_USER"); v == "" {
t.Skip("GITHUB_USER must be set for acceptance tests")
}
if v := os.Getenv("GITHUB_ORG"); v == "" {
t.Skip("GITHUB_ORG must be set for acceptance tests")
}
if v := os.Getenv("GITHUB_BASEURL"); v == "" {
t.Skip("GITHUB_BASEURL must be set for acceptance tests (use 'https://api.github.com' if you don't know what you're doing)")
}
}
func testAccStepConfig(t *testing.T, upper bool) logicaltest.TestStep {
ts := logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config",
Data: map[string]interface{}{
"organization": os.Getenv("GITHUB_ORG"),
"token_policies": []string{"abc"},
},
}
if upper {
ts.Data["organization"] = strings.ToUpper(os.Getenv("GITHUB_ORG"))
}
return ts
}
func testAccStepConfigWithBaseURL(t *testing.T) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "config",
Data: map[string]interface{}{
"organization": os.Getenv("GITHUB_ORG"),
"base_url": os.Getenv("GITHUB_BASEURL"),
},
}
}
func testAccMap(t *testing.T, k string, v string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "map/teams/" + k,
Data: map[string]interface{}{
"value": v,
},
}
}
func mapUserToPolicy(t *testing.T, k string, v string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "map/users/" + k,
Data: map[string]interface{}{
"value": v,
},
}
}
func testAccLogin(t *testing.T, policies []string) logicaltest.TestStep {
return logicaltest.TestStep{
Operation: logical.UpdateOperation,
Path: "login",
Data: map[string]interface{}{
"token": os.Getenv("GITHUB_TOKEN"),
},
Unauthenticated: true,
Check: logicaltest.TestCheckAuth(policies),
}
}

View File

@ -1,97 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"fmt"
"io"
"os"
"strings"
"github.com/hashicorp/go-secure-stdlib/password"
"github.com/hashicorp/vault/api"
)
type CLIHandler struct {
// for tests
testStdout io.Writer
}
func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, error) {
mount, ok := m["mount"]
if !ok {
mount = "github"
}
// Extract or prompt for token
token := m["token"]
if token == "" {
token = os.Getenv("VAULT_AUTH_GITHUB_TOKEN")
}
if token == "" {
// Override the output
stdout := h.testStdout
if stdout == nil {
stdout = os.Stderr
}
var err error
fmt.Fprintf(stdout, "GitHub Personal Access Token (will be hidden): ")
token, err = password.Read(os.Stdin)
fmt.Fprintf(stdout, "\n")
if err != nil {
if err == password.ErrInterrupted {
return nil, fmt.Errorf("user interrupted")
}
return nil, fmt.Errorf("An error occurred attempting to "+
"ask for a token. The raw error message is shown below, but usually "+
"this is because you attempted to pipe a value into the command or "+
"you are executing outside of a terminal (tty). If you want to pipe "+
"the value, pass \"-\" as the argument to read from stdin. The raw "+
"error was: %w", err)
}
}
path := fmt.Sprintf("auth/%s/login", mount)
secret, err := c.Logical().Write(path, map[string]interface{}{
"token": strings.TrimSpace(token),
})
if err != nil {
return nil, err
}
if secret == nil {
return nil, fmt.Errorf("empty response from credential provider")
}
return secret, nil
}
func (h *CLIHandler) Help() string {
help := `
Usage: vault login -method=github [CONFIG K=V...]
The GitHub auth method allows users to authenticate using a GitHub
personal access token. Users can generate a personal access token from the
settings page on their GitHub account.
Authenticate using a GitHub token:
$ vault login -method=github token=abcd1234
Configuration:
mount=<string>
Path where the GitHub credential method is mounted. This is usually
provided via the -path flag in the "vault login" command, but it can be
specified here as well. If specified here, it takes precedence over the
value for -path. The default value is "github".
token=<string>
GitHub personal access token to use for authentication. If not provided,
Vault will prompt for the value.
`
return strings.TrimSpace(help)
}

View File

@ -1,34 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package main
import (
"os"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/credential/github"
"github.com/hashicorp/vault/sdk/plugin"
)
func main() {
apiClientMeta := &api.PluginAPIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(os.Args[1:])
tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := api.VaultPluginTLSProvider(tlsConfig)
if err := plugin.ServeMultiplex(&plugin.ServeOpts{
BackendFactoryFunc: github.Factory,
// set the TLSProviderFunc so that the plugin maintains backwards
// compatibility with Vault versions that dont support plugin AutoMTLS
TLSProviderFunc: tlsProviderFunc,
}); err != nil {
logger := hclog.New(&hclog.LoggerOptions{})
logger.Error("plugin shutting down", "error", err)
os.Exit(1)
}
}

View File

@ -1,247 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"fmt"
"net/url"
"os"
"strings"
"time"
"github.com/google/go-github/github"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/tokenutil"
"github.com/hashicorp/vault/sdk/logical"
)
func pathConfig(b *backend) *framework.Path {
p := &framework.Path{
Pattern: "config",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
},
Fields: map[string]*framework.FieldSchema{
"organization": {
Type: framework.TypeString,
Description: "The organization users must be part of",
Required: true,
},
"organization_id": {
Type: framework.TypeInt64,
Description: "The ID of the organization users must be part of",
},
"base_url": {
Type: framework.TypeString,
Description: `The API endpoint to use. Useful if you
are running GitHub Enterprise or an
API-compatible authentication server.`,
DisplayAttrs: &framework.DisplayAttributes{
Name: "Base URL",
Group: "GitHub Options",
},
},
"ttl": {
Type: framework.TypeDurationSecond,
Description: tokenutil.DeprecationText("token_ttl"),
Deprecated: true,
},
"max_ttl": {
Type: framework.TypeDurationSecond,
Description: tokenutil.DeprecationText("token_max_ttl"),
Deprecated: true,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathConfigWrite,
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
OperationVerb: "configure",
},
},
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathConfigRead,
DisplayAttrs: &framework.DisplayAttributes{
OperationSuffix: "configuration",
},
},
},
}
tokenutil.AddTokenFields(p.Fields)
p.Fields["token_policies"].Description += ". This will apply to all tokens generated by this auth method, in addition to any policies configured for specific users/groups."
return p
}
func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
var resp logical.Response
c, err := b.Config(ctx, req.Storage)
if err != nil {
return nil, err
}
if c == nil {
c = &config{}
}
if organizationRaw, ok := data.GetOk("organization"); ok {
c.Organization = organizationRaw.(string)
}
if c.Organization == "" {
return logical.ErrorResponse("organization is a required parameter"), nil
}
if organizationRaw, ok := data.GetOk("organization_id"); ok {
c.OrganizationID = organizationRaw.(int64)
}
var parsedURL *url.URL
if baseURLRaw, ok := data.GetOk("base_url"); ok {
baseURL := baseURLRaw.(string)
if !strings.HasSuffix(baseURL, "/") {
baseURL += "/"
}
parsedURL, err = url.Parse(baseURL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("error parsing given base_url: %s", err)), nil
}
c.BaseURL = baseURL
}
if c.OrganizationID == 0 {
githubToken := os.Getenv("VAULT_AUTH_CONFIG_GITHUB_TOKEN")
client, err := b.Client(githubToken)
if err != nil {
return nil, err
}
// ensure our client has the BaseURL if it was provided
if parsedURL != nil {
client.BaseURL = parsedURL
}
// we want to set the Org ID in the config so we can use that to verify
// the credentials on login
err = c.setOrganizationID(ctx, client)
if err != nil {
errorMsg := fmt.Errorf("unable to fetch the organization_id, you must manually set it in the config: %s", err)
b.Logger().Error(errorMsg.Error())
return nil, errorMsg
}
}
if err := c.ParseTokenFields(req, data); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
// Handle upgrade cases
{
if err := tokenutil.UpgradeValue(data, "ttl", "token_ttl", &c.TTL, &c.TokenTTL); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
if err := tokenutil.UpgradeValue(data, "max_ttl", "token_max_ttl", &c.MaxTTL, &c.TokenMaxTTL); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
}
entry, err := logical.StorageEntryJSON("config", c)
if err != nil {
return nil, err
}
if err := req.Storage.Put(ctx, entry); err != nil {
return nil, err
}
if len(resp.Warnings) == 0 {
return nil, nil
}
return &resp, nil
}
func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
config, err := b.Config(ctx, req.Storage)
if err != nil {
return nil, err
}
if config == nil {
return nil, nil
}
d := map[string]interface{}{
"organization_id": config.OrganizationID,
"organization": config.Organization,
"base_url": config.BaseURL,
}
config.PopulateTokenData(d)
if config.TTL > 0 {
d["ttl"] = int64(config.TTL.Seconds())
}
if config.MaxTTL > 0 {
d["max_ttl"] = int64(config.MaxTTL.Seconds())
}
return &logical.Response{
Data: d,
}, nil
}
// Config returns the configuration for this backend.
func (b *backend) Config(ctx context.Context, s logical.Storage) (*config, error) {
entry, err := s.Get(ctx, "config")
if err != nil {
return nil, err
}
if entry == nil {
return nil, nil
}
var result config
if entry != nil {
if err := entry.DecodeJSON(&result); err != nil {
return nil, fmt.Errorf("error reading configuration: %w", err)
}
}
if result.TokenTTL == 0 && result.TTL > 0 {
result.TokenTTL = result.TTL
}
if result.TokenMaxTTL == 0 && result.MaxTTL > 0 {
result.TokenMaxTTL = result.MaxTTL
}
return &result, nil
}
type config struct {
tokenutil.TokenParams
OrganizationID int64 `json:"organization_id" structs:"organization_id" mapstructure:"organization_id"`
Organization string `json:"organization" structs:"organization" mapstructure:"organization"`
BaseURL string `json:"base_url" structs:"base_url" mapstructure:"base_url"`
TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl"`
MaxTTL time.Duration `json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl"`
}
func (c *config) setOrganizationID(ctx context.Context, client *github.Client) error {
org, _, err := client.Organizations.Get(ctx, c.Organization)
if err != nil {
return err
}
orgID := org.GetID()
if orgID == 0 {
return fmt.Errorf("organization_id not found for %s", c.Organization)
}
c.OrganizationID = orgID
return nil
}

View File

@ -1,255 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"os"
"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_WriteReadConfig_Token tests that we can successfully read and
// write the github auth config with a token environment variable
func TestGitHub_WriteReadConfig_Token(t *testing.T) {
b, s := createBackendWithStorage(t)
// use a test server to return our mock GH org info
ts := setupTestServer(t)
defer ts.Close()
err := os.Setenv("VAULT_AUTH_CONFIG_GITHUB_TOKEN", "foobar")
assert.NoError(t, err)
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 token should not be returned in the read config response.
assert.Nil(t, resp.Data["token"])
}
// 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))

View File

@ -1,312 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"errors"
"fmt"
"net/url"
"github.com/google/go-github/github"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/cidrutil"
"github.com/hashicorp/vault/sdk/helper/policyutil"
"github.com/hashicorp/vault/sdk/logical"
)
func pathLogin(b *backend) *framework.Path {
return &framework.Path{
Pattern: "login",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixGithub,
OperationVerb: "login",
},
Fields: map[string]*framework.FieldSchema{
"token": {
Type: framework.TypeString,
Description: "GitHub personal API token",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathLogin,
logical.AliasLookaheadOperation: b.pathLoginAliasLookahead,
},
}
}
func (b *backend) pathLoginAliasLookahead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
token := data.Get("token").(string)
verifyResp, err := b.verifyCredentials(ctx, req, token)
if err != nil {
return nil, err
}
return &logical.Response{
Warnings: verifyResp.Warnings,
Auth: &logical.Auth{
Alias: &logical.Alias{
Name: *verifyResp.User.Login,
},
},
}, nil
}
func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
token := data.Get("token").(string)
verifyResp, err := b.verifyCredentials(ctx, req, token)
if err != nil {
return nil, err
}
auth := &logical.Auth{
InternalData: map[string]interface{}{
"token": token,
},
Metadata: map[string]string{
"username": *verifyResp.User.Login,
"org": *verifyResp.Org.Login,
},
DisplayName: *verifyResp.User.Login,
Alias: &logical.Alias{
Name: *verifyResp.User.Login,
},
}
verifyResp.Config.PopulateTokenAuth(auth)
// Add in configured policies from user/group mapping
if len(verifyResp.Policies) > 0 {
auth.Policies = append(auth.Policies, verifyResp.Policies...)
}
resp := &logical.Response{
Warnings: verifyResp.Warnings,
Auth: auth,
}
for _, teamName := range verifyResp.TeamNames {
if teamName == "" {
continue
}
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
Name: teamName,
})
}
return resp, nil
}
func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
if req.Auth == nil {
return nil, fmt.Errorf("request auth was nil")
}
tokenRaw, ok := req.Auth.InternalData["token"]
if !ok {
return nil, fmt.Errorf("token created in previous version of Vault cannot be validated properly at renewal time")
}
token := tokenRaw.(string)
verifyResp, err := b.verifyCredentials(ctx, req, token)
if err != nil {
return nil, err
}
if !policyutil.EquivalentPolicies(verifyResp.Policies, req.Auth.TokenPolicies) {
return nil, fmt.Errorf("policies do not match")
}
resp := &logical.Response{Auth: req.Auth}
resp.Auth.Period = verifyResp.Config.TokenPeriod
resp.Auth.TTL = verifyResp.Config.TokenTTL
resp.Auth.MaxTTL = verifyResp.Config.TokenMaxTTL
resp.Warnings = verifyResp.Warnings
// Remove old aliases
resp.Auth.GroupAliases = nil
for _, teamName := range verifyResp.TeamNames {
resp.Auth.GroupAliases = append(resp.Auth.GroupAliases, &logical.Alias{
Name: teamName,
})
}
return resp, nil
}
func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, token string) (*verifyCredentialsResp, error) {
var warnings []string
config, err := b.Config(ctx, req.Storage)
if err != nil {
return nil, err
}
if config == nil {
return nil, errors.New("configuration has not been set")
}
// Check for a CIDR match.
if len(config.TokenBoundCIDRs) > 0 {
if req.Connection == nil {
b.Logger().Error("token bound CIDRs found but no connection information available for validation")
return nil, logical.ErrPermissionDenied
}
if !cidrutil.RemoteAddrIsOk(req.Connection.RemoteAddr, config.TokenBoundCIDRs) {
return nil, logical.ErrPermissionDenied
}
}
client, err := b.Client(token)
if err != nil {
return nil, err
}
if config.BaseURL != "" {
parsedURL, err := url.Parse(config.BaseURL)
if err != nil {
return nil, fmt.Errorf("successfully parsed base_url when set but failing to parse now: %w", err)
}
client.BaseURL = parsedURL
}
if config.OrganizationID == 0 {
// Previously we did not verify using the Org ID. So if the Org ID is
// not set, we will trust-on-first-use and set it now.
err = config.setOrganizationID(ctx, client)
if err != nil {
b.Logger().Error("failed to set the organization_id on login", "error", err)
return nil, err
}
entry, err := logical.StorageEntryJSON("config", config)
if err != nil {
return nil, err
}
if err := req.Storage.Put(ctx, entry); err != nil {
return nil, err
}
b.Logger().Info("set ID on a trust-on-first-use basis", "organization_id", config.OrganizationID)
}
// Get the user
user, _, err := client.Users.Get(ctx, "")
if err != nil {
return nil, err
}
// Verify that the user is part of the organization
var org *github.Organization
orgOpt := &github.ListOptions{
PerPage: 100,
}
var allOrgs []*github.Organization
for {
orgs, resp, err := client.Organizations.List(ctx, "", orgOpt)
if err != nil {
return nil, err
}
allOrgs = append(allOrgs, orgs...)
if resp.NextPage == 0 {
break
}
orgOpt.Page = resp.NextPage
}
orgLoginName := ""
for _, o := range allOrgs {
if o.GetID() == config.OrganizationID {
org = o
orgLoginName = *o.Login
break
}
}
if org == nil {
return nil, errors.New("user is not part of required org")
}
if orgLoginName != config.Organization {
warningMsg := fmt.Sprintf(
"the organization name has changed to %q. It is recommended to verify and update the organization name in the config: %s=%d",
orgLoginName,
"organization_id",
config.OrganizationID,
)
b.Logger().Warn(warningMsg)
warnings = append(warnings, warningMsg)
}
// Get the teams that this user is part of to determine the policies
var teamNames []string
teamOpt := &github.ListOptions{
PerPage: 100,
}
var allTeams []*github.Team
for {
teams, resp, err := client.Teams.ListUserTeams(ctx, teamOpt)
if err != nil {
return nil, err
}
allTeams = append(allTeams, teams...)
if resp.NextPage == 0 {
break
}
teamOpt.Page = resp.NextPage
}
for _, t := range allTeams {
// We only care about teams that are part of the organization we use
if *t.Organization.ID != *org.ID {
continue
}
// Append the names so we can get the policies
teamNames = append(teamNames, *t.Name)
if *t.Name != *t.Slug {
teamNames = append(teamNames, *t.Slug)
}
}
groupPoliciesList, err := b.TeamMap.Policies(ctx, req.Storage, teamNames...)
if err != nil {
return nil, err
}
userPoliciesList, err := b.UserMap.Policies(ctx, req.Storage, []string{*user.Login}...)
if err != nil {
return nil, err
}
verifyResp := &verifyCredentialsResp{
User: user,
Org: org,
Policies: append(groupPoliciesList, userPoliciesList...),
TeamNames: teamNames,
Config: config,
Warnings: warnings,
}
return verifyResp, nil
}
type verifyCredentialsResp struct {
User *github.User
Org *github.Organization
Policies []string
TeamNames []string
// Warnings to send back to the caller
Warnings []string
// This is just a cache to send back to the caller
Config *config
}

View File

@ -1,189 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"errors"
"testing"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/assert"
)
// TestGitHub_Login tests that we can successfully login with the given config
func TestGitHub_Login(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.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())
// attempt a login
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Storage: s,
})
expectedMetaData := map[string]string{
"org": "foo-org",
"username": "user-foo",
}
assert.Equal(t, expectedMetaData, resp.Auth.Metadata)
assert.NoError(t, err)
assert.NoError(t, resp.Error())
}
// TestGitHub_Login_OrgInvalid tests that we cannot login with an ID other than
// what is set in the config
func TestGitHub_Login_OrgInvalid(t *testing.T) {
b, s := createBackendWithStorage(t)
ctx := namespace.RootContext(nil)
// use a test server to return our mock GH org info
ts := setupTestServer(t)
defer ts.Close()
// write and store config
config := config{
Organization: "foo-org",
OrganizationID: 9999,
BaseURL: ts.URL + "/", // base_url will call the test server
}
entry, err := logical.StorageEntryJSON("config", config)
if err != nil {
t.Fatalf("failed creating storage entry")
}
if err := s.Put(ctx, entry); err != nil {
t.Fatalf("writing to in mem storage failed")
}
// attempt a login
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Storage: s,
})
assert.Nil(t, resp)
assert.Error(t, err)
assert.Equal(t, errors.New("user is not part of required org"), err)
}
// TestGitHub_Login_OrgNameChanged tests that we can successfully login with the
// given config and emit a warning when the organization name has changed
func TestGitHub_Login_OrgNameChanged(t *testing.T) {
b, s := createBackendWithStorage(t)
ctx := namespace.RootContext(nil)
// use a test server to return our mock GH org info
ts := setupTestServer(t)
defer ts.Close()
// write and store config
// the name does not match what the API will return but the ID does
config := config{
Organization: "old-name",
OrganizationID: 12345,
BaseURL: ts.URL + "/", // base_url will call the test server
}
entry, err := logical.StorageEntryJSON("config", config)
if err != nil {
t.Fatalf("failed creating storage entry")
}
if err := s.Put(ctx, entry); err != nil {
t.Fatalf("writing to in mem storage failed")
}
// attempt a login
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Storage: s,
})
assert.NoError(t, err)
assert.Nil(t, resp.Error())
assert.Equal(
t,
[]string{"the organization name has changed to \"foo-org\". It is recommended to verify and update the organization name in the config: organization_id=12345"},
resp.Warnings,
)
}
// TestGitHub_Login_NoOrgID tests that we can successfully login with the given
// config when no organization ID is present and write the fetched ID to the
// config
func TestGitHub_Login_NoOrgID(t *testing.T) {
b, s := createBackendWithStorage(t)
ctx := namespace.RootContext(nil)
// use a test server to return our mock GH org info
ts := setupTestServer(t)
defer ts.Close()
// write and store config without Org ID
config := config{
Organization: "foo-org",
BaseURL: ts.URL + "/", // base_url will call the test server
}
entry, err := logical.StorageEntryJSON("config", config)
if err != nil {
t.Fatalf("failed creating storage entry")
}
if err := s.Put(ctx, entry); err != nil {
t.Fatalf("writing to in mem storage failed")
}
// attempt a login
resp, err := b.HandleRequest(context.Background(), &logical.Request{
Path: "login",
Operation: logical.UpdateOperation,
Storage: s,
})
expectedMetaData := map[string]string{
"org": "foo-org",
"username": "user-foo",
}
assert.Equal(t, expectedMetaData, resp.Auth.Metadata)
assert.NoError(t, err)
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"])
}

View File

@ -105,7 +105,6 @@ func (b *BaseCommand) PredictVaultAvailableAuths() complete.Predictor {
"app-id",
"approle",
"cert",
"github",
"ldap",
"plugin",
"radius",

View File

@ -29,7 +29,6 @@ import (
credOIDC "github.com/hashicorp/vault-plugin-auth-jwt"
credKerb "github.com/hashicorp/vault-plugin-auth-kerberos"
credCert "github.com/hashicorp/vault/builtin/credential/cert"
credGitHub "github.com/hashicorp/vault/builtin/credential/github"
credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
credToken "github.com/hashicorp/vault/builtin/credential/token"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
@ -178,7 +177,6 @@ var (
func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.CommandFactory {
loginHandlers := map[string]LoginHandler{
"cert": &credCert.CLIHandler{},
"github": &credGitHub.CLIHandler{},
"kerberos": &credKerb.CLIHandler{},
"ldap": &credLdap.CLIHandler{},
"oidc": &credOIDC.CLIHandler{},

3
go.mod
View File

@ -45,7 +45,6 @@ require (
github.com/gocql/gocql v1.0.0
github.com/golang/protobuf v1.5.3
github.com/google/go-cmp v0.6.0
github.com/google/go-github v17.0.0+incompatible
github.com/google/tink/go v1.7.0
github.com/hashicorp-forge/bbolt v1.3.8-hc3
github.com/hashicorp/cap v0.3.0
@ -155,7 +154,6 @@ require (
golang.org/x/crypto v0.23.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.18.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.20.0
golang.org/x/term v0.20.0
@ -341,6 +339,7 @@ require (
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/api v0.163.0 // indirect
google.golang.org/appengine v1.6.8 // indirect

2
go.sum
View File

@ -1713,8 +1713,6 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=

View File

@ -16,7 +16,6 @@ import (
logicalTerraform "github.com/hashicorp/vault-plugin-secrets-terraform"
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
credCert "github.com/hashicorp/vault/builtin/credential/cert"
credGitHub "github.com/hashicorp/vault/builtin/credential/github"
credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
credRadius "github.com/hashicorp/vault/builtin/credential/radius"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
@ -80,7 +79,6 @@ func newRegistry() *registry {
},
"approle": {Factory: credAppRole.Factory},
"cert": {Factory: credCert.Factory},
"github": {Factory: credGitHub.Factory},
"jwt": {Factory: credJWT.Factory},
"kerberos": {Factory: credKerb.Factory},
"kubernetes": {Factory: credKube.Factory},

View File

@ -52,7 +52,6 @@ echo "Mounting all builtin plugins ..."
# Enable auth plugins
vault auth enable "approle"
vault auth enable "cert"
vault auth enable "github"
vault auth enable "jwt"
vault auth enable "kerberos"
vault auth enable "kubernetes"

View File

@ -1,8 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import AuthConfig from './_base';
export default AuthConfig.extend();

View File

@ -18,13 +18,6 @@ const MOUNTABLE_AUTH_METHODS = [
type: 'approle',
category: 'generic',
},
{
displayName: 'GitHub',
value: 'github',
type: 'github',
category: 'cloud',
glyph: 'github-color',
},
{
displayName: 'JWT',
value: 'jwt',

View File

@ -60,14 +60,6 @@ const SUPPORTED_AUTH_BACKENDS = [
displayNamePath: 'metadata.username',
formAttributes: ['username', 'password'],
},
{
type: 'github',
typeDisplay: 'GitHub',
description: 'GitHub authentication.',
tokenPath: 'client_token',
displayNamePath: ['metadata.org', 'metadata.username'],
formAttributes: ['token'],
},
];
export function supportedAuthBackends() {

View File

@ -1,32 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { attr } from '@ember-data/model';
import { computed } from '@ember/object';
import AuthConfig from '../auth-config';
import fieldToAttrs from 'vault/utils/field-to-attrs';
import { combineFieldGroups } from 'vault/utils/openapi-to-attrs';
export default AuthConfig.extend({
useOpenAPI: true,
organization: attr('string'),
baseUrl: attr('string', {
label: 'Base URL',
}),
fieldGroups: computed('newFields', function () {
let groups = [
{ default: ['organization'] },
{
'GitHub Options': ['baseUrl'],
},
];
if (this.newFields) {
groups = combineFieldGroups(groups, this.newFields, []);
}
return fieldToAttrs(this, groups);
}),
});

View File

@ -7,15 +7,9 @@ import Model, { attr } from '@ember-data/model';
import parseURL from 'core/utils/parse-url';
const DOMAIN_STRINGS = {
'github.com': 'GitHub',
'gitlab.com': 'GitLab',
'google.com': 'Google',
'ping.com': 'Ping',
'okta.com': 'Okta',
'auth0.com': 'Auth0',
};
const PROVIDER_WITH_LOGO = ['GitLab', 'Google', 'Auth0'];
const PROVIDER_WITH_LOGO = [];
export { DOMAIN_STRINGS, PROVIDER_WITH_LOGO };

View File

@ -17,7 +17,6 @@ export default Route.extend(UnloadModelRoute, {
modelType(backendType, section) {
const MODELS = {
'github-configuration': 'auth-config/github',
'jwt-configuration': 'auth-config/jwt',
'oidc-configuration': 'auth-config/oidc',
'kubernetes-configuration': 'auth-config/kubernetes',

View File

@ -10,18 +10,7 @@
border-radius: 1px;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.4);
}
.auth-button-type-google {
position: relative;
top: -10px;
left: -1.05rem;
}
.auth-button-type-auth0,
.auth-button-type-gitlab {
position: relative;
top: -6px;
left: -0.75rem;
}
[class*='auth-button-type'] .text {
padding-left: $spacing-m;
}

View File

@ -1,24 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<div class="is-flex-v-centered auth-button-type-auth0">
<div class="auth-button-tile is-flex-column is-flex-v-centered">
{{! template-lint-disable no-forbidden-elements }}
<svg height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 193.71 216.56">
<defs>
<style>.cls-1{fill:#eb5424;}</style>
</defs>
<path
class="cls-1"
d="M189,66.92,167.22,0H96.85L118.6,66.92ZM96.85,0H26.49L4.75,66.92H75.11ZM4.75,66.92Zm0,0h0A96.85,96.85,0,0,0,39.93,175.2l21.74-66.92Zm184.21,0h0L132,108.28l21.74,66.92h0A96.85,96.85,0,0,0,189,66.92ZM39.93,175.2h0l56.93,41.36,56.92-41.36L96.85,133.84Z"
>
</path>
</svg>
{{! template-lint-enable no-forbidden-elements }}
</div>
<div class="text">
Sign in with Auth0
</div>
</div>

View File

@ -1,53 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<div class="is-flex-v-centered auth-button-type-gitlab">
{{! template-lint-disable no-forbidden-elements }}
<svg class="auth-button-tile" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 586 559">
<defs>
<style>.cls-1{fill:#fc6d26;}.cls-2{fill:#e24329;}.cls-3{fill:#fca326;}</style>
</defs>
<g id="g44">
<path
id="path46"
class="cls-1"
d="M461.17,301.83l-18.91-58.12L404.84,128.43a6.47,6.47,0,0,0-12.27,0L355.15,243.64H230.82L193.4,128.43a6.46,6.46,0,0,0-12.26,0L143.78,243.64l-18.91,58.19a12.88,12.88,0,0,0,4.66,14.39L293,435,456.44,316.22a12.9,12.9,0,0,0,4.73-14.39"
></path>
</g>
<g id="g48">
<path id="path50" class="cls-2" d="M293,434.91h0l62.16-191.28H230.87L293,434.91Z"></path>
</g>
<g id="g56">
<path id="path58" class="cls-1" d="M293,434.91,230.82,243.63h-87L293,434.91Z"></path>
</g>
<g id="g64">
<path
id="path66"
class="cls-3"
d="M143.75,243.69h0l-18.91,58.12a12.88,12.88,0,0,0,4.66,14.39L293,435,143.75,243.69Z"
></path>
</g>
<g id="g72">
<path id="path74" class="cls-2" d="M143.78,243.69h87.11L193.4,128.49a6.47,6.47,0,0,0-12.27,0l-37.35,115.2Z"></path>
</g>
<g id="g76">
<path id="path78" class="cls-1" d="M293,434.91l62.16-191.28H442.3L293,434.91Z"></path>
</g>
<g id="g80">
<path
id="path82"
class="cls-3"
d="M442.24,243.69h0l18.91,58.12a12.85,12.85,0,0,1-4.66,14.39L293,434.91l149.2-191.22Z"
></path>
</g>
<g id="g84">
<path id="path86" class="cls-2" d="M442.28,243.69h-87.1l37.42-115.2a6.46,6.46,0,0,1,12.26,0l37.42,115.2Z"></path>
</g>
</svg>
{{! template-lint-enable no-forbidden-elements }}
<div class="text">
Sign in with GitLab
</div>
</div>

View File

@ -1,75 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<div class="is-flex-v-centered auth-button-type-google">
<svg
width="39"
height="39"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 46 46"
>
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="a">
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation=".5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.168 0"
in="shadowBlurOuter1"
result="shadowMatrixOuter1"
></feColorMatrix>
<feOffset in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation=".5" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.084 0"
in="shadowBlurOuter2"
result="shadowMatrixOuter2"
></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="b" width="40" height="40" rx="2"></rect>
<rect id="c" x="5" y="5" width="38" height="38" rx="1"></rect>
</defs>
<g fill="none" fill-rule="evenodd">
<g transform="translate(3 3)" filter="url(#a)">
<use fill="#4285F4" xlink:href="#b"></use>
<use xlink:href="#b"></use>
<use xlink:href="#b"></use>
<use xlink:href="#b"></use>
</g>
<g transform="translate(-1 -1)">
<use fill="#FFF" xlink:href="#c"></use>
<use xlink:href="#c"></use>
<use xlink:href="#c"></use>
<use xlink:href="#c"></use>
</g>
<path
d="M31.64 23.20454545c0-.63818181-.0572727-1.25181818-.1636364-1.84090909H23V24.845h4.8436364c-.2086364 1.125-.8427273 2.0781818-1.7959091 2.7163636v2.2581819h2.9086363C30.6581818 28.2527273 31.64 25.9454545 31.64 23.20454545z"
fill="#4285F4"
></path>
<path
d="M23 32c2.43 0 4.4672727-.8059091 5.9563636-2.1804545l-2.9086363-2.2581819c-.8059091.54-1.8368182.8590909-3.0477273.8590909-2.34409091 0-4.32818182-1.5831818-5.03590909-3.7104545h-3.00681818v2.3318182C16.43818182 29.9831818 19.48181818 32 23 32z"
fill="#34A853"
></path>
<path
d="M17.96409091 24.71c-.18-.54-.28227273-1.11681818-.28227273-1.71 0-.59318182.10227273-1.17.28227273-1.71v-2.33181818h-3.00681818C14.34772727 20.17318182 14 21.54772727 14 23c0 1.4522727.34772727 2.8268182.95727273 4.0418182L17.9640909 24.71z"
fill="#FBBC05"
></path>
<path
d="M23 17.57954545c1.3213636 0 2.5077273.45409091 3.4404545 1.3459091l2.5813637-2.58136364C27.4631818 14.89181818 25.4259091 14 23 14c-3.51818182 0-6.56181818 2.01681818-8.04272727 4.95818182L17.9640909 21.29C18.67181818 19.16272727 20.65590909 17.57954545 23 17.57954545z"
fill="#EA4335"
></path>
<path d="M14 14h18v18H14V14z"></path>
</g>
</svg>
<div class="is-flex-1 text">
Sign in with Google
</div>
</div>

View File

@ -87,23 +87,7 @@
</AuthJwt>
{{else}}
<form id="auth-form" onsubmit={{action "doSubmit" null}}>
{{#if (eq this.providerName "github")}}
<div class="field">
<label for="token" class="is-label">GitHub token</label>
<div class="control">
<Input
@type="password"
@value={{this.token}}
name="token"
id="token"
class="input"
data-test-token={{true}}
autocomplete="off"
spellcheck="false"
/>
</div>
</div>
{{else if (eq this.providerName "token")}}
{{#if (eq this.providerName "token")}}
<div class="field">
<label for="token" class="is-label">Token</label>
<div class="control">

View File

@ -1,15 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<WizardSection
@headerText="GitHub"
@headerIcon="github"
@docText="Docs: GitHub Authentication"
@docPath="/docs/auth/github.html"
>
<p>
The Github Auth Method can be used to authenticate with Vault using a GitHub personal access token.
</p>
</WizardSection>

View File

@ -125,8 +125,6 @@ export const structureIconMap = {
'logo-bitbucket-monochrome': 'bitbucket',
'logo-f5-color': 'f5-color',
'logo-f5-monochrome': 'f5',
'logo-github-color': 'github-color',
'logo-github-monochrome': 'github',
'logo-gitlab-color': 'gitlab-color',
'logo-gitlab-monochrome': 'gitlab',
'logo-google-color': 'google-color',

View File

@ -1 +0,0 @@
<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><path d="M24 4c11.045 0 20 8.954 20 20 0 8.834-5.723 16.328-13.666 18.976-1.014.196-1.374-.428-1.374-.96 0-.659.024-2.812.024-5.488 0-1.866-.64-3.083-1.357-3.704 4.454-.495 9.132-2.187 9.132-9.868 0-2.184-.776-3.968-2.058-5.369.208-.503.893-2.538-.196-5.293 0 0-1.677-.537-5.495 2.05a19.22 19.22 0 0 0-5.01-.672c-1.7.007-3.41.23-5.006.673-3.822-2.588-5.501-2.05-5.501-2.05-1.086 2.754-.402 4.789-.194 5.292-1.28 1.4-2.06 3.185-2.06 5.369 0 7.663 4.67 9.378 9.111 9.884-.572.5-1.09 1.381-1.27 2.674-1.14.511-4.036 1.395-5.82-1.662 0 0-1.057-1.92-3.065-2.061 0 0-1.952-.026-.137 1.215 0 0 1.31.615 2.22 2.927 0 0 1.175 3.57 6.739 2.36.008 1.67.027 3.246.027 3.723 0 .528-.367 1.147-1.367.962C9.731 40.334 4 32.837 4 24 4 12.954 12.956 4 24 4zM11.575 32.716c-.044.1-.2.129-.343.06-.145-.064-.226-.2-.18-.3.044-.102.2-.13.345-.062.146.065.228.202.178.302zm.81.903c-.095.089-.282.048-.408-.092-.131-.14-.156-.326-.059-.416.099-.088.28-.047.41.093.131.14.157.326.057.415zm.789 1.152c-.123.085-.323.006-.447-.172-.123-.178-.123-.391.003-.477.124-.085.321-.008.447.168.122.18.122.394-.003.481zm1.08 1.113c-.11.121-.343.089-.514-.076-.175-.162-.223-.39-.114-.511.111-.122.346-.088.518.076.174.161.227.392.11.511zm1.49.646c-.048.157-.273.228-.5.162-.225-.069-.374-.253-.328-.41.047-.158.273-.233.501-.161.226.068.375.25.327.41zm1.637.12c.006.165-.186.302-.424.305-.24.005-.433-.128-.435-.29 0-.167.188-.303.427-.307.238-.004.432.128.432.292zm1.523-.26c.029.162-.136.327-.373.371-.232.042-.447-.057-.476-.217-.029-.164.14-.33.371-.372.237-.042.448.055.478.219z" fill="#161514" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB