Skip to content

Commit

Permalink
Refactoring by linters
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikhail Borovikov committed Nov 8, 2021
1 parent 504db99 commit c7f84c4
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 199 deletions.
211 changes: 130 additions & 81 deletions cmd/secretable.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,143 +33,192 @@ import (

"github.com/jessevdk/go-flags"
"github.com/mr-tron/base58/base58"
"github.com/pkg/errors"
)

const (
longPollerTimeout = 5 // in sec
saltLength = 32
)

var opts struct {
ConfigFile string `short:"c" default:"" long:"config" description:"Path to config file" required:"false"`
}

var cmds = []tb.Command{
{
Text: "/id", Description: "Get your chat id",
},
{
Text: "/generate", Description: "Generate a strong password as recommended by OWASP. You can pass the length of the password like: /generate 8",
},
{
Text: "/add", Description: "Add a new secret",
},
{
Text: "/delete", Description: "Delete secret by index, for example: /delete 12",
},
{
Text: "/setpass", Description: "Set new master password, for example: /setpass your_new_master_pass",
},
}

//go:embed locales
var localesFS embed.FS

func main() {
_, err := flags.Parse(&opts)
if flags.WroteHelp(err) {
return
}
opts, next, err := getFlags()
if err != nil {
log.Fatal(err.Error())
return
}

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

conf, err := config.ParseFromFile(opts.ConfigFile)
if err != nil {
log.Fatal(err.Error())
if !next {
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"))

if conf.Unencrypted {
log.Info("🔓 Unecrypted mode")
} else {
log.Info("🔐 Encrypted mode")

if conf.Salt == "" {
log.Info("🧂 Salt not set and will be generated automatically.")
} else {
log.Info("🧂 Salt setted")
}
}

locales := new(localizator.Localizator)
if err = locales.InitFromFS(localesFS, "locales"); err != nil {
log.Fatal("Initialization locales: " + err.Error())

return
}

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

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

return
}

if !conf.Unencrypted {
if conf.Salt == "" {
s, _ := crypto.MakeRandom(32)
conf.Salt = base58.Encode(s)
}
tableProvider, err := tables.NewTablesProvider(conf.GoogleCredentials, conf.SpreadsheetID)
if err != nil {
log.Fatal("Unable to create tables provider: " + err.Error())
}

b, err := tb.NewBot(tb.Settings{
Token: conf.TelegramBotToken,
Poller: &tb.LongPoller{Timeout: longPollerTimeout * time.Second},
bot, err := tb.NewBot(tb.Settings{
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, conf, opts.ConfigFile, !conf.Unencrypted)
setRouting(
bot,
&handlers.Handler{
Bot: bot,
TablesProvider: tableProvider,
Locales: locales,
EncriptionMode: !conf.Unencrypted,
Config: conf,
},
conf,
)

startMessage := "Welcome! Just enter text into the chat to find secrets or use the commands:\n\n"
for _, cmd := range cmds {
startMessage += fmt.Sprintf("<code>%s</code> - %s\n", cmd.Text, cmd.Description)
log.Info("🚀 Start Telegram Bot")

bot.Start()
}

type option struct {
ConfigFile string `short:"c" default:"" long:"config" description:"Path to config file" required:"false"`
}

func getFlags() (opts option, ok bool, err error) {
_, err = flags.Parse(&opts)
if flags.WroteHelp(err) {
return opts, false, nil
}

b.Handle("/start", middleware(false, false, false, 0, handler, handler.MakeStart(startMessage)))
if err != nil {
return opts, false, errors.Wrap(err, "parse flags")
}

b.Handle("/id", middleware(false, false, false, conf.CleanupTimeout, handler, handler.ID))
b.Handle("/generate", middleware(false, false, false, conf.CleanupTimeout, handler, handler.Generate))
if opts.ConfigFile == "" {
homedir, err := os.UserHomeDir()
if err != nil {
return opts, false, errors.Wrap(err, "get home dir")
}

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))
opts.ConfigFile = filepath.Join(homedir, ".secretable", "config.yaml")
}

if err = b.SetCommands(cmds); err != nil {
log.Error("Error of setting commands: " + err.Error())
return opts, true, nil
}

func getConf(path string) (conf *config.Config, err error) {
conf, err = config.ParseFromFile(path)
if err != nil {
return nil, errors.Wrap(err, "parse config from file")
}

log.Info("🚀 Start Telegram Bot")
log.Info("📝 Google credentials: " + conf.GoogleCredentials)
log.Info("📄 Spreadsheet ID: " + conf.SpreadsheetID)
log.Info("🧹 Cleanup timeout: " + fmt.Sprint(conf.CleanupTimeout, " sec"))

b.Start()
if conf.Unencrypted {
log.Info("🔓 Unecrypted mode")
} else {
log.Info("🔐 Encrypted mode")
}

if !conf.Unencrypted && conf.Salt == "" {
s, _ := crypto.MakeRandom(saltLength)
conf.Salt = base58.Encode(s)

if err = config.UpdateFile(conf); err != nil {
return nil, errors.Wrap(err, "update config file")
}

log.Info("🧂 Salt generated automatically")
}

return conf, nil
}

func middleware(useMasterPassCheck, isQuery, hasAccessControl bool, cleanupTime int, handler *handlers.Handler, next func(*tb.Message)) func(*tb.Message) {
func middleware(
useMasterPassCheck, isQuery, hasAccessControl bool,
cleanupTime int, handler *handlers.Handler,
next func(*tb.Message),
) func(*tb.Message) {
next = handler.ControlSetSecretMiddleware(isQuery, next)
next = handler.ControlMasterPassMiddleware(useMasterPassCheck, isQuery, next)

if hasAccessControl {
next = handler.AccessMiddleware(next)
}

if cleanupTime > 0 {
next = handler.CleanupMessagesMiddleware(cleanupTime, next)
}
next = handler.LoggerMiddleware(next)

return next
return handler.LoggerMiddleware(next)
}

func setRouting(bot *tb.Bot, handler *handlers.Handler, conf *config.Config) {
var cmds = []tb.Command{
{
Text: "/id", Description: "Get your chat id",
},
{
Text: "/generate", Description: "Generate a strong password as recommended by OWASP. " +
"You can pass the length of the password like: /generate 8",
},
{
Text: "/add", Description: "Add a new secret",
},
{
Text: "/delete", Description: "Delete secret by index, for example: /delete 12",
},
{
Text: "/setpass", Description: "Set new master password, for example: /setpass your_new_master_pass",
},
}

startMessage := "Welcome! Just enter text into the chat to find secrets or use the commands:\n\n"

if err := bot.SetCommands(cmds); err != nil {
log.Error("Error of setting commands: " + err.Error())
}

for _, cmd := range cmds {
startMessage += fmt.Sprintf("<code>%s</code> - %s\n", cmd.Text, cmd.Description)
}

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

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

bot.Handle("/add", middleware(true, false, true, conf.CleanupTimeout, handler, handler.Set))
bot.Handle("/setpass", middleware(true, false, true, conf.CleanupTimeout, handler, handler.ResetPass))
bot.Handle("/delete", middleware(true, false, true, conf.CleanupTimeout, handler, handler.Delete))
bot.Handle(tb.OnText, middleware(true, true, true, conf.CleanupTimeout, handler, handler.Query))
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.17
require (
github.com/jessevdk/go-flags v1.5.0
github.com/mr-tron/base58 v1.2.0
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.26.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
google.golang.org/api v0.60.0
Expand All @@ -17,7 +18,6 @@ require (
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
Expand Down
6 changes: 4 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
)

type Config struct {
filePath string
TelegramBotToken string `yaml:"telegram_bot_token"`
GoogleCredentials string `yaml:"google_credentials_file"`
SpreadsheetID string `yaml:"spreadsheet_id"`
Expand Down Expand Up @@ -57,13 +58,14 @@ func ParseFromFile(path string) (config *Config, err error) {
return nil, err
}

config.filePath = path
return config, nil
}

func UpdateFile(path string, config *Config) error {
func UpdateFile(config *Config) error {
buf := bytes.NewBuffer([]byte{})
if err := yaml.NewEncoder(buf).Encode(config); err != nil {
return nil
}
return os.WriteFile(path, buf.Bytes(), os.ModePerm)
return os.WriteFile(config.filePath, buf.Bytes(), os.ModePerm)
}
15 changes: 10 additions & 5 deletions pkg/crypto/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"crypto/rand"
"crypto/sha512"

"github.com/pkg/errors"
"golang.org/x/crypto/pbkdf2"
)

Expand Down Expand Up @@ -49,7 +50,12 @@ func DecryptWithPhrase(phrase, salt, nonce []byte, ciphertext []byte) ([]byte, e
return nil, err
}

return gcm.Open(nil, nonce, ciphertext, nil)
b, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, errors.Wrap(err, "gcm open")
}

return b, nil
}

func SHA512(s string) []byte {
Expand All @@ -60,19 +66,18 @@ func SHA512(s string) []byte {
func GeneratePrivKey() (priv *ecdsa.PrivateKey, err error) {
priv, err = ecdsa.GenerateKey(curve(), rand.Reader)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "ecdsa generate key")
}

return priv, err

return priv, nil
}

func DeriveCipher(password, keySalt []byte) (cipher.AEAD, error) {
block, err := aes.NewCipher(
pbkdf2.Key(password, keySalt, 200000, AESKeySize, hashNew),
)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "aes new cipher")
}
return cipher.NewGCM(block)
}
Expand Down
Loading

0 comments on commit c7f84c4

Please sign in to comment.