Skip to content

Commit

Permalink
support for token auth (#8)
Browse files Browse the repository at this point in the history
- fixes #7 
- fixes #5
  • Loading branch information
rsds authored and foundev committed Mar 8, 2021
1 parent 00480f0 commit 92f2c53
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 28 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
issues:
exclude:
SA1019
32 changes: 26 additions & 6 deletions cmd/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,34 @@ func DBUsage() string {

// ExecuteDB launches several different subcommands and as of today is the main entry point
// into automation of Astra
func ExecuteDB(args []string, confFile string, verbose bool) error {
clientInfo, err := pkg.ReadLogin(confFile)
func ExecuteDB(args []string, confFile pkg.ConfFiles, verbose bool) error {
hasToken, err := confFile.HasToken()
if err != nil {
return fmt.Errorf("%v", err)
return fmt.Errorf("unable to read conf file %v with error %v", confFile.TokenPath, err)
}
client, err := astraops.Authenticate(clientInfo, verbose)
if err != nil {
return fmt.Errorf("authenticate failed with error %v", err)
var client *astraops.AuthenticatedClient
if hasToken {
token, err := pkg.ReadToken(confFile.TokenPath)
if err != nil {
return fmt.Errorf("found token at %v but unable to read it with error %v", confFile.TokenPath, err)
}
client = astraops.AuthenticateToken(token, verbose)
} else {
hasSa, err := confFile.HasServiceAccount()
if err != nil {
return fmt.Errorf("unable to read conf file %v with error %v", confFile.SaPath, err)
}
if !hasSa {
return fmt.Errorf("unable to access any configuration, run astra-cli login first")
}
clientInfo, err := pkg.ReadLogin(confFile.SaPath)
if err != nil {
return fmt.Errorf("%v", err)
}
client, err = astraops.Authenticate(clientInfo, verbose)
if err != nil {
return fmt.Errorf("authenticate failed with error %v", err)
}
}
if len(args) == 0 {
return &pkg.ParseError{
Expand Down
6 changes: 4 additions & 2 deletions cmd/db/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,22 @@ func ExecuteCreate(args []string, client *astraops.AuthenticatedClient) error {
Err: err,
}
}
capacity := int32(*createDbCapacityUnitFlag)
createDb := astraops.CreateDb{
Name: *createDbNameFlag,
Keyspace: *createDbKeyspaceFlag,
CapacityUnits: *createDbCapacityUnitFlag,
CapacityUnits: capacity,
Region: *createDbRegionFlag,
User: *createDbUserFlag,
Password: *createDbPasswordFlag,
Tier: *createDbTierFlag,
CloudProvider: *createDbCloudProviderFlag,
}
id, _, err := client.CreateDb(createDb)
db, err := client.CreateDb(createDb)
if err != nil {
return fmt.Errorf("unable to create '%v' with error %v", createDb, err)
}
id := db.ID
fmt.Printf("database %v created\n", id)
return nil
}
4 changes: 2 additions & 2 deletions cmd/db/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func ExecuteGet(args []string, client *astraops.AuthenticatedClient) error {
}
}
id := args[0]
var db astraops.DataBase
var db astraops.Database
var err error
if db, err = client.FindDb(id); err != nil {
return fmt.Errorf("unable to get '%s' with error %v\n", id, err)
Expand All @@ -59,7 +59,7 @@ func ExecuteGet(args []string, client *astraops.AuthenticatedClient) error {
case "text":
var rows [][]string
rows = append(rows, []string{"name", "id", "status"})
rows = append(rows, []string{db.Info.Name, db.ID, db.Status})
rows = append(rows, []string{db.Info.Name, db.ID, string(db.Status)})
for _, row := range pkg.PadColumns(rows) {
fmt.Println(strings.Join(row, " "))
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/db/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func ExecuteList(args []string, client *astraops.AuthenticatedClient) error {
Err: err,
}
}
var dbs []astraops.DataBase
var dbs []astraops.Database
var err error
if dbs, err = client.ListDb(*includeFlag, *providerFlag, *startingAfterFlag, int32(*limitFlag)); err != nil {
return fmt.Errorf("unable to get list of dbs with error %v", err)
Expand All @@ -56,7 +56,7 @@ func ExecuteList(args []string, client *astraops.AuthenticatedClient) error {
var rows [][]string
rows = append(rows, []string{"name", "id", "status"})
for _, db := range dbs {
rows = append(rows, []string{db.Info.Name, db.ID, db.Status})
rows = append(rows, []string{db.Info.Name, db.ID, string(db.Status)})
}
for _, row := range pkg.PadColumns(rows) {
fmt.Println(strings.Join(row, " "))
Expand Down
2 changes: 1 addition & 1 deletion cmd/db/tiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func ExecuteTiers(args []string, client *astraops.AuthenticatedClient) error {
rows = append(rows, []string{
tier.Tier,
tier.CloudProvider,
tier.RegionDisplay,
tier.Region,
fmt.Sprintf("%v/%v", tier.DatabaseCountUsed, tier.DatabaseCountLimit),
fmt.Sprintf("%v/%v", tier.CapacityUnitsUsed, tier.CapacityUnitsLimit),
fmt.Sprintf("$%.2f", costMonth),
Expand Down
2 changes: 1 addition & 1 deletion cmd/db/unpark.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func ExecuteUnpark(args []string, client *astraops.AuthenticatedClient) error {
}
id := args[0]
fmt.Printf("starting to unpark database %v\n", id)
if err := client.UnPark(id); err != nil {
if err := client.Unpark(id); err != nil {
return fmt.Errorf("unable to unpark '%s' with error %v\n", id, err)
}
fmt.Printf("database %v unparked\n", id)
Expand Down
15 changes: 11 additions & 4 deletions cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,25 @@ var loginCmd = flag.NewFlagSet("login", flag.ExitOnError)
var clientIDFlag = loginCmd.String("id", "", "clientId from service account. Ignored if -json flag is used.")
var clientNameFlag = loginCmd.String("name", "", "clientName from service account. Ignored if -json flag is used.")
var clientSecretFlag = loginCmd.String("secret", "", "clientSecret from service account. Ignored if -json flag is used.")
var clientJSONFlag = loginCmd.String("json", "", "copy the json for service account from the Astra page")
var clientJSONFlag = loginCmd.String("json", "", "copy the json for service account from the Astra site")
var authTokenFlag = loginCmd.String("token", "", "authtoken generated with enough rights to perform the devops actions. Generated from the Astra site")

//LoginUsage returns the usage text for login
func LoginUsage() string {
return pkg.PrintFlags(loginCmd, "astra-cli login", "stores off login credentials for astra devops api")
}

//ExecuteLogin logs into Astra
func ExecuteLogin(args []string, confDir string, confFile string) error {
func ExecuteLogin(args []string, confDir string, confFiles pkg.ConfFiles) error {
if err := loginCmd.Parse(args); err != nil {
return &pkg.ParseError{
Args: args,
Err: fmt.Errorf("incorrect options with error %v", err),
}
}
if authTokenFlag != nil {
return makeConf(confDir, confFiles.TokenPath, *authTokenFlag)
}
var clientJSON string
if clientJSONFlag != nil {
clientJSON = *clientJSONFlag
Expand Down Expand Up @@ -71,13 +75,16 @@ func ExecuteLogin(args []string, confDir string, confFile string) error {
Err: fmt.Errorf("clientSecret missing"),
}
}

} else {
clientID := *clientIDFlag
clientName := *clientNameFlag
clientSecret := *clientSecretFlag
clientJSON = fmt.Sprintf("{\"clientId\":\"%v\",\"clientName\":\"%v\",\"clientSecret\":\"%v:\"}", clientID, clientName, clientSecret)
}
return makeConf(confDir, confFiles.SaPath, clientJSON)
}

func makeConf(confDir, confFile, content string) error {
if err := os.MkdirAll(confDir, 0700); err != nil {
return fmt.Errorf("unable to get make config directory with error %s", err)
}
Expand All @@ -92,7 +99,7 @@ func ExecuteLogin(args []string, confDir string, confFile string) error {
}()
writer := bufio.NewWriter(f)
//safe to write after validation
_, err = writer.Write([]byte(clientJSON))
_, err = writer.Write([]byte(content))
if err != nil {
return fmt.Errorf("error writing file")
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module github.com/rsds143/astra-cli

go 1.16

require github.com/rsds143/astra-devops-sdk-go v0.2.0
require github.com/rsds143/astra-devops-sdk-go v0.3.0
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github.com/rsds143/astra-devops-sdk-go v0.2.0 h1:Oq5fWejjQ7pr3D7IeFrEO7hybvGHa++MdTMtvA+VEIU=
github.com/rsds143/astra-devops-sdk-go v0.2.0/go.mod h1:LQaUwm75Ydy/z71nl466Xv0yw8ib5b9L6laTFXbvtHU=
github.com/rsds143/astra-devops-sdk-go v0.3.0 h1:ymkYLcf5AfM1zYbyEmiETHzR34HcItp4n9b4GKGVj+Q=
github.com/rsds143/astra-devops-sdk-go v0.3.0/go.mod h1:LQaUwm75Ydy/z71nl466Xv0yw8ib5b9L6laTFXbvtHU=
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func usage() {
}
func main() {
flag.Parse()
confDir, confFile, err := pkg.GetHome()
confDir, confFiles, err := pkg.GetHome()
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(3)
Expand All @@ -46,9 +46,9 @@ func main() {
}
switch flag.Arg(0) {
case "login":
err = cmd.ExecuteLogin(flag.Args()[1:], confDir, confFile)
err = cmd.ExecuteLogin(flag.Args()[1:], confDir, confFiles)
case "db":
err = cmd.ExecuteDB(flag.Args()[1:], confFile, *verbose)
err = cmd.ExecuteDB(flag.Args()[1:], confFiles, *verbose)
default:
fmt.Printf("%q is not valid command.\n", flag.Arg(1))
os.Exit(1)
Expand Down
67 changes: 63 additions & 4 deletions pkg/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,78 @@ import (
"io"
"os"
"path"
"strings"
)

//ConfFiles supports both formats of credentials and will say if the token one is present
type ConfFiles struct {
TokenPath string
SaPath string
}

//HasServiceAccount returns true if there is a service account file present and accessible
func (c ConfFiles) HasServiceAccount() (bool, error) {
if _, err := os.Stat(c.SaPath); err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("warning error of %v is unexpected", err)
}
return true, nil
}

//Hastoken returns true if there is a token file present and accessible
func (c ConfFiles) HasToken() (bool, error) {
if _, err := os.Stat(c.TokenPath); err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("warning error of %v is unexpected", err)
}
return true, nil
}

// GetHome returns the configuration directory and file
// error will return if there is no user home folder
func GetHome() (confDir string, confFile string, err error) {
func GetHome() (confDir string, confFiles ConfFiles, err error) {
var home string
home, err = os.UserHomeDir()
if err != nil {
return "", "", fmt.Errorf("unable to get user home directory with error %s", err)
return "", ConfFiles{}, fmt.Errorf("unable to get user home directory with error %s", err)
}
confDir = path.Join(home, ".config", "astra")
confFile = path.Join(confDir, "sa.json")
return confDir, confFile, nil

tokenFile := path.Join(confDir, "token")
saFile := path.Join(confDir, "sa.json")
return confDir, ConfFiles{
TokenPath: tokenFile,
SaPath: saFile,
}, nil
}

// ReadToken retrieves the login from the specified json file
func ReadToken(tokenFile string) (string, error) {
f, err := os.Open(tokenFile)
if err != nil {
return "", &FileNotFoundError{
Path: tokenFile,
Err: fmt.Errorf("unable to read login file with error %w", err),
}
}
defer func() {
if err := f.Close(); err != nil {
fmt.Printf("warning unable to close %v with error %v", tokenFile, err)
}
}()
b, err := io.ReadAll(f)
if err != nil {
return "", fmt.Errorf("unable to read login file %s with error %w", tokenFile, err)
}
token := strings.Trim(string(b), "\n")
if !strings.HasPrefix(token, "AstraCS") {
return "", fmt.Errorf("invalid token in login file %s with error %s", tokenFile, err)
}
return token, nil
}

// ReadLogin retrieves the login from the specified json file
Expand Down

0 comments on commit 92f2c53

Please sign in to comment.