Skip to content

Commit

Permalink
Change config as suggested and add config tests
Browse files Browse the repository at this point in the history
  • Loading branch information
marc-gr committed Jun 5, 2020
1 parent a596016 commit 0475b64
Show file tree
Hide file tree
Showing 6 changed files with 490 additions and 126 deletions.
5 changes: 1 addition & 4 deletions x-pack/filebeat/input/httpjson/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ func (c *config) Validate() error {
}
if c.OAuth2 != nil {
if c.APIKey != "" || c.AuthenticationScheme != "" {
return errors.Errorf("invalid configuration: both oauth2 and api_key or authentication_scheme cannot be set simultaneously")
}
if err := c.OAuth2.validate(); err != nil {
return err
return errors.Errorf("invalid configuration: oauth2 and api_key or authentication_scheme cannot be set simultaneously")
}
}
return nil
Expand Down
66 changes: 38 additions & 28 deletions x-pack/filebeat/input/httpjson/config_oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ func (p OAuth2Provider) canonical() OAuth2Provider {
// OAuth2 contains information about oauth2 authentication settings.
type OAuth2 struct {
// common oauth fields
ClientID string `config:"client_id"`
ClientSecret string `config:"client_secret"`
ClientID string `config:"client.id"`
ClientSecret string `config:"client.secret"`
Enabled *bool `config:"enabled"`
EndpointParams map[string][]string `config:"endpoint_params"`
Provider OAuth2Provider `config:"provider"`
Provider string `config:"provider"`
Scopes []string `config:"scopes"`
TokenURL string `config:"token_url"`

// google specific
GoogleCredentialsFile string `config:"google_credentials_file"`
GoogleCredentialsJSON []byte `config:"google_credentials_json"`
GoogleJWTFile string `config:"google_jwt_file"`
GoogleCredentialsFile string `config:"google.credentials_file"`
GoogleCredentialsJSON []byte `config:"google.credentials_json"`
GoogleJWTFile string `config:"google.jwt_file"`

// microsoft azure specific
AzureTenantID string `config:"azure_tenant_id"`
AzureTenantID string `config:"azure.tenant_id"`
}

// IsEnabled returns true if the `enable` field is set to true in the yaml.
Expand All @@ -59,34 +59,43 @@ func (o *OAuth2) IsEnabled() bool {
func (o *OAuth2) Client(ctx context.Context, client *http.Client) (*http.Client, error) {
ctx = context.WithValue(ctx, oauth2.HTTPClient, client)

if o.Provider != OAuth2ProviderGoogle {
creds := clientcredentials.Config{
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
TokenURL: o.GetTokenURL(),
Scopes: o.Scopes,
EndpointParams: o.EndpointParams,
switch o.GetProvider() {
case OAuth2ProviderGoogle:
creds, err := google.CredentialsFromJSON(ctx, o.GoogleCredentialsJSON, o.Scopes...)
if err != nil {
return nil, fmt.Errorf("oauth2 client: error loading credentials: %w", err)
}
return creds.Client(ctx), nil
return oauth2.NewClient(ctx, creds.TokenSource), nil
}

creds, err := google.CredentialsFromJSON(ctx, o.GoogleCredentialsJSON, o.Scopes...)
if err != nil {
return nil, fmt.Errorf("oauth2 client: error loading credentials: %w", err)
creds := clientcredentials.Config{
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
TokenURL: o.GetTokenURL(),
Scopes: o.Scopes,
EndpointParams: o.EndpointParams,
}

return oauth2.NewClient(ctx, creds.TokenSource), nil
return creds.Client(ctx), nil
}

func (o *OAuth2) GetTokenURL() string {
if o.Provider == OAuth2ProviderAzure && o.TokenURL == "" {
return endpoints.AzureAD(o.AzureTenantID).TokenURL
switch o.GetProvider() {
case OAuth2ProviderAzure:
if o.TokenURL == "" {
return endpoints.AzureAD(o.AzureTenantID).TokenURL
}
}

return o.TokenURL
}

func (o *OAuth2) validate() error {
switch o.Provider.canonical() {
func (o OAuth2) GetProvider() OAuth2Provider {
return OAuth2Provider(o.Provider).canonical()
}

func (o *OAuth2) Validate() error {
switch o.GetProvider() {
case OAuth2ProviderAzure:
return o.validateAzureProvider()
case OAuth2ProviderGoogle:
Expand All @@ -96,15 +105,15 @@ func (o *OAuth2) validate() error {
return errors.New("invalid configuration: both token_url and client credentials must be provided")
}
default:
return fmt.Errorf("invalid configuration: unknown provider %q", o.Provider)
return fmt.Errorf("invalid configuration: unknown provider %q", o.GetProvider())
}
return nil
}

func (o *OAuth2) validateGoogleProvider() error {
if o.TokenURL != "" || o.ClientID != "" || o.ClientSecret != "" ||
o.AzureTenantID != "" || len(o.EndpointParams) > 0 {
return errors.New("invalid configuration: none of token_url and client credentials can be used, use google_credentials_file or google_jwt_file instead")
return errors.New("invalid configuration: none of token_url and client credentials can be used, use google.credentials_file, google.jwt_file, google.credentials_json or ADC instead")
}

// credentials_json
Expand All @@ -124,7 +133,8 @@ func (o *OAuth2) validateGoogleProvider() error {

// Application Default Credentials (ADC)
ctx := context.Background()
if _, err := google.FindDefaultCredentials(ctx, o.Scopes...); err == nil {
if creds, err := google.FindDefaultCredentials(ctx, o.Scopes...); err == nil {
o.GoogleCredentialsJSON = creds.JSON
return nil
}

Expand All @@ -133,12 +143,12 @@ func (o *OAuth2) validateGoogleProvider() error {

func (o *OAuth2) populateCredentialsJSONFromFile(file string) error {
if _, err := os.Stat(file); os.IsNotExist(err) {
return fmt.Errorf("the file %q cannot be found", file)
return fmt.Errorf("invalid configuration: the file %q cannot be found", file)
}

credBytes, err := ioutil.ReadFile(file)
if err != nil {
return fmt.Errorf("the file %q cannot be read", file)
return fmt.Errorf("invalid configuration: the file %q cannot be read", file)
}

o.GoogleCredentialsJSON = credBytes
Expand Down
69 changes: 69 additions & 0 deletions x-pack/filebeat/input/httpjson/config_oauth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package httpjson

import "testing"

func TestProviderCanonical(t *testing.T) {
const (
a OAuth2Provider = "gOoGle"
b OAuth2Provider = "google"
)

if a.canonical() != b.canonical() {
t.Fatal("Canonical provider should be equal")
}
}

func TestGetProviderIsCanonical(t *testing.T) {
const expected OAuth2Provider = "google"

oauth2 := OAuth2{Provider: "GOogle"}
if oauth2.GetProvider() != expected {
t.Fatal("GetProvider should return canonical provider")
}
}

func TestIsEnabled(t *testing.T) {
oauth2 := OAuth2{}
if !oauth2.IsEnabled() {
t.Fatal("OAuth2 should be enabled by default")
}

var enabled = false
oauth2.Enabled = &enabled

if oauth2.IsEnabled() {
t.Fatal("OAuth2 should be disabled")
}

enabled = true
if !oauth2.IsEnabled() {
t.Fatal("OAuth2 should be enabled")
}
}

func TestGetTokenURL(t *testing.T) {
const expected = "http://localhost"
oauth2 := OAuth2{TokenURL: "http://localhost"}
if oauth2.GetTokenURL() != expected {
t.Fatal("GetTokenURL should return the provided TokenURL")
}
}

func TestGetTokenURLWithAzure(t *testing.T) {
const expectedWithoutTenantID = "http://localhost"
oauth2 := OAuth2{TokenURL: "http://localhost", Provider: "azure"}
if got := oauth2.GetTokenURL(); got != expectedWithoutTenantID {
t.Fatalf("GetTokenURL should return the provided TokenURL but got %q", got)
}

oauth2.TokenURL = ""
oauth2.AzureTenantID = "a_tenant_id"
const expectedWithTenantID = "https://login.microsoftonline.com/a_tenant_id/oauth2/v2.0/token"
if got := oauth2.GetTokenURL(); got != expectedWithTenantID {
t.Fatalf("GetTokenURL should return the generated TokenURL but got %q", got)
}
}
Loading

0 comments on commit 0475b64

Please sign in to comment.