store.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package store
  2. import (
  3. "crypto/md5"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. fs "io/ioutil"
  8. "net/url"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "github.com/jsmartx/giter/util"
  13. )
  14. const ROOT = "~/.giter/"
  15. type User struct {
  16. Name string `json:"name"`
  17. Email string `json:"email"`
  18. Scheme string `json:"scheme"`
  19. Host string `json:"host"`
  20. Port string `json:"port"`
  21. }
  22. type Options struct {
  23. KeyPath string
  24. Password string
  25. }
  26. func (u *User) Hash() string {
  27. host := util.JoinHostPort(u.Host, u.Port)
  28. url := fmt.Sprintf("%s://%s@%s", u.Scheme, u.Name, host)
  29. return fmt.Sprintf("%x", md5.Sum([]byte(url)))
  30. }
  31. func (u *User) URL(pwd string) string {
  32. fullURL := &url.URL{
  33. Scheme: u.Scheme,
  34. User: url.UserPassword(u.Name, pwd),
  35. Host: util.JoinHostPort(u.Host, u.Port),
  36. }
  37. return fullURL.String()
  38. }
  39. func (u *User) KeyPath() (string, error) {
  40. return util.JoinPath(ROOT, "keys", u.Hash())
  41. }
  42. func (u *User) FullHost() string {
  43. host := util.JoinHostPort(u.Host, u.Port)
  44. return fmt.Sprintf("%s://%s", u.Scheme, host)
  45. }
  46. func (u *User) IsSSH() bool {
  47. if u.Scheme == "http" || u.Scheme == "https" {
  48. return false
  49. }
  50. return true
  51. }
  52. func (u *User) String() string {
  53. return fmt.Sprintf("%s - %s", u.Name, u.FullHost())
  54. }
  55. type Config struct {
  56. Users []*User `json:"users"`
  57. }
  58. type Store struct {
  59. c *Config
  60. }
  61. func loadConfig() *Config {
  62. cfgPath, err := util.JoinPath(ROOT, "config.json")
  63. if err != nil {
  64. return nil
  65. }
  66. f, err := os.Open(cfgPath)
  67. if err != nil {
  68. return nil
  69. }
  70. defer f.Close()
  71. var cfg Config
  72. parser := json.NewDecoder(f)
  73. if err := parser.Decode(&cfg); err != nil {
  74. panic(err)
  75. }
  76. return &cfg
  77. }
  78. func saveConfig(cfg *Config) error {
  79. root, err := util.Mkdir(ROOT)
  80. if err != nil {
  81. return err
  82. }
  83. b, err := json.MarshalIndent(cfg, "", " ")
  84. if err != nil {
  85. return err
  86. }
  87. cfgPath := filepath.Join(root, "config.json")
  88. // write marshaled data to the file
  89. return fs.WriteFile(cfgPath, b, 0755)
  90. }
  91. func New() *Store {
  92. cfg := loadConfig()
  93. if cfg != nil {
  94. return &Store{c: cfg}
  95. }
  96. cfg = &Config{
  97. Users: make([]*User, 0),
  98. }
  99. if err := saveConfig(cfg); err != nil {
  100. panic(err)
  101. }
  102. return &Store{c: cfg}
  103. }
  104. func (s *Store) check(user *User) error {
  105. for _, u := range s.c.Users {
  106. if u.Hash() == user.Hash() {
  107. return errors.New("user already exist")
  108. }
  109. }
  110. return nil
  111. }
  112. func (s *Store) Add(u *User, opts *Options) error {
  113. if err := s.check(u); err != nil {
  114. return err
  115. }
  116. keysDir, err := util.Mkdir(ROOT, "keys")
  117. if err != nil {
  118. return err
  119. }
  120. if u.IsSSH() {
  121. pvtPath := filepath.Join(keysDir, u.Hash())
  122. pubPath := filepath.Join(keysDir, u.Hash()+".pub")
  123. if opts.KeyPath == "" {
  124. if err := util.Keygen(pubPath, pvtPath, 4096); err != nil {
  125. return err
  126. }
  127. } else {
  128. if err := util.Copy(opts.KeyPath, pvtPath); err != nil {
  129. return err
  130. }
  131. if err := util.Copy(opts.KeyPath+".pub", pubPath); err != nil {
  132. return err
  133. }
  134. }
  135. } else {
  136. pwdPath := filepath.Join(keysDir, u.Hash()+".credential")
  137. data := []byte(u.URL(opts.Password))
  138. if err := fs.WriteFile(pwdPath, data, 0755); err != nil {
  139. return err
  140. }
  141. }
  142. s.c.Users = append(s.c.Users, u)
  143. return saveConfig(s.c)
  144. }
  145. func (s *Store) Update(hash string, u *User, opts *Options) error {
  146. if hash != u.Hash() {
  147. if err := s.check(u); err != nil {
  148. return err
  149. }
  150. }
  151. keysDir, err := util.Mkdir(ROOT, "keys")
  152. if err != nil {
  153. return err
  154. }
  155. if u.IsSSH() {
  156. pvtPath := filepath.Join(keysDir, u.Hash())
  157. pubPath := filepath.Join(keysDir, u.Hash()+".pub")
  158. if opts.KeyPath == "" {
  159. if err := util.Keygen(pubPath, pvtPath, 4096); err != nil {
  160. return err
  161. }
  162. } else if opts.KeyPath != pvtPath {
  163. if err := util.Copy(opts.KeyPath, pvtPath); err != nil {
  164. return err
  165. }
  166. if err := util.Copy(opts.KeyPath+".pub", pubPath); err != nil {
  167. return err
  168. }
  169. }
  170. } else {
  171. pwdPath := filepath.Join(keysDir, u.Hash()+".credential")
  172. if opts.Password != "" {
  173. data := []byte(u.URL(opts.Password))
  174. if err := fs.WriteFile(pwdPath, data, 0600); err != nil {
  175. return err
  176. }
  177. } else if hash != u.Hash() {
  178. oldPath := filepath.Join(keysDir, hash+".credential")
  179. if err := util.Copy(oldPath, pwdPath); err != nil {
  180. return err
  181. }
  182. }
  183. }
  184. for i := 0; i < len(s.c.Users); i++ {
  185. if s.c.Users[i].Hash() == hash {
  186. s.c.Users[i] = u
  187. }
  188. }
  189. return saveConfig(s.c)
  190. }
  191. func (s *Store) Delete(u *User) error {
  192. p, err := u.KeyPath()
  193. if err != nil {
  194. return err
  195. }
  196. if u.IsSSH() {
  197. os.Remove(p)
  198. os.Remove(p + ".pub")
  199. } else {
  200. os.Remove(p + ".credential")
  201. }
  202. users := make([]*User, 0)
  203. for _, v := range s.c.Users {
  204. if v.Hash() != u.Hash() {
  205. users = append(users, v)
  206. }
  207. }
  208. s.c.Users = users
  209. return saveConfig(s.c)
  210. }
  211. func (s *Store) List(filter string, strict bool) []*User {
  212. users := make([]*User, 0)
  213. for _, u := range s.c.Users {
  214. if !strict && strings.Contains(u.Name, filter) {
  215. users = append(users, u)
  216. }
  217. if strict && u.Name == filter {
  218. users = append(users, u)
  219. }
  220. }
  221. return users
  222. }