123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- package store
- import (
- "crypto/md5"
- "encoding/json"
- "errors"
- "fmt"
- fs "io/ioutil"
- "net/url"
- "os"
- "path/filepath"
- "strings"
- "github.com/jsmartx/giter/util"
- )
- const ROOT = "~/.giter/"
- type User struct {
- Name string `json:"name"`
- Email string `json:"email"`
- Scheme string `json:"scheme"`
- Host string `json:"host"`
- Port string `json:"port"`
- }
- type Options struct {
- KeyPath string
- Password string
- }
- func (u *User) Hash() string {
- host := util.JoinHostPort(u.Host, u.Port)
- url := fmt.Sprintf("%s://%s@%s", u.Scheme, u.Name, host)
- return fmt.Sprintf("%x", md5.Sum([]byte(url)))
- }
- func (u *User) URL(pwd string) string {
- fullURL := &url.URL{
- Scheme: u.Scheme,
- User: url.UserPassword(u.Name, pwd),
- Host: util.JoinHostPort(u.Host, u.Port),
- }
- return fullURL.String()
- }
- func (u *User) KeyPath() (string, error) {
- return util.JoinPath(ROOT, "keys", u.Hash())
- }
- func (u *User) FullHost() string {
- host := util.JoinHostPort(u.Host, u.Port)
- return fmt.Sprintf("%s://%s", u.Scheme, host)
- }
- func (u *User) IsSSH() bool {
- if u.Scheme == "http" || u.Scheme == "https" {
- return false
- }
- return true
- }
- func (u *User) String() string {
- return fmt.Sprintf("%s - %s", u.Name, u.FullHost())
- }
- type Config struct {
- Users []*User `json:"users"`
- }
- type Store struct {
- c *Config
- }
- func loadConfig() *Config {
- cfgPath, err := util.JoinPath(ROOT, "config.json")
- if err != nil {
- return nil
- }
- f, err := os.Open(cfgPath)
- if err != nil {
- return nil
- }
- defer f.Close()
- var cfg Config
- parser := json.NewDecoder(f)
- if err := parser.Decode(&cfg); err != nil {
- panic(err)
- }
- return &cfg
- }
- func saveConfig(cfg *Config) error {
- root, err := util.Mkdir(ROOT)
- if err != nil {
- return err
- }
- b, err := json.MarshalIndent(cfg, "", " ")
- if err != nil {
- return err
- }
- cfgPath := filepath.Join(root, "config.json")
- // write marshaled data to the file
- return fs.WriteFile(cfgPath, b, 0755)
- }
- func New() *Store {
- cfg := loadConfig()
- if cfg != nil {
- return &Store{c: cfg}
- }
- cfg = &Config{
- Users: make([]*User, 0),
- }
- if err := saveConfig(cfg); err != nil {
- panic(err)
- }
- return &Store{c: cfg}
- }
- func (s *Store) check(user *User) error {
- for _, u := range s.c.Users {
- if u.Hash() == user.Hash() {
- return errors.New("user already exist")
- }
- }
- return nil
- }
- func (s *Store) Add(u *User, opts *Options) error {
- if err := s.check(u); err != nil {
- return err
- }
- keysDir, err := util.Mkdir(ROOT, "keys")
- if err != nil {
- return err
- }
- if u.IsSSH() {
- pvtPath := filepath.Join(keysDir, u.Hash())
- pubPath := filepath.Join(keysDir, u.Hash()+".pub")
- if opts.KeyPath == "" {
- if err := util.Keygen(pubPath, pvtPath, 4096); err != nil {
- return err
- }
- } else {
- if err := util.Copy(opts.KeyPath, pvtPath); err != nil {
- return err
- }
- if err := util.Copy(opts.KeyPath+".pub", pubPath); err != nil {
- return err
- }
- }
- } else {
- pwdPath := filepath.Join(keysDir, u.Hash()+".credential")
- data := []byte(u.URL(opts.Password))
- if err := fs.WriteFile(pwdPath, data, 0755); err != nil {
- return err
- }
- }
- s.c.Users = append(s.c.Users, u)
- return saveConfig(s.c)
- }
- func (s *Store) Update(hash string, u *User, opts *Options) error {
- if hash != u.Hash() {
- if err := s.check(u); err != nil {
- return err
- }
- }
- keysDir, err := util.Mkdir(ROOT, "keys")
- if err != nil {
- return err
- }
- if u.IsSSH() {
- pvtPath := filepath.Join(keysDir, u.Hash())
- pubPath := filepath.Join(keysDir, u.Hash()+".pub")
- if opts.KeyPath == "" {
- if err := util.Keygen(pubPath, pvtPath, 4096); err != nil {
- return err
- }
- } else if opts.KeyPath != pvtPath {
- if err := util.Copy(opts.KeyPath, pvtPath); err != nil {
- return err
- }
- if err := util.Copy(opts.KeyPath+".pub", pubPath); err != nil {
- return err
- }
- }
- } else {
- pwdPath := filepath.Join(keysDir, u.Hash()+".credential")
- if opts.Password != "" {
- data := []byte(u.URL(opts.Password))
- if err := fs.WriteFile(pwdPath, data, 0600); err != nil {
- return err
- }
- } else if hash != u.Hash() {
- oldPath := filepath.Join(keysDir, hash+".credential")
- if err := util.Copy(oldPath, pwdPath); err != nil {
- return err
- }
- }
- }
- for i := 0; i < len(s.c.Users); i++ {
- if s.c.Users[i].Hash() == hash {
- s.c.Users[i] = u
- }
- }
- return saveConfig(s.c)
- }
- func (s *Store) Delete(u *User) error {
- p, err := u.KeyPath()
- if err != nil {
- return err
- }
- if u.IsSSH() {
- os.Remove(p)
- os.Remove(p + ".pub")
- } else {
- os.Remove(p + ".credential")
- }
- users := make([]*User, 0)
- for _, v := range s.c.Users {
- if v.Hash() != u.Hash() {
- users = append(users, v)
- }
- }
- s.c.Users = users
- return saveConfig(s.c)
- }
- func (s *Store) List(filter string, strict bool) []*User {
- users := make([]*User, 0)
- for _, u := range s.c.Users {
- if !strict && strings.Contains(u.Name, filter) {
- users = append(users, u)
- }
- if strict && u.Name == filter {
- users = append(users, u)
- }
- }
- return users
- }
|