2023-09-13 02:50:15 +00:00
|
|
|
package pkg
|
2023-09-13 08:51:05 +00:00
|
|
|
|
|
|
|
import (
|
2023-09-23 10:45:10 +00:00
|
|
|
"cli-go/internal"
|
2023-09-14 04:20:32 +00:00
|
|
|
"cli-go/internal/api"
|
2023-09-21 09:35:52 +00:00
|
|
|
"cli-go/pkg/model"
|
2023-09-23 04:10:54 +00:00
|
|
|
"cli-go/utils/encoding"
|
2023-09-13 08:51:05 +00:00
|
|
|
"context"
|
|
|
|
"encoding/json"
|
2023-09-21 06:25:40 +00:00
|
|
|
"fmt"
|
2023-09-14 04:20:32 +00:00
|
|
|
"log"
|
|
|
|
|
2023-09-13 08:51:05 +00:00
|
|
|
bolt "go.etcd.io/bbolt"
|
|
|
|
)
|
|
|
|
|
|
|
|
const AccBucket = "accounts"
|
|
|
|
|
2023-09-14 04:20:32 +00:00
|
|
|
func (c *ClICtrl) AddAccount(cxt context.Context) {
|
|
|
|
var flowErr error
|
|
|
|
defer func() {
|
|
|
|
if flowErr != nil {
|
|
|
|
log.Fatal(flowErr)
|
|
|
|
}
|
|
|
|
}()
|
2023-09-23 10:45:10 +00:00
|
|
|
app := internal.GetAppType()
|
2023-09-21 09:35:52 +00:00
|
|
|
cxt = context.WithValue(cxt, "app", string(app))
|
2023-09-23 10:45:10 +00:00
|
|
|
email, flowErr := internal.GetUserInput("Enter email address")
|
2023-09-14 04:20:32 +00:00
|
|
|
if flowErr != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var verifyEmail bool
|
2023-09-21 06:25:40 +00:00
|
|
|
|
2023-09-14 04:20:32 +00:00
|
|
|
srpAttr, flowErr := c.Client.GetSRPAttributes(cxt, email)
|
|
|
|
if flowErr != nil {
|
2023-09-21 03:16:26 +00:00
|
|
|
// if flowErr type is ApiError and status code is 404, then set verifyEmail to true and continue
|
|
|
|
// else return
|
|
|
|
if apiErr, ok := flowErr.(*api.ApiError); ok && apiErr.StatusCode == 404 {
|
|
|
|
verifyEmail = true
|
|
|
|
} else {
|
2023-09-14 04:20:32 +00:00
|
|
|
return
|
|
|
|
}
|
2023-09-21 03:16:26 +00:00
|
|
|
}
|
2023-09-21 06:25:40 +00:00
|
|
|
var authResponse *api.AuthorizationResponse
|
|
|
|
var keyEncKey []byte
|
2023-09-21 03:16:26 +00:00
|
|
|
if verifyEmail || srpAttr.IsEmailMFAEnabled {
|
|
|
|
authResponse, flowErr = c.validateEmail(cxt, email)
|
2023-09-14 04:20:32 +00:00
|
|
|
} else {
|
|
|
|
authResponse, keyEncKey, flowErr = c.signInViaPassword(cxt, email, srpAttr)
|
|
|
|
}
|
|
|
|
if flowErr != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if authResponse.IsMFARequired() {
|
2023-09-15 10:47:38 +00:00
|
|
|
authResponse, flowErr = c.validateTOTP(cxt, authResponse)
|
2023-09-14 04:20:32 +00:00
|
|
|
}
|
2023-09-21 03:16:26 +00:00
|
|
|
if authResponse.EncryptedToken == "" || authResponse.KeyAttributes == nil {
|
2023-09-21 12:07:18 +00:00
|
|
|
panic("no encrypted token or keyAttributes")
|
2023-09-21 03:16:26 +00:00
|
|
|
}
|
2023-09-22 16:15:01 +00:00
|
|
|
secretInfo, decErr := c.decryptAccSecretInfo(cxt, authResponse, keyEncKey)
|
2023-09-21 06:25:40 +00:00
|
|
|
if decErr != nil {
|
|
|
|
flowErr = decErr
|
|
|
|
return
|
2023-09-14 04:20:32 +00:00
|
|
|
}
|
2023-09-22 12:18:14 +00:00
|
|
|
err := c.storeAccount(cxt, email, authResponse.ID, app, secretInfo)
|
2023-09-21 12:07:18 +00:00
|
|
|
if err != nil {
|
|
|
|
flowErr = err
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
fmt.Println("Account added successfully")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-23 10:45:10 +00:00
|
|
|
func (c *ClICtrl) storeAccount(_ context.Context, email string, userID int64, app api.App, secretInfo *model.AccSecretInfo) error {
|
2023-09-21 12:07:18 +00:00
|
|
|
// get password
|
2023-09-22 13:37:12 +00:00
|
|
|
secret := c.CliKey
|
2023-09-21 12:07:18 +00:00
|
|
|
err := c.DB.Update(func(tx *bolt.Tx) error {
|
|
|
|
b, err := tx.CreateBucketIfNotExists([]byte(AccBucket))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-22 05:29:02 +00:00
|
|
|
accInfo := model.Account{
|
2023-09-21 12:07:18 +00:00
|
|
|
Email: email,
|
|
|
|
UserID: userID,
|
2023-09-22 16:15:01 +00:00
|
|
|
MasterKey: *model.MakeEncString(secretInfo.MasterKey, secret),
|
|
|
|
SecretKey: *model.MakeEncString(secretInfo.SecretKey, secret),
|
|
|
|
Token: *model.MakeEncString(secretInfo.Token, secret),
|
2023-09-21 12:07:18 +00:00
|
|
|
App: app,
|
2023-09-23 04:10:54 +00:00
|
|
|
PublicKey: encoding.EncodeBase64(secretInfo.PublicKey),
|
2023-09-21 12:07:18 +00:00
|
|
|
}
|
|
|
|
accInfoBytes, err := json.Marshal(accInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-22 05:29:02 +00:00
|
|
|
accountKey := accInfo.AccountKey()
|
2023-09-21 12:07:18 +00:00
|
|
|
return b.Put([]byte(accountKey), accInfoBytes)
|
|
|
|
})
|
|
|
|
return err
|
2023-09-14 04:20:32 +00:00
|
|
|
}
|
|
|
|
|
2023-09-22 05:29:02 +00:00
|
|
|
func (c *ClICtrl) GetAccounts(cxt context.Context) ([]model.Account, error) {
|
|
|
|
var accounts []model.Account
|
2023-09-14 04:20:32 +00:00
|
|
|
err := c.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket([]byte(AccBucket))
|
|
|
|
err := b.ForEach(func(k, v []byte) error {
|
2023-09-22 05:29:02 +00:00
|
|
|
var info model.Account
|
2023-09-14 04:20:32 +00:00
|
|
|
err := json.Unmarshal(v, &info)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-22 04:35:25 +00:00
|
|
|
accounts = append(accounts, info)
|
2023-09-14 04:20:32 +00:00
|
|
|
return nil
|
|
|
|
})
|
2023-09-13 08:51:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-14 04:20:32 +00:00
|
|
|
return nil
|
2023-09-13 08:51:05 +00:00
|
|
|
})
|
2023-09-22 04:35:25 +00:00
|
|
|
return accounts, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ClICtrl) ListAccounts(cxt context.Context) error {
|
|
|
|
accounts, err := c.GetAccounts(cxt)
|
2023-09-14 04:20:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-22 04:35:25 +00:00
|
|
|
fmt.Printf("Configured accounts: %d\n", len(accounts))
|
|
|
|
for _, acc := range accounts {
|
|
|
|
fmt.Println("====================================")
|
|
|
|
fmt.Println("Email: ", acc.Email)
|
2023-09-22 05:29:02 +00:00
|
|
|
fmt.Println("ID: ", acc.UserID)
|
|
|
|
fmt.Println("App: ", acc.App)
|
2023-09-22 04:35:25 +00:00
|
|
|
fmt.Println("====================================")
|
|
|
|
}
|
|
|
|
return nil
|
2023-09-13 08:51:05 +00:00
|
|
|
}
|