Skip to content
This repository has been archived by the owner on Oct 9, 2024. It is now read-only.

Commit

Permalink
add grant_type client_credentials to WE and add some test
Browse files Browse the repository at this point in the history
  • Loading branch information
Chadiii committed Apr 26, 2024
1 parent f9877c3 commit 0cc0766
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 114 deletions.
3 changes: 2 additions & 1 deletion cmd/feature_experimentation/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func checkSingleFlag(bool1, bool2 bool) bool {

// createCmd represents the create command
var loginCmd = &cobra.Command{
Use: "login [--credential-file] | [-u <username> | --username=<username>] [-i <clientID> | --client-id=<clientID>] [-s <clientSecret> | --client-secret=<clientSecret>]",
Use: "login [--credential-file] | [-u <username> | --username=<username>] [-i <clientID> | --client-id=<clientID>] [-s <clientSecret> | --client-secret=<clientSecret>] [-a <accountID> | --account-id=<accountID>]",
Short: "login",
Long: `login`,
Run: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -89,6 +89,7 @@ var loginCmd = &cobra.Command{
fmt.Fprintf(cmd.OutOrStderr(), "error occurred: %s", err)
return
}

if slices.Contains(existingCredentials, Username) {
if AccountId != "" {
err := config.SelectAuth(utils.FEATURE_EXPERIMENTATION, Username)
Expand Down
19 changes: 15 additions & 4 deletions cmd/web_experimentation/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,28 @@ var testAuth models.Auth
var testAuthList []models.Auth

func TestAuthCommand(t *testing.T) {
output, _ := utils.ExecuteCommand(AuthCmd)
output, err := utils.ExecuteCommand(AuthCmd)

assert.Nil(t, err)
assert.Contains(t, output, "Manage your CLI authentication for web experimentation\n\nUsage:\n authentication [login|get|list|delete]")
}

func TestAuthHelpCommand(t *testing.T) {
output, _ := utils.ExecuteCommand(AuthCmd, "--help")
output, err := utils.ExecuteCommand(AuthCmd, "--help")

assert.Nil(t, err)
assert.Contains(t, output, "Manage your CLI authentication for web experimentation\n\nUsage:\n authentication [login|get|list|delete]")
}

func TestAuthLoginCommand(t *testing.T) {
successOutput, _ := utils.ExecuteCommand(AuthCmd, "login", "-u=test_auth", "--password=password", "--totp=00000")

failOutput, _ := utils.ExecuteCommand(AuthCmd, "login", "-u=test_auth", "--client-id=CI", "--client-secret=CS")

assert.Equal(t, "Error while login, required fields (username, client ID, client secret, account id)\n", failOutput)

successOutput, err := utils.ExecuteCommand(AuthCmd, "login", "-u=test_auth", "--client-id=CI", "--client-secret=CS", "--account-id=AI")

assert.Nil(t, err)
assert.Equal(t, "Credential created successfully\n", successOutput)
}

Expand Down Expand Up @@ -70,7 +81,7 @@ func TestAuthGetCommand(t *testing.T) {

assert.Nil(t, err)

//assert.Equal(t, feature_experimentation.TestAuth, testAuth)
assert.Equal(t, web_experimentation.TestAuth, testAuth)
}

func TestAuthDeleteCommand(t *testing.T) {
Expand Down
164 changes: 61 additions & 103 deletions cmd/web_experimentation/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ package auth
import (
"fmt"
"log"
"net/http"
"os"
"os/exec"
"runtime"
"slices"
"time"

"github.com/flagship-io/flagship/utils"
"github.com/flagship-io/flagship/utils/config"
Expand All @@ -20,10 +15,8 @@ import (
)

var (
browser bool
password string
redirectUri string
totp string
credentialsFile string
accountId string
)

func checkSingleFlag(bool1, bool2 bool) bool {
Expand All @@ -38,113 +31,93 @@ func checkSingleFlag(bool1, bool2 bool) bool {
return count == 1
}

func openLink(url string) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "linux":
cmd = exec.Command("xdg-open", url)
case "darwin":
cmd = exec.Command("open", url)
case "windows":
cmd = exec.Command("cmd", "/c", "start", url)
default:
return fmt.Errorf("unsupported operating system")
}
err := cmd.Run()
return err
}

// createCmd represents the create command
var loginCmd = &cobra.Command{
Use: "login [--browser] [-i <clientID> | --client-id=<clientID>] [-s <clientSecret> | --client-secret=<clientSecret>] | [-u <username> | --username=<username>] [--password <password>]",
Use: "login [--credential-file] | [-u <username> | --username=<username>] [-i <clientID> | --client-id=<clientID>] [-s <clientSecret> | --client-secret=<clientSecret>] [-a <accountID> | --account-id=<accountID>]",
Short: "login",
Long: `login`,
Run: func(cmd *cobra.Command, args []string) {
if !checkSingleFlag(browser, Username != "") {
if !checkSingleFlag(credentialsFile != "", Username != "") {
log.Fatalf("error occurred: %s", "1 flag is required. (browser, username)")
}

if browser {
codeChan := make(chan string)
clientID := utils.CLIENT_ID
clientSecret := utils.CLIENT_SECRET

if ClientID != "" {
clientID = ClientID
if credentialsFile != "" {
v, err := config.ReadCredentialsFromFile(credentialsFile)
if err != nil {
log.Fatalf("error occurred: %v", err)
}

if ClientSecret != "" {
clientSecret = ClientSecret
if v.GetString("username") == "" || v.GetString("client_id") == "" || v.GetString("client_secret") == "" || v.GetString("account_id") == "" {
fmt.Fprintln(cmd.OutOrStderr(), "Error while login, required fields (username, client ID, client secret, account id)")
return
}

var url = fmt.Sprintf("https://auth.abtasty.com/authorize?client_id=%s&client_secret=%s&redirect_uri=http://localhost:8010/auth/callback", clientID, clientSecret)

if err := openLink(url); err != nil {
log.Fatalf("Error opening link: %s", err)
authenticationResponse, err := common.HTTPCreateTokenWE(v.GetString("client_id"), v.GetString("client_secret"), v.GetString("account_id"))
if err != nil {
fmt.Fprintf(cmd.OutOrStderr(), "error occurred: %s", err)
return
}

go func() {
http.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
handleCallback(w, r, codeChan)
})

if err := http.ListenAndServe("127.0.0.1:8010", nil); err != nil {
log.Fatalf("Error starting callback server: %s", err)
}
}()

code := <-codeChan

if code != "" {
authenticationResponse, err := common.HTTPCreateTokenWEAuthorizationCode(clientID, clientSecret, code)
if err != nil {
log.Fatalf("error occurred: %s", err)
return
}
err = config.CreateAuthFile(utils.WEB_EXPERIMENTATION, v.GetString("username"), v.GetString("client_id"), v.GetString("client_secret"), authenticationResponse)
if err != nil {
log.Fatalf("error occurred: %v", err)
}

if authenticationResponse.AccessToken == "" {
log.Fatal("Credentials not valid.")
}
// Waiting for fix to implemente route to get username "/users/me"
// add flag for redirect uri (for vscode etc...)
err = config.SelectAuth(utils.WEB_EXPERIMENTATION, v.GetString("username"))
if err != nil {
log.Fatalf("error occurred: %v", err)
}

fmt.Fprintln(cmd.OutOrStdout(), "Credential created successfully")
return
err = config.SetAccountID(utils.WEB_EXPERIMENTATION, v.GetString("account_id"))
if err != nil {
log.Fatalf("error occurred: %s", err)
}

fmt.Fprintln(cmd.OutOrStderr(), "Error occurred.")
fmt.Fprintln(cmd.OutOrStdout(), "Credential created successfully")
return
}

if Username != "" {
existingCredentials, err := config.GetUsernames(utils.WEB_EXPERIMENTATION)
if err != nil {
log.Fatalf("error occurred: %s", err)
fmt.Fprintf(cmd.OutOrStderr(), "error occurred: %s", err)
return
}

if slices.Contains(existingCredentials, Username) {
err := config.SelectAuth(utils.WEB_EXPERIMENTATION, Username)
if err != nil {
log.Fatalf("error occurred: %v", err)
if accountId != "" {
err := config.SelectAuth(utils.WEB_EXPERIMENTATION, Username)
if err != nil {
log.Fatalf("error occurred: %v", err)
}

err = config.SetAccountID(utils.WEB_EXPERIMENTATION, accountId)
if err != nil {
log.Fatalf("error occurred: %s", err)
}

fmt.Fprintln(cmd.OutOrStdout(), "Credential changed successfully to "+Username)
return
}

fmt.Fprintln(cmd.OutOrStdout(), "Credential changed successfully to "+Username)
fmt.Fprintln(cmd.OutOrStderr(), "Error while login, required fields (account id)")
return
}

if password == "" || totp == "" {
fmt.Fprintln(cmd.OutOrStderr(), "Error while login, required fields (password, totp)")
if ClientID == "" || ClientSecret == "" || accountId == "" {
fmt.Fprintln(cmd.OutOrStderr(), "Error while login, required fields (username, client ID, client secret, account id)")
return
}
authenticationResponse, err := common.HTTPCreateTokenWEPassword(utils.CLIENT_ID, utils.CLIENT_SECRET, Username, password, totp)
authenticationResponse, err := common.HTTPCreateTokenWE(ClientID, ClientSecret, accountId)
if err != nil {
log.Fatalf("error occurred: %s", err)
fmt.Fprintln(cmd.OutOrStderr(), err)
return
}

if authenticationResponse.AccessToken == "" {
log.Fatal("Credentials not valid.")
fmt.Fprintln(cmd.OutOrStderr(), "Error while login, client_id or client_secret not valid")
return
}

err = config.CreateAuthFile(utils.WEB_EXPERIMENTATION, Username, "", "", authenticationResponse)
err = config.CreateAuthFile(utils.WEB_EXPERIMENTATION, Username, ClientID, ClientSecret, authenticationResponse)
if err != nil {
log.Fatalf("error occurred: %v", err)
}
Expand All @@ -154,6 +127,11 @@ var loginCmd = &cobra.Command{
log.Fatalf("error occurred: %v", err)
}

err = config.SetAccountID(utils.WEB_EXPERIMENTATION, accountId)
if err != nil {
log.Fatalf("error occurred: %s", err)
}

fmt.Fprintln(cmd.OutOrStdout(), "Credential created successfully")
}

Expand All @@ -162,32 +140,12 @@ var loginCmd = &cobra.Command{

func init() {

loginCmd.Flags().StringVarP(&Username, "username", "u", "", "username")

loginCmd.Flags().StringVarP(&ClientID, "client-id", "i", "", "client ID of an auth")
loginCmd.Flags().StringVarP(&ClientSecret, "client-secret", "s", "", "client secret of an auth")

loginCmd.Flags().BoolVarP(&browser, "browser", "", false, "generate link for browser")
loginCmd.Flags().StringVarP(&Username, "username", "u", "", "username")
loginCmd.Flags().StringVarP(&redirectUri, "redirect-uri", "", "http://abtasty.com", "redirect uri")
loginCmd.Flags().StringVarP(&password, "password", "", "", "password")
loginCmd.Flags().StringVarP(&totp, "totp", "", "", "totp")
loginCmd.Flags().StringVarP(&accountId, "account-id", "a", "", "account id of an auth")
loginCmd.Flags().StringVarP(&credentialsFile, "credential-file", "p", "", "config file to create")

AuthCmd.AddCommand(loginCmd)
}

func handleCallback(w http.ResponseWriter, r *http.Request, codeChan chan<- string) {
code := r.URL.Query().Get("code")
if code == "" {
http.Error(w, "No token found in URL", http.StatusBadRequest)
os.Exit(0)
return
}

codeChan <- code

http.Redirect(w, r, redirectUri, http.StatusSeeOther)

go func() {
time.Sleep(5 * time.Second)
close(codeChan)
}()
}
3 changes: 2 additions & 1 deletion models/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ type TokenFE struct {
type TokenResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Scope string `json:"scope"`
}

type ClientCredentialsRequest struct {
GrantType string `json:"grant_type"`
Scope string `json:"scope"`
Scope string `json:"scope,omitempty"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
}
Expand Down
26 changes: 26 additions & 0 deletions utils/http_request/common/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ func HTTPCreateTokenFE(clientId, clientSecret, accountId string) (models.TokenRe
return authenticationResponse, err
}

func HTTPCreateTokenWE(clientId, clientSecret, accountId string) (models.TokenResponse, error) {
var authenticationResponse models.TokenResponse
authRequest := models.ClientCredentialsRequest{
ClientID: clientId,
ClientSecret: clientSecret,
GrantType: "client_credentials",
}

authRequestJSON, err := json.Marshal(authRequest)
if err != nil {
return models.TokenResponse{}, err
}

respBody, err := HTTPRequest[models.TokenFE](http.MethodPost, utils.GetHostWebExperimentationAuth()+"/v1/token", authRequestJSON)
if err != nil {
return models.TokenResponse{}, err
}

err = json.Unmarshal(respBody, &authenticationResponse)
if err != nil {
return models.TokenResponse{}, err
}

return authenticationResponse, err
}

func HTTPCreateTokenWEAuthorizationCode(client_id, client_secret, code string) (models.TokenResponse, error) {
var authenticationResponse models.TokenResponse
authRequest := models.AuthorizationCodeRequest{
Expand Down
5 changes: 2 additions & 3 deletions utils/mock_function/web_experimentation/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import (

var TestAuth = models.Auth{
Username: "test_auth",
ClientID: "",
ClientSecret: "",
ClientID: "CI",
ClientSecret: "CS",
Token: "testAccessToken",
RefreshToken: "testRefreshToken",
}

func InitMockAuth() {
Expand Down
3 changes: 1 addition & 2 deletions utils/mock_function/web_experimentation/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ func APIToken() {
token := "token"

testAuthenticationResponse := models.TokenResponse{
AccessToken: "testAccessToken",
RefreshToken: "testRefreshToken",
AccessToken: "testAccessToken",
}

httpmock.RegisterResponder("GET", utils.GetHostWebExperimentationAuth()+"/v1/token?access_token="+token,
Expand Down

0 comments on commit 0cc0766

Please sign in to comment.