Skip to content

Commit

Permalink
basic cli framework added
Browse files Browse the repository at this point in the history
  • Loading branch information
Kristof Daja committed Jun 6, 2023
1 parent 5a9a1ad commit da4fc59
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 5 deletions.
36 changes: 36 additions & 0 deletions cli/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"os"

"gopkg.in/yaml.v2"
)

type UserConfig struct {
Host string `yaml:"host"`
HostAuthType string `yaml:"host_auth_type"`
Username string `yaml:"username"`
Password string `yaml:"password"`
PasswordEncrypted string `yaml:"password_encrypted"`
ProjectSlugs []string `yaml:"project_slugs"`
}

func readConfigFile(path string) (*UserConfig, error) {
b, err := os.ReadFile(path)
if err != nil {
return cfg, err
}
s := UserConfig{}
err = yaml.Unmarshal([]byte(b), &s)
return &s, err
}

func writeConfigFile(cfg *UserConfig, path string) error {
cfgCopy := *cfg
cfgCopy.Password = "" // clear password to avoid writing it back to config
b, err := yaml.Marshal(&cfgCopy)
if err != nil {
return err
}
return os.WriteFile(path, b, 0644)
}
32 changes: 32 additions & 0 deletions cli/encrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"log"

"github.com/denisbrodbeck/machineid"
"github.com/theriverman/taigo/cli/passwordbasedencryption"
)

func PasswordEncrypt(plainPassword string) string {
machineID, err := machineid.ID()
if err != nil {
log.Fatal(err)
}
s, err := passwordbasedencryption.Encrypt(machineID, 5, plainPassword)
if err != nil {
log.Fatal(err)
}
return s
}

func PasswordDecrypt(encryptedPassword string) string {
machineID, err := machineid.ID()
if err != nil {
log.Fatal(err)
}
s, err := passwordbasedencryption.Decrypt(machineID, 5, encryptedPassword)
if err != nil {
log.Fatal(err)
}
return s
}
20 changes: 20 additions & 0 deletions cli/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module github.com/theriverman/taigo/cli

replace github.com/theriverman/taigo => ../

replace github.com/theriverman/taigo/cli/passwordbasedencryption => ./passwordbasedencryption

go 1.19

require (
github.com/denisbrodbeck/machineid v1.0.1
github.com/theriverman/taigo v1.6.1
github.com/theriverman/taigo/cli/passwordbasedencryption v0.0.0-00010101000000-000000000000
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/google/go-querystring v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.8 // indirect
)
34 changes: 34 additions & 0 deletions cli/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
106 changes: 106 additions & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"fmt"
"log"
"net/http"
"os"
"path/filepath"

taiga "github.com/theriverman/taigo"
)

const TaigoCfgFolder string = "TaigoCLI"
const UserCfgFilename string = "taigocli.cfg.yaml"
const DefaultTaigaHost string = "https://api.taiga.io"
const DefaultTaigaHostAuthType string = "normal"

var cfg *UserConfig = &UserConfig{}
var client taiga.Client

var AppName string = "Taigo CLI"
var username, password string // if set, overrides cfg.Username|Password

func init() {
/*
setup userspace
*/
// get home
userHomeDir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
// check/create TaigoCli directory existence
TaigoCliHomeDir := filepath.Join(userHomeDir, TaigoCfgFolder)
TaigoCliCfgPath := filepath.Join(TaigoCliHomeDir, UserCfgFilename)
if err = os.MkdirAll(TaigoCliHomeDir, 0644); err != nil {
log.Fatal(err)
}
// get/create config file
if cfg, err = readConfigFile(TaigoCliCfgPath); err != nil {
log.Printf("config file not found (%s). creating a new one", err)
if err = writeConfigFile(cfg, TaigoCliCfgPath); err != nil {
log.Fatalln("writeConfigFile:", err)
}
}
/*
setup Taigo client
*/
// new client
client = taiga.Client{
BaseURL: DefaultTaigaHost,
HTTPClient: &http.Client{},
}
// store the password in an encrypted fashion
if password != "" {
cfg.Password = password
}
if cfg.PasswordEncrypted != "" {
cfg.Password = PasswordDecrypt(cfg.PasswordEncrypted)
} else if cfg.Password != "" {
cfg.PasswordEncrypted = PasswordEncrypt(cfg.Password)
}
// set default values if empty/missing from config file
if cfg.Host != "" {
client.BaseURL = cfg.Host
}
if username != "" {
cfg.Username = username
}
if err = writeConfigFile(cfg, TaigoCliCfgPath); err != nil {
log.Fatalln("writeConfigFile:", err)
}
}

func main() {
// init client
if err := client.Initialise(); err != nil {
log.Fatal(err)
}
// create credentials
credentials := &taiga.Credentials{
Type: DefaultTaigaHostAuthType,
Username: cfg.Username,
Password: cfg.Password,
}
// set host auth type
if cfg.HostAuthType != "" {
credentials.Type = cfg.HostAuthType
}
// authenticate (get/set token)
if err := client.AuthByCredentials(credentials); err != nil {
log.Fatal(err)
}
// get /users/me
me, _ := client.User.Me()
fmt.Println("Me: (ID, Username, FullName)", me.ID, me.Username, me.FullName)

// Get Project (by its slug)
// slug := "therivermantaigo-taigo-public-test"
// fmt.Printf("Getting Project (slug=%s)..\n", slug)
// project, err := client.Project.GetBySlug(slug)
// if err != nil {
// fmt.Println(err)
// }
// fmt.Printf("Project name: %s \n\n", project.Name)
}
3 changes: 3 additions & 0 deletions cli/passwordbasedencryption/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/theriverman/taigo/cli/passwordbasedencryption

go 1.19
110 changes: 110 additions & 0 deletions cli/passwordbasedencryption/pbewithmd5anddes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
Original author: LucasSloan/passwordbasedencryption
Original GitGub: https://github.com/LucasSloan/passwordbasedencryption
*/

package passwordbasedencryption

import (
"strings"

"encoding/base64"

"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/rand"
)

func getDerivedKey(password string, salt string, count int) ([]byte, []byte) {
key := md5.Sum([]byte(password + salt))
for i := 0; i < count-1; i++ {
key = md5.Sum(key[:])
}
return key[:8], key[8:]
}

func Encrypt(password string, obtenationIterations int, plainText string) (string, error) {
salt := make([]byte, 8)
_, err := rand.Read(salt)
if err != nil {
return "", err
}

encText, err := doEncrypt(password, plainText, salt, obtenationIterations)
if err != nil {
return "", err
}

return base64.StdEncoding.EncodeToString(append(salt, encText...)), nil
}

func Decrypt(password string, obtenationIterations int, cipherText string) (string, error) {
msgBytes, err := base64.StdEncoding.DecodeString(cipherText)
if err != nil {
return "", err
}

salt := msgBytes[:8]
encText := msgBytes[8:]
return doDecrypt(password, encText, salt, obtenationIterations)
}

func EncryptWithFixedSalt(password string, obtenationIterations int, plainText string, fixedSalt string) (string, error) {
salt := make([]byte, 8)
copy(salt[:], fixedSalt)

encText, err := doEncrypt(password, plainText, salt, obtenationIterations)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(encText), nil
}

func DecryptWithFixedSalt(password string, obtenationIterations int, cipherText string, fixedSalt string) (string, error) {
msgBytes, err := base64.StdEncoding.DecodeString(cipherText)
if err != nil {
return "", err
}

salt := make([]byte, 8)
copy(salt[:], fixedSalt)
encText := msgBytes[:]
return doDecrypt(password, encText, salt, obtenationIterations)
}

func doEncrypt(password, plainText string, salt []byte, obtenationIterations int) ([]byte, error) {
padNum := byte(8 - len(plainText)%8)
for i := byte(0); i < padNum; i++ {
plainText += string(padNum)
}

dk, iv := getDerivedKey(password, string(salt), obtenationIterations)
block, err := des.NewCipher(dk)
if err != nil {
return nil, err
}

encrypter := cipher.NewCBCEncrypter(block, iv)
encrypted := make([]byte, len(plainText))
encrypter.CryptBlocks(encrypted, []byte(plainText))

return encrypted, nil
}

func doDecrypt(password string, encText, salt []byte, obtenationIterations int) (string, error) {
dk, iv := getDerivedKey(password, string(salt), obtenationIterations)
block, err := des.NewCipher(dk)

if err != nil {
return "", err
}

decrypter := cipher.NewCBCDecrypter(block, iv)
decrypted := make([]byte, len(encText))
decrypter.CryptBlocks(decrypted, encText)

decryptedString := strings.TrimRight(string(decrypted), "\x01\x02\x03\x04\x05\x06\x07\x08")

return decryptedString, nil
}

0 comments on commit da4fc59

Please sign in to comment.