Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP basic auth login. #144

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/cmd/grafana-kiosk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ type Args struct {
WindowPosition string
WindowSize string
ScaleFactor string
BasicAuthUsername string
BasicAuthPassword string
}

// ProcessArgs processes and handles CLI arguments.
Expand Down Expand Up @@ -69,6 +71,8 @@ func ProcessArgs(cfg interface{}) Args {
flagSettings.StringVar(&processedArgs.Audience, "audience", "", "idtoken audience")
flagSettings.StringVar(&processedArgs.KeyFile, "keyfile", "key.json", "idtoken json credentials")
flagSettings.StringVar(&processedArgs.APIKey, "apikey", "", "apikey")
flagSettings.StringVar(&processedArgs.BasicAuthUsername, "basic-username", "", "Specify HTTP basic auth username.")
flagSettings.StringVar(&processedArgs.BasicAuthPassword, "basic-password", "", "Specify HTTP basic auth password.")

fu := flagSettings.Usage
flagSettings.Usage = func() {
Expand Down Expand Up @@ -195,6 +199,10 @@ func main() {
cfg.IDToken.KeyFile = args.KeyFile

cfg.APIKey.APIKey = args.APIKey

//
cfg.BasicAuth.Username = args.BasicAuthUsername
cfg.BasicAuth.Password = args.BasicAuthPassword
}

// make sure the url has content
Expand Down
13 changes: 13 additions & 0 deletions pkg/kiosk/anonymous_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"time"

"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
)

Expand Down Expand Up @@ -45,7 +46,15 @@ func GrafanaKioskAnonymous(cfg *Config, messages chan string) {
/*
Launch chrome and look for main-view element
*/
headers := make(map[string]interface{})
if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would anonymous login require authentication?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the anonymous login is for Grafana, meaning that the grafana dashboard (app) itself doesn't require authentication (user account) but it still might be hidden behind a reverse proxy that requires HTTP-Basic auth.
So I see them as two separate authentication requirements.

headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
chromedp.WaitVisible(`//div[@class="main-view"]`, chromedp.BySearch),
); err != nil {
Expand All @@ -55,6 +64,10 @@ func GrafanaKioskAnonymous(cfg *Config, messages chan string) {
for {
messageFromChrome := <-messages
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
Expand Down
4 changes: 4 additions & 0 deletions pkg/kiosk/apikey_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ func GrafanaKioskAPIKey(cfg *Config, messages chan string) {
/*
Launch chrome and look for main-view element
*/
if len(cfg.BasicAuth.Username) != 0 {
log.Fatal("Grafana Kiosk API login cannot be combined with http basic auth login.")
}
headers := map[string]interface{}{
"Authorization": "Bearer " + cfg.APIKey.APIKey,
}

if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
Expand Down
14 changes: 14 additions & 0 deletions pkg/kiosk/aws_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"time"

"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
"github.com/chromedp/chromedp/kb"
)
Expand Down Expand Up @@ -46,7 +47,16 @@ func GrafanaKioskAWSLogin(cfg *Config, messages chan string) {
// Give browser time to load next page (this can be prone to failure, explore different options vs sleeping)
time.Sleep(2000 * time.Millisecond)

headers := make(map[string]interface{})
if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for this file, this is for aws login, and doesn't use basic auth, maybe this was not intended?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see the following comment #144 (comment)
but if u think it's not needed then I'll just remove it.

headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}

if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
chromedp.WaitVisible(`//a[contains(@href,'login/sso')]`, chromedp.BySearch),
chromedp.Click(`//a[contains(@href,'login/sso')]`, chromedp.BySearch),
Expand All @@ -70,6 +80,10 @@ func GrafanaKioskAWSLogin(cfg *Config, messages chan string) {
for {
messageFromChrome := <-messages
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
Expand Down
18 changes: 12 additions & 6 deletions pkg/kiosk/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,18 @@ type APIKey struct {
APIKey string `yaml:"apikey" env:"KIOSK_APIKEY_APIKEY" env-description:"APIKEY"`
}

type HTTPBasicAuth struct {
Username string `yaml:"basicauth-username" env:"KIOSK_BASICAUTH_USERNAME" env-description:"HTTP basic auth username"`
Password string `yaml:"basicauth-password" env:"KIOSK_BASICAUTH_PASSWORD" env-description:"HTTP basic auth password"`
}

// Config configuration for backend.
type Config struct {
BuildInfo BuildInfo `yaml:"buildinfo"`
General General `yaml:"general"`
Target Target `yaml:"target"`
GoAuth GoAuth `yaml:"goauth"`
IDToken IDToken `yaml:"idtoken"`
APIKey APIKey `yaml:"apikey"`
BuildInfo BuildInfo `yaml:"buildinfo"`
General General `yaml:"general"`
Target Target `yaml:"target"`
GoAuth GoAuth `yaml:"goauth"`
IDToken IDToken `yaml:"idtoken"`
APIKey APIKey `yaml:"apikey"`
BasicAuth HTTPBasicAuth `yaml:"BasicAuth"`
}
18 changes: 18 additions & 0 deletions pkg/kiosk/grafana_com_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"time"

"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
"github.com/chromedp/chromedp/kb"
)
Expand Down Expand Up @@ -50,8 +51,17 @@ func GrafanaKioskGCOM(cfg *Config, messages chan string) {

// chromedp.WaitVisible(`//*[@href="login/grafana_com"]/i`, chromedp.BySearch),

headers := make(map[string]interface{})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for this file, this is for gcom (grafana cloud) login, and doesn't use basic auth, maybe this was not intended?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see the following comment #144 (comment)
but if u think it's not needed then I'll just remove it.

if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}

// Click the grafana_com login button
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
chromedp.ActionFunc(func(context.Context) error {
log.Println("waiting for login dialog")
Expand All @@ -74,6 +84,10 @@ func GrafanaKioskGCOM(cfg *Config, messages chan string) {
time.Sleep(3000 * time.Millisecond)
// Fill out grafana_com login page
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.WaitVisible(`//input[@name="login"]`, chromedp.BySearch),
chromedp.SendKeys(`//input[@name="login"]`, cfg.Target.Username, chromedp.BySearch),
chromedp.Click(`#submit`, chromedp.ByID),
Expand All @@ -85,6 +99,10 @@ func GrafanaKioskGCOM(cfg *Config, messages chan string) {
for {
messageFromChrome := <-messages
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
Expand Down
22 changes: 21 additions & 1 deletion pkg/kiosk/grafana_genericoauth_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"time"

"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
"github.com/chromedp/chromedp/kb"
)
Expand Down Expand Up @@ -47,15 +48,26 @@ func GrafanaKioskGenericOauth(cfg *Config, messages chan string) {

// Click the OAUTH login button
log.Println("Oauth_Auto_Login enabled: ", cfg.GoAuth.AutoLogin)

headers := make(map[string]interface{})
if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}
if cfg.GoAuth.AutoLogin {
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
}
} else {
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
chromedp.WaitVisible(`//*[@href="login/generic_oauth"]`, chromedp.BySearch),
chromedp.Click(`//*[@href="login/generic_oauth"]`, chromedp.BySearch),
Expand All @@ -70,6 +82,10 @@ func GrafanaKioskGenericOauth(cfg *Config, messages chan string) {

// Fill out OAUTH login page
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.WaitVisible(`//input[@name="`+cfg.GoAuth.UsernameField+`"]`, chromedp.BySearch),
chromedp.SendKeys(`//input[@name="`+cfg.GoAuth.UsernameField+`"]`, cfg.Target.Username, chromedp.BySearch),
chromedp.SendKeys(`//input[@name="`+cfg.GoAuth.PasswordField+`"]`, cfg.Target.Password+kb.Enter, chromedp.BySearch),
Expand All @@ -80,6 +96,10 @@ func GrafanaKioskGenericOauth(cfg *Config, messages chan string) {
for {
messageFromChrome := <-messages
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
Expand Down
17 changes: 16 additions & 1 deletion pkg/kiosk/grafana_idtoken_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/fetch"
"github.com/chromedp/cdproto/network"

"github.com/chromedp/chromedp"

Expand Down Expand Up @@ -56,6 +57,11 @@ func GrafanaKioskIDToken(cfg *Config, messages chan string) {
panic(err)
}

headers := make(map[string]interface{})
if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}

chromedp.ListenTarget(taskCtx, func(ev interface{}) {
//nolint:gocritic // future events can be handled here
switch ev := ev.(type) {
Expand All @@ -78,14 +84,23 @@ func GrafanaKioskIDToken(cfg *Config, messages chan string) {
}
})

if err := chromedp.Run(taskCtx, enableFetch(generatedURL)); err != nil {
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
enableFetch(generatedURL)); err != nil {
panic(err)
}

// blocking wait
for {
messageFromChrome := <-messages
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
Expand Down
13 changes: 12 additions & 1 deletion pkg/kiosk/listen_chrome_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"

"github.com/chromedp/cdproto/inspector"
"github.com/chromedp/cdproto/network"
"github.com/chromedp/cdproto/runtime"
"github.com/chromedp/chromedp"
)
Expand All @@ -17,6 +18,11 @@ const (
)

func listenChromeEvents(taskCtx context.Context, cfg *Config, events chromeEvents) {
headers := make(map[string]interface{})
if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}

chromedp.ListenTarget(taskCtx, func(ev interface{}) {
switch ev := ev.(type) {
case *runtime.EventConsoleAPICalled:
Expand All @@ -30,7 +36,12 @@ func listenChromeEvents(taskCtx context.Context, cfg *Config, events chromeEvent
if events&targetCrashed != 0 {
log.Printf("target crashed, reload...")
go func() {
_ = chromedp.Run(taskCtx, chromedp.Reload())
_ = chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Reload())
}()
}
default:
Expand Down
18 changes: 18 additions & 0 deletions pkg/kiosk/local_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"time"

"github.com/chromedp/cdproto/network"
"github.com/chromedp/chromedp"
"github.com/chromedp/chromedp/kb"
)
Expand Down Expand Up @@ -50,6 +51,11 @@ func GrafanaKioskLocal(cfg *Config, messages chan string) {
log.Printf("Sleeping %d MS before navigating to url", cfg.General.PageLoadDelayMS)
time.Sleep(time.Duration(cfg.General.PageLoadDelayMS) * time.Millisecond)

headers := make(map[string]interface{})
if len(cfg.BasicAuth.Username) != 0 && len(cfg.BasicAuth.Password) != 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for this file, this is for local login (via ui interaction), and doesn't use basic auth, maybe this was not intended?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see the following comment #144 (comment)
but if u think it's not needed then I'll just remove it.

headers["Authorization"] = GenerateHTTPBasicAuthHeader(cfg.BasicAuth.Username, cfg.BasicAuth.Password)
}

if cfg.GoAuth.AutoLogin {
// if AutoLogin is set, get the base URL and append the local login bypass before navigating to the full url
startIndex := strings.Index(cfg.Target.URL, "://") + 3
Expand All @@ -60,6 +66,10 @@ func GrafanaKioskLocal(cfg *Config, messages chan string) {
log.Println("Bypassing Azure AD autoLogin at ", bypassURL)

if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(bypassURL),
chromedp.WaitVisible(`//input[@name="user"]`, chromedp.BySearch),
chromedp.SendKeys(`//input[@name="user"]`, cfg.Target.Username, chromedp.BySearch),
Expand All @@ -71,6 +81,10 @@ func GrafanaKioskLocal(cfg *Config, messages chan string) {
}
} else {
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
chromedp.WaitVisible(`//input[@name="user"]`, chromedp.BySearch),
chromedp.SendKeys(`//input[@name="user"]`, cfg.Target.Username, chromedp.BySearch),
Expand All @@ -84,6 +98,10 @@ func GrafanaKioskLocal(cfg *Config, messages chan string) {
for {
messageFromChrome := <-messages
if err := chromedp.Run(taskCtx,
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
network.Enable(),
network.SetExtraHTTPHeaders(network.Headers(headers)),
chromedp.Navigate(generatedURL),
); err != nil {
panic(err)
Expand Down
7 changes: 7 additions & 0 deletions pkg/kiosk/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kiosk

import (
"encoding/base64"
"fmt"
"log"
"net/url"
Expand Down Expand Up @@ -105,3 +106,9 @@ func generateExecutorOptions(dir string, cfg *Config) []chromedp.ExecAllocatorOp

return execAllocatorOption
}

func GenerateHTTPBasicAuthHeader(username string, password string) string {
auth := username + ":" + password
encodedAuth := base64.StdEncoding.EncodeToString([]byte(auth))
return "Basic " + encodedAuth
}