Skip to content

Commit

Permalink
Add support of config file
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikhail Borovikov committed Nov 7, 2021
1 parent 08c8108 commit d99607a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 42 deletions.
60 changes: 35 additions & 25 deletions cmd/secretable.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import (
"embed"
"fmt"
"os"
"path/filepath"
"strings"
"time"

"secretable/pkg/config"
"secretable/pkg/crypto"
"secretable/pkg/handlers"
"secretable/pkg/localizator"
Expand All @@ -38,12 +40,7 @@ const (
)

var opts struct {
TGToken string `short:"t" long:"tg_bot_token" description:"Telegram bot token" required:"true"`
GoogleCredentials string `short:"g" long:"google_credentials" description:"Path to Google credentials JSON file" required:"true"`
SpreadsheetID string `short:"s" long:"spreadsheet_id" description:"Spreadsheet ID" required:"true"`
CleanupTime int `short:"c" long:"cleanup_timeout" default:"30" description:"Received and send messages cleanup timeout in seconds"`
Unencrypted bool `short:"u" long:"unencrypted" description:"Unencrypted mode"`
Salt string `long:"salt" env:"ST_SALT" description:"Salt for encryption with a master password. Automatically set to environment variable. If not specified, a new one is generated and setted."`
ConfigFile string `short:"c" default:"" long:"config" description:"Path to config file" required:"false"`
}

var cmds = []tb.Command{
Expand Down Expand Up @@ -77,17 +74,32 @@ func main() {
return
}

if opts.ConfigFile == "" {
homedir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err.Error())
return
}
opts.ConfigFile = filepath.Join(homedir, ".secretable", "config.yaml")
}

conf, err := config.ParseFromFile(opts.ConfigFile)
if err != nil {
log.Fatal(err.Error())
return
}

log.Info("⏳ Initialization Secretable")
log.Info("📝 Google credentials: " + conf.GoogleCredentials)
log.Info("📄 Spreadsheet ID: " + conf.SpreadsheetID)
log.Info("🧹 Cleanup timeout: " + fmt.Sprint(conf.CleanupTimeout, " sec"))

log.Info("📝 Google credentials: " + opts.GoogleCredentials)
log.Info("📄 Spreadsheet ID: " + opts.SpreadsheetID)
log.Info("🧹 Cleanup timeout: " + fmt.Sprint(opts.CleanupTime, " sec"))
if opts.Unencrypted {
if conf.Unencrypted {
log.Info("🔓 Unecrypted mode")
} else {
log.Info("🔐 Encrypted mode")

if opts.Salt == "" {
if conf.Salt == "" {
log.Info("🧂 Salt not set and will be generated automatically.")
} else {
log.Info("🧂 Salt setted")
Expand All @@ -101,30 +113,28 @@ func main() {

log.Info("🌎 Supported locales: " + strings.Join(locales.GetLocales(), ", "))

tableProvider, err := tables.NewTablesProvider(opts.GoogleCredentials, opts.SpreadsheetID)
tableProvider, err := tables.NewTablesProvider(conf.GoogleCredentials, conf.SpreadsheetID)
if err != nil {
log.Fatal("Unable to create tables provider: " + err.Error())
}

if !opts.Unencrypted {
if opts.Salt == "" {
if !conf.Unencrypted {
if conf.Salt == "" {
s, _ := crypto.MakeRandom(32)
opts.Salt = base58.Encode(s)
os.Setenv("ST_SALT", opts.Salt)
conf.Salt = base58.Encode(s)
}

}

b, err := tb.NewBot(tb.Settings{
Token: opts.TGToken,
Token: conf.TelegramBotToken,
Poller: &tb.LongPoller{Timeout: longPollerTimeout * time.Second},
})

if err != nil {
log.Fatal("Unable to create new bot instance: " + err.Error())
}

handler := handlers.NewHandler(b, tableProvider, locales, opts.CleanupTime, !opts.Unencrypted)
handler := handlers.NewHandler(b, tableProvider, locales, conf, opts.ConfigFile, !conf.Unencrypted)

startMessage := "Welcome! Just enter text into the chat to find secrets or use the commands:\n\n"
for _, cmd := range cmds {
Expand All @@ -133,13 +143,13 @@ func main() {

b.Handle("/start", middleware(false, false, false, 0, handler, handler.MakeStart(startMessage)))

b.Handle("/id", middleware(false, false, false, opts.CleanupTime, handler, handler.ID))
b.Handle("/generate", middleware(false, false, false, opts.CleanupTime, handler, handler.Generate))
b.Handle("/id", middleware(false, false, false, conf.CleanupTimeout, handler, handler.ID))
b.Handle("/generate", middleware(false, false, false, conf.CleanupTimeout, handler, handler.Generate))

b.Handle("/add", middleware(true, false, true, opts.CleanupTime, handler, handler.Set))
b.Handle("/setpass", middleware(true, false, true, opts.CleanupTime, handler, handler.ResetPass))
b.Handle("/delete", middleware(true, false, true, opts.CleanupTime, handler, handler.Delete))
b.Handle(tb.OnText, middleware(true, true, true, opts.CleanupTime, handler, handler.Query))
b.Handle("/add", middleware(true, false, true, conf.CleanupTimeout, handler, handler.Set))
b.Handle("/setpass", middleware(true, false, true, conf.CleanupTimeout, handler, handler.ResetPass))
b.Handle("/delete", middleware(true, false, true, conf.CleanupTimeout, handler, handler.Delete))
b.Handle(tb.OnText, middleware(true, true, true, conf.CleanupTimeout, handler, handler.Query))

if err = b.SetCommands(cmds); err != nil {
log.Error("Error of setting commands: " + err.Error())
Expand Down
4 changes: 4 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"io"
"os"
"path/filepath"
"secretable/pkg/log"

"gopkg.in/yaml.v3"
Expand All @@ -38,6 +39,9 @@ func ParseFromFile(path string) (config *Config, err error) {
file, err := os.Open(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return nil, err
}
if file, err = os.Create(path); err != nil {
return nil, err
}
Expand Down
26 changes: 18 additions & 8 deletions pkg/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"fmt"
"html"
"math/rand"
"os"
"secretable/pkg/config"
"secretable/pkg/crypto"
"secretable/pkg/localizator"
"secretable/pkg/log"
Expand Down Expand Up @@ -50,12 +50,14 @@ type Handler struct {
b *tb.Bot
tp *tables.TablesProvider
locales *localizator.Localizator
conf *config.Config
confPath string
}

func NewHandler(b *tb.Bot, tp *tables.TablesProvider, locales *localizator.Localizator, cleanupTime int, enc bool) *Handler {
func NewHandler(b *tb.Bot, tp *tables.TablesProvider, locales *localizator.Localizator, conf *config.Config, confPath string, enc bool) *Handler {
return &Handler{
b: b, tp: tp, locales: locales,
cleanupTime: cleanupTime, encMode: enc,
b: b, tp: tp, locales: locales, encMode: enc,
conf: conf, confPath: confPath,
}
}

Expand Down Expand Up @@ -136,7 +138,7 @@ func (h *Handler) query(m *tb.Message) {
}

func (h *Handler) queryEncrypted(m *tb.Message) {
privkey, err := getPrivkey(h.b, h.tp, m, h.mastePass)
privkey, err := getPrivkey(h.b, h.tp, m, h.conf.Salt, h.mastePass)
if err != nil {
return
}
Expand Down Expand Up @@ -196,17 +198,25 @@ func (h *Handler) ResetPass(m *tb.Message) {
return
}

privkeyBytes, ok, err := getPrivkeyAsBytes(h.b, h.tp, m, h.mastePass)
privkeyBytes, ok, err := getPrivkeyAsBytes(h.b, h.tp, m, h.conf.Salt, h.mastePass)
if err != nil || !ok {
sendMessage(m, h.b, h.locales.Get(m.Sender.LanguageCode, "setpass_unable_set"))
return
}

b, _ := crypto.MakeRandom(16)
os.Setenv("ST_SALT", base58.Encode(b))
oldSalt := h.conf.Salt
h.conf.Salt = base58.Encode(b)
err = config.UpdateFile(h.confPath, h.conf)
if err != nil {
h.conf.Salt = oldSalt
log.Error("Update config: " + err.Error())
sendMessage(m, h.b, h.locales.Get(m.Sender.LanguageCode, "setpass_unable_set"))
return
}

nonce, _ := crypto.MakeRandom(crypto.NonceSize)
cypher, err := crypto.EncryptWithPhrase([]byte(data), []byte(os.Getenv("ST_SALT")), nonce, privkeyBytes)
cypher, err := crypto.EncryptWithPhrase([]byte(data), []byte(h.conf.Salt), nonce, privkeyBytes)
if err != nil {
log.Error("Encrypt with password: " + err.Error())
sendMessage(m, h.b, h.locales.Get(m.Sender.LanguageCode, "setpass_unable_set"))
Expand Down
9 changes: 4 additions & 5 deletions pkg/handlers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"errors"
"fmt"
"html"
"os"
"secretable/pkg/crypto"
"secretable/pkg/log"
"secretable/pkg/tables"
Expand Down Expand Up @@ -60,7 +59,7 @@ func hasAccess(b *tb.Bot, tp *tables.TablesProvider, m *tb.Message) bool {
return false
}

func getPrivkeyAsBytes(b *tb.Bot, tp *tables.TablesProvider, m *tb.Message, masterPass string) ([]byte, bool, error) {
func getPrivkeyAsBytes(b *tb.Bot, tp *tables.TablesProvider, m *tb.Message, salt, masterPass string) ([]byte, bool, error) {
keys := tp.GetKeys()
if len(keys) == 0 {
return nil, false, nil
Expand All @@ -76,16 +75,16 @@ func getPrivkeyAsBytes(b *tb.Bot, tp *tables.TablesProvider, m *tb.Message, mast
nonce := key[:12]
encprivkey := key[12:]

decPrivkey, err := crypto.DecryptWithPhrase([]byte(masterPass), []byte(os.Getenv("ST_SALT")), nonce, encprivkey)
decPrivkey, err := crypto.DecryptWithPhrase([]byte(masterPass), []byte(salt), nonce, encprivkey)
if err != nil {
return nil, false, fmt.Errorf("decrypt with phrase: %s", err.Error())
}

return decPrivkey, true, nil
}

func getPrivkey(b *tb.Bot, tp *tables.TablesProvider, m *tb.Message, masterPass string) (*ecdsa.PrivateKey, error) {
decPrivkey, ok, err := getPrivkeyAsBytes(b, tp, m, masterPass)
func getPrivkey(b *tb.Bot, tp *tables.TablesProvider, m *tb.Message, salt, masterPass string) (*ecdsa.PrivateKey, error) {
decPrivkey, ok, err := getPrivkeyAsBytes(b, tp, m, salt, masterPass)
if err != nil {
return nil, err
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/handlers/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package handlers

import (
"crypto/x509"
"os"
"secretable/pkg/crypto"
"secretable/pkg/log"
"secretable/pkg/tables"
Expand Down Expand Up @@ -73,7 +72,7 @@ func (h *Handler) setPass(m *tb.Message) {

newMasterPass := strings.TrimSpace(m.Text)

_, ok, err := getPrivkeyAsBytes(h.b, h.tp, m, newMasterPass)
_, ok, err := getPrivkeyAsBytes(h.b, h.tp, m, h.conf.Salt, newMasterPass)
if err != nil {
log.Error("Get private key: " + err.Error())
sendMessage(m, h.b, h.locales.Get(m.Sender.LanguageCode, "setpass_unable_set"))
Expand All @@ -85,7 +84,7 @@ func (h *Handler) setPass(m *tb.Message) {
privkey, _ := crypto.GeneratePrivKey()
binPrivkey, _ := x509.MarshalPKCS8PrivateKey(privkey)
nonce, _ := crypto.MakeRandom(crypto.NonceSize)
cypher, err := crypto.EncryptWithPhrase([]byte(newMasterPass), []byte(os.Getenv("ST_SALT")), nonce, binPrivkey)
cypher, err := crypto.EncryptWithPhrase([]byte(newMasterPass), []byte(h.conf.Salt), nonce, binPrivkey)
if err != nil {
log.Error("Encrypt with phrase: " + err.Error())
sendMessage(m, h.b, h.locales.Get(m.Sender.LanguageCode, "setpass_unable_set"))
Expand Down Expand Up @@ -159,7 +158,7 @@ func (h *Handler) querySetNewEncryptedSecret(b *tb.Bot, tp *tables.TablesProvide
}
arr = arr[:3]

privkey, err := getPrivkey(b, tp, m, masterPass)
privkey, err := getPrivkey(b, tp, m, h.conf.Salt, masterPass)
if err != nil {
return
}
Expand Down

0 comments on commit d99607a

Please sign in to comment.