Skip to content

Commit

Permalink
feat: accept base64 encoded credentials (#84)
Browse files Browse the repository at this point in the history
In Jenkins, I sew users hosting their credentials as a `username:token`
base64 encoded secret, so I want to support it as an argument.
  • Loading branch information
Baruch Odem (Rothkoff) authored Jun 12, 2023
1 parent 88c46df commit 6cd4255
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 25 deletions.
17 changes: 13 additions & 4 deletions lib/http.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package lib

import (
"encoding/base64"
"fmt"
"io"
"net/http"
Expand All @@ -10,15 +11,23 @@ type ICredentials interface {
GetCredentials() (string, string)
}

func HttpRequest(method string, url string, credentials ICredentials) ([]byte, *http.Response, error) {
func CreateBasicAuthCredentials(credentials ICredentials) string {
username, password := credentials.GetCredentials()
return "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
}

type IAuthorizationHeader interface {
GetAuthorizationHeader() string
}

func HttpRequest(method string, url string, autherization IAuthorizationHeader) ([]byte, *http.Response, error) {
request, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, nil, fmt.Errorf("unexpected error creating an http request %w", err)
}

username, password := credentials.GetCredentials()
if username != "" && password != "" {
request.SetBasicAuth(username, password)
if autherization.GetAuthorizationHeader() != "" {
request.Header.Set("Authorization", autherization.GetAuthorizationHeader())
}

client := &http.Client{}
Expand Down
4 changes: 4 additions & 0 deletions plugins/confluence.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ func (p *ConfluencePlugin) GetCredentials() (string, string) {
return p.Username, p.Token
}

func (p *ConfluencePlugin) GetAuthorizationHeader() string {
return lib.CreateBasicAuthCredentials(p)
}

func (p *ConfluencePlugin) DefineCommand(channels Channels) (*cobra.Command, error) {
var confluenceCmd = &cobra.Command{
Use: fmt.Sprintf("%s --%s URL", p.GetName(), argUrl),
Expand Down
49 changes: 28 additions & 21 deletions plugins/paligo.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
paligoInstanceFlag = "instance"
paligoUsernameFlag = "username"
paligoTokenFlag = "token"
paligoAuthFlag = "auth"
paligoFolderFlag = "folder"
)

Expand All @@ -33,6 +34,7 @@ type PaligoPlugin struct {

username string
token string
auth string

paligoApi *PaligoClient
}
Expand All @@ -41,6 +43,13 @@ func (p *PaligoPlugin) GetCredentials() (string, string) {
return p.username, p.token
}

func (p *PaligoPlugin) GetAuthorizationHeader() string {
if p.auth != "" {
return fmt.Sprintf("Basic %s", p.auth)
}
return lib.CreateBasicAuthCredentials(p)
}

func (p *PaligoPlugin) GetName() string {
return "paligo"
}
Expand All @@ -57,6 +66,11 @@ func (p *PaligoPlugin) DefineCommand(channels Channels) (*cobra.Command, error)
Short: "Scan Paligo instance",
Long: "Scan Paligo instance for sensitive information.",
Run: func(cmd *cobra.Command, args []string) {
// Waits for MarkFlagsMutuallyExclusiveAndRequired https://github.com/spf13/cobra/pull/1972
if p.auth == "" && (p.username == "" || p.token == "") {
p.Channels.Errors <- fmt.Errorf("exactly one of the flags in the group %v must be set; none were set", []string{paligoAuthFlag, paligoUsernameFlag, paligoTokenFlag})
return
}
log.Info().Msg("Paligo plugin started")
p.getItems()
},
Expand All @@ -67,23 +81,22 @@ func (p *PaligoPlugin) DefineCommand(channels Channels) (*cobra.Command, error)
if err != nil {
return nil, fmt.Errorf("error while marking flag %s as required: %w", paligoInstanceFlag, err)
}
command.Flags().StringVar(&p.username, paligoUsernameFlag, "", "Paligo username [required]")
err = command.MarkFlagRequired(paligoUsernameFlag)
if err != nil {
return nil, fmt.Errorf("error while marking flag %s as required: %w", paligoUsernameFlag, err)
}
command.Flags().StringVar(&p.token, paligoTokenFlag, "", "Paligo token [required]")
err = command.MarkFlagRequired(paligoTokenFlag)
if err != nil {
return nil, fmt.Errorf("error while marking flag %s as required: %w", paligoTokenFlag, err)
}

command.Flags().StringVar(&p.username, paligoUsernameFlag, "", "Paligo username")
command.Flags().StringVar(&p.token, paligoTokenFlag, "", "Paligo token")
command.MarkFlagsRequiredTogether(paligoUsernameFlag, paligoTokenFlag)

command.Flags().StringVar(&p.auth, paligoAuthFlag, "", "Paligo encoded username:password")
command.MarkFlagsMutuallyExclusive(paligoUsernameFlag, paligoAuthFlag)
command.MarkFlagsMutuallyExclusive(paligoTokenFlag, paligoAuthFlag)

command.Flags().IntVar(&paligoFolderArg, paligoFolderFlag, 0, "Paligo folder ID")

return command, nil
}

func (p *PaligoPlugin) getItems() {
p.paligoApi = newPaligoApi(paligoInstanceArg, p.username, p.token)
p.paligoApi = newPaligoApi(paligoInstanceArg, p)

foldersToProcess, err := p.getFirstProcessingFolders()
if err != nil {
Expand Down Expand Up @@ -238,8 +251,7 @@ type Document struct {

type PaligoClient struct {
Instance string
Username string
Token string
auth lib.IAuthorizationHeader

foldersLimiter *rate.Limiter
documentsLimiter *rate.Limiter
Expand All @@ -264,18 +276,14 @@ func reserveRateLimit(response *http.Response, lim *rate.Limiter, err error) err
return nil
}

func (p *PaligoClient) GetCredentials() (string, string) {
return p.Username, p.Token
}

func (p *PaligoClient) request(endpoint string, lim *rate.Limiter) ([]byte, error) {
if err := lim.Wait(context.Background()); err != nil {
log.Error().Msgf("Error waiting for rate limiter: %s", err)
return nil, err
}

url := fmt.Sprintf("https://%s.paligoapp.com/api/v2/%s", p.Instance, endpoint)
body, response, err := lib.HttpRequest("GET", url, p)
body, response, err := lib.HttpRequest("GET", url, p.auth)
if err != nil {
if err := reserveRateLimit(response, lim, err); err != nil {
return nil, err
Expand Down Expand Up @@ -322,11 +330,10 @@ func (p *PaligoClient) showDocument(documentId int) (*Document, error) {
return document, err
}

func newPaligoApi(instance string, username string, token string) *PaligoClient {
func newPaligoApi(instance string, auth lib.IAuthorizationHeader) *PaligoClient {
return &PaligoClient{
Instance: instance,
Username: username,
Token: token,
auth: auth,

foldersLimiter: rate.NewLimiter(rateLimitPerSecond(PALIGO_FOLDER_SHOW_LIMIT), PALIGO_FOLDER_SHOW_LIMIT),
documentsLimiter: rate.NewLimiter(rateLimitPerSecond(PALIGO_DOCUMENT_SHOW_LIMIT), PALIGO_DOCUMENT_SHOW_LIMIT),
Expand Down

0 comments on commit 6cd4255

Please sign in to comment.