Skip to content

Commit

Permalink
Initial work on login command (not yet working)
Browse files Browse the repository at this point in the history
  • Loading branch information
filip-debricked committed Sep 3, 2024
1 parent 52060ea commit 8e1432d
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 0 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.4
github.com/vifraa/gopom v0.2.1
golang.org/x/oauth2 v0.22.0
golang.org/x/tools v0.19.0
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.2.1
Expand All @@ -25,12 +26,15 @@ require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/cli/browser v1.0.0 // indirect
github.com/cli/safeexec v1.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-oauth2/oauth2 v3.9.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ github.com/chelnak/ysmrr v0.2.1/go.mod h1:9TEgLy2xDMGN62zJm9XZrEWY/fHoGoBslSVEkE
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cli/browser v1.0.0 h1:RIleZgXrhdiCVgFBSjtWwkLPUCWyhhhN5k5HGSBt1js=
github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
github.com/cli/oauth v1.0.1 h1:pXnTFl/qUegXHK531Dv0LNjW4mLx626eS42gnzfXJPA=
github.com/cli/oauth v1.0.1/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4=
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
Expand Down Expand Up @@ -99,6 +105,8 @@ github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lK
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-oauth2/oauth2 v3.9.2+incompatible h1:A8gSjq4110EgZDVk4ZtcpusynU2Fto9eM6sXvxL+EOs=
github.com/go-oauth2/oauth2 v3.9.2+incompatible/go.mod h1:GGcZ+i513KxN4yS7zBYfmwo3P+cyGvCS675uCNmWv/g=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -375,6 +383,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
39 changes: 39 additions & 0 deletions internal/cmd/login/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package login

import (
"fmt"
"github.com/debricked/cli/internal/login"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func NewLoginCmd(authenticator login.IAuthenticator) *cobra.Command {
cmd := &cobra.Command{
Use: "login",
Short: "Authenticate debricked user",
Long: `Start authentication flow to generate access token.`,
PreRun: func(cmd *cobra.Command, _ []string) {
_ = viper.BindPFlags(cmd.Flags())
},
RunE: RunE(authenticator),
}

return cmd
}

func RunE(a login.IAuthenticator) func(_ *cobra.Command, args []string) error {
return func(cmd *cobra.Command, _ []string) error {
token, err := a.Authenticate()
if err != nil {
return err
}
fmt.Printf(
"%s Successfully authenticated\nToken=%s",
color.GreenString("✔"),
color.BlueString(token),
)

return nil
}
}
2 changes: 2 additions & 0 deletions internal/cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/debricked/cli/internal/cmd/callgraph"
"github.com/debricked/cli/internal/cmd/files"
"github.com/debricked/cli/internal/cmd/fingerprint"
"github.com/debricked/cli/internal/cmd/login"
"github.com/debricked/cli/internal/cmd/report"
"github.com/debricked/cli/internal/cmd/resolve"
"github.com/debricked/cli/internal/cmd/scan"
Expand Down Expand Up @@ -47,6 +48,7 @@ Read more: https://docs.debricked.com/product/administration/generate-access-tok
rootCmd.AddCommand(fingerprint.NewFingerprintCmd(container.Fingerprinter()))
rootCmd.AddCommand(resolve.NewResolveCmd(container.Resolver()))
rootCmd.AddCommand(callgraph.NewCallgraphCmd(container.CallgraphGenerator()))
rootCmd.AddCommand(login.NewLoginCmd(container.Authenticator()))

rootCmd.CompletionOptions.DisableDefaultCmd = true

Expand Down
136 changes: 136 additions & 0 deletions internal/login/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package login

import (
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
"log"
"math/rand"
"net/http"
"os/exec"
"runtime"
"strings"
"time"

"golang.org/x/oauth2"
)

type IAuthenticator interface {
Authenticate() (string, error)
}

type Authenticator struct {
ClientID string
Scopes []string
}

const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

func generateRandomString(length int) string {
seed := rand.NewSource(time.Now().UnixNano())
r := rand.New(seed)

b := make([]byte, length)
for i := range b {
b[i] = charset[r.Intn(len(charset))]
}
return string(b)
}

func createCodeChallenge(codeVerifier string) string {
// Create a SHA-256 hash of the code verifier
hash := sha256.Sum256([]byte(codeVerifier))

// Encode the hash to base64
encoded := base64.StdEncoding.EncodeToString(hash[:])

// Make it URL safe
encoded = strings.TrimRight(encoded, "=")
encoded = strings.ReplaceAll(encoded, "+", "-")
encoded = strings.ReplaceAll(encoded, "/", "_")

return encoded
}

func (a Authenticator) Authenticate() (string, error) {
// Set up OAuth2 configuration
config := &oauth2.Config{
ClientID: a.ClientID,
ClientSecret: "",
Endpoint: oauth2.Endpoint{
AuthURL: "https://debricked.com/app/oauth/authorize",
TokenURL: "https://debricked.com/app/oauth/token",
},
RedirectURL: "http://localhost:9096/callback",
Scopes: a.Scopes,
}

// Create a random state
state := generateRandomString(8)
codeVerifier := generateRandomString(64)

// Generate the authorization URL
authURL := config.AuthCodeURL(
state,
oauth2.SetAuthURLParam("code_challenge", createCodeChallenge(codeVerifier)),
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
)

// Start a temporary HTTP server to handle the callback
code := make(chan string)
defer close(code)
server := &http.Server{Addr: ":9096"}
// Start the server in a goroutine
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("HTTP server error: %v", err)
}
}()

// Ensure the server is shut down when we're done
defer server.Shutdown(context.Background())

// Open the browser for the user to log in
err := openBrowser(authURL)
if err != nil {
log.Fatal("Could not open browser:", err)
}

http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("state") != state {
http.Error(w, "Invalid state", http.StatusBadRequest)
return
}

code <- r.URL.Query().Get("code")
fmt.Fprintf(w, "Authentication successful! You can close this window now.")
})

// Wait for the authorization code
authCode := <-code

// Exchange the authorization code for a token
token, err := config.Exchange(context.Background(), authCode)
if err != nil {
return "", err
}
return token.AccessToken, nil
}

func openBrowser(url string) error {
var cmd string
var args []string

switch runtime.GOOS {
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
case "darwin":
cmd = "open"
default: // "linux", "freebsd", "openbsd", "netbsd"
cmd = "xdg-open"
}
args = append(args, url)
return exec.Command(cmd, args...).Start()
}
11 changes: 11 additions & 0 deletions internal/wire/cli_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/debricked/cli/internal/file"
"github.com/debricked/cli/internal/fingerprint"
"github.com/debricked/cli/internal/io"
"github.com/debricked/cli/internal/login"
licenseReport "github.com/debricked/cli/internal/report/license"
vulnerabilityReport "github.com/debricked/cli/internal/report/vulnerability"
"github.com/debricked/cli/internal/resolution"
Expand Down Expand Up @@ -92,6 +93,11 @@ func (cc *CliContainer) wire() error {
cc.licenseReporter = licenseReport.Reporter{DebClient: cc.debClient}
cc.vulnerabilityReporter = vulnerabilityReport.Reporter{DebClient: cc.debClient}

cc.authenticator = login.Authenticator{
ClientID: "01919462-7d6e-78e8-aa24-ba779213c90f",
Scopes: []string{"select", "profile", "basicRepo"},
}

return nil
}

Expand All @@ -112,6 +118,7 @@ type CliContainer struct {
callgraph callgraph.IGenerator
cgScheduler callgraph.IScheduler
cgStrategyFactory callgraphStrategy.IFactory
authenticator login.IAuthenticator
}

func (cc *CliContainer) DebClient() client.IDebClient {
Expand Down Expand Up @@ -146,6 +153,10 @@ func (cc *CliContainer) Fingerprinter() fingerprint.IFingerprint {
return cc.fingerprinter
}

func (cc *CliContainer) Authenticator() login.IAuthenticator {
return cc.authenticator
}

func wireErr(err error) error {
return fmt.Errorf("failed to wire with cli-container. Error %s", err)
}

0 comments on commit 8e1432d

Please sign in to comment.