ente/cli/pkg/account.go

204 lines
5.3 KiB
Go
Raw Normal View History

2023-09-13 02:50:15 +00:00
package pkg
2023-09-13 08:51:05 +00:00
import (
"context"
"encoding/json"
"fmt"
2023-10-21 09:26:13 +00:00
"github.com/ente-io/cli/internal"
"github.com/ente-io/cli/internal/api"
"github.com/ente-io/cli/pkg/model"
"github.com/ente-io/cli/utils/encoding"
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()
cxt = context.WithValue(cxt, "app", string(app))
2023-10-16 18:18:00 +00:00
dir := internal.GetExportDir()
if dir == "" {
flowErr = fmt.Errorf("export directory not set")
return
}
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-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
}
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 {
2023-09-27 05:32:36 +00:00
authResponse, keyEncKey, flowErr = c.signInViaPassword(cxt, srpAttr)
2023-09-14 04:20:32 +00:00
}
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 {
log.Fatalf("missing key attributes or token.\nNote: Please use the mobile,web or desktop app to create a new account.\nIf you are trying to login to an existing account, report a bug.")
2023-09-21 03:16:26 +00:00
}
2023-09-22 16:15:01 +00:00
secretInfo, decErr := c.decryptAccSecretInfo(cxt, authResponse, keyEncKey)
if decErr != nil {
flowErr = decErr
return
2023-09-14 04:20:32 +00:00
}
2023-10-16 18:18:00 +00:00
err := c.storeAccount(cxt, email, authResponse.ID, app, secretInfo, dir)
2023-09-21 12:07:18 +00:00
if err != nil {
flowErr = err
return
} else {
fmt.Println("Account added successfully")
2023-10-25 11:52:22 +00:00
fmt.Println("run `ente export` to initiate export of your account data")
2023-09-21 12:07:18 +00:00
}
}
2023-10-16 18:18:00 +00:00
func (c *ClICtrl) storeAccount(_ context.Context, email string, userID int64, app api.App, secretInfo *model.AccSecretInfo, exportDir string) error {
2023-09-21 12:07:18 +00:00
// get password
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-27 08:39:44 +00:00
MasterKey: *model.MakeEncString(secretInfo.MasterKey, c.KeyHolder.DeviceKey),
SecretKey: *model.MakeEncString(secretInfo.SecretKey, c.KeyHolder.DeviceKey),
Token: *model.MakeEncString(secretInfo.Token, c.KeyHolder.DeviceKey),
2023-09-21 12:07:18 +00:00
App: app,
2023-09-23 04:10:54 +00:00
PublicKey: encoding.EncodeBase64(secretInfo.PublicKey),
2023-10-16 18:18:00 +00:00
ExportDir: exportDir,
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)
fmt.Println("ID: ", acc.UserID)
fmt.Println("App: ", acc.App)
fmt.Println("ExportDir:", acc.ExportDir)
2023-09-22 04:35:25 +00:00
fmt.Println("====================================")
}
return nil
2023-09-13 08:51:05 +00:00
}
2024-03-08 12:05:16 +00:00
func (c *ClICtrl) UpdateAccount(ctx context.Context, params model.AccountCommandParams) error {
accounts, err := c.GetAccounts(ctx)
if err != nil {
return err
}
var acc *model.Account
for _, a := range accounts {
if a.Email == params.Email && a.App == params.App {
acc = &a
break
}
}
if acc == nil {
2023-10-18 07:11:20 +00:00
return fmt.Errorf("account not found, use `account list` to list accounts")
}
if params.ExportDir != nil && *params.ExportDir != "" {
2023-10-16 18:18:00 +00:00
_, err := internal.ValidateDirForWrite(*params.ExportDir)
if err != nil {
return err
}
acc.ExportDir = *params.ExportDir
}
err = c.DB.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(AccBucket))
if err != nil {
return err
}
accInfoBytes, err := json.Marshal(acc)
if err != nil {
return err
}
accountKey := acc.AccountKey()
return b.Put([]byte(accountKey), accInfoBytes)
})
return err
2024-03-08 12:05:16 +00:00
}
2024-03-08 12:05:16 +00:00
func (c *ClICtrl) GetToken(ctx context.Context, params model.AccountCommandParams) error {
accounts, err := c.GetAccounts(ctx)
if err != nil {
return err
}
var acc *model.Account
for _, a := range accounts {
if a.Email == params.Email && a.App == params.App {
acc = &a
break
}
}
if acc == nil {
return fmt.Errorf("account not found, use `account list` to list accounts")
}
secretInfo, err := c.KeyHolder.LoadSecrets(*acc)
if err != nil {
return err
}
fmt.Println(secretInfo.TokenStr())
return nil
}