blob: 338af2409f4ca09af5807cb55addb825c31c2389 [file] [log] [blame]
giolekva8aa73e82022-07-09 11:34:39 +04001package soft
2
3import (
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +04004 "errors"
giolekva8aa73e82022-07-09 11:34:39 +04005 "fmt"
giolekva8aa73e82022-07-09 11:34:39 +04006 "log"
7 "net"
8 "os"
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +04009 "regexp"
gio266c04f2024-07-03 14:18:45 +040010 "slices"
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040011 "strings"
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040012 "time"
giolekva8aa73e82022-07-09 11:34:39 +040013
Davit Tabidzea5ea5092024-08-01 15:28:09 +040014 "golang.org/x/crypto/ssh"
15
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040016 "github.com/cenkalti/backoff/v4"
giolekva8aa73e82022-07-09 11:34:39 +040017 "github.com/go-git/go-billy/v5/memfs"
18 "github.com/go-git/go-git/v5"
gio7fbd4ad2024-08-27 10:06:39 +040019 "github.com/go-git/go-git/v5/plumbing"
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040020 "github.com/go-git/go-git/v5/plumbing/transport"
giolekva8aa73e82022-07-09 11:34:39 +040021 gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
22 "github.com/go-git/go-git/v5/storage/memory"
giolekva8aa73e82022-07-09 11:34:39 +040023)
24
gio0eaf2712024-04-14 13:08:46 +040025var ErrorAlreadyExists = errors.New("already exists")
26
gioe72b54f2024-04-22 10:44:41 +040027type Client interface {
28 Address() string
29 Signer() ssh.Signer
30 GetPublicKeys() ([]string, error)
gio33059762024-07-05 13:19:07 +040031 RepoExists(name string) (bool, error)
gioe72b54f2024-04-22 10:44:41 +040032 GetRepo(name string) (RepoIO, error)
gio7fbd4ad2024-08-27 10:06:39 +040033 GetRepoBranch(name, branch string) (RepoIO, error)
gio5887caa2024-10-03 15:07:23 +040034 DeleteRepoBranch(name, branch string) error
35 DeleteRepo(name string) error
giocafd4e62024-07-31 10:53:40 +040036 GetAllRepos() ([]string, error)
gioe72b54f2024-04-22 10:44:41 +040037 GetRepoAddress(name string) string
38 AddRepository(name string) error
gio33059762024-07-05 13:19:07 +040039 UserExists(name string) (bool, error)
40 FindUser(pubKey string) (string, error)
Davit Tabidzea5ea5092024-08-01 15:28:09 +040041 GetAllUsers() ([]string, error)
gioe72b54f2024-04-22 10:44:41 +040042 AddUser(name, pubKey string) error
Davit Tabidzea5ea5092024-08-01 15:28:09 +040043 RemoveUser(user string) error
gioe72b54f2024-04-22 10:44:41 +040044 AddPublicKey(user string, pubKey string) error
45 RemovePublicKey(user string, pubKey string) error
Davit Tabidzea5ea5092024-08-01 15:28:09 +040046 GetUserPublicKeys(user string) ([]string, error)
gioe72b54f2024-04-22 10:44:41 +040047 MakeUserAdmin(name string) error
48 AddReadWriteCollaborator(repo, user string) error
49 AddReadOnlyCollaborator(repo, user string) error
gio0eaf2712024-04-14 13:08:46 +040050 AddWebhook(repo, url string, opts ...string) error
gio7fbd4ad2024-08-27 10:06:39 +040051 DisableAnonAccess() error
52 DisableKeyless() error
gioe72b54f2024-04-22 10:44:41 +040053}
54
55type realClient struct {
56 addr string
57 signer ssh.Signer
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040058 log *log.Logger
59 pemBytes []byte
giolekva8aa73e82022-07-09 11:34:39 +040060}
61
gioe72b54f2024-04-22 10:44:41 +040062func NewClient(addr string, clientPrivateKey []byte, log *log.Logger) (Client, error) {
giolekva8aa73e82022-07-09 11:34:39 +040063 signer, err := ssh.ParsePrivateKey(clientPrivateKey)
64 if err != nil {
65 return nil, err
66 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040067 log.SetPrefix("SOFT-SERVE: ")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040068 log.Printf("Created signer")
gioe72b54f2024-04-22 10:44:41 +040069 return &realClient{
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040070 addr,
giolekva8aa73e82022-07-09 11:34:39 +040071 signer,
72 log,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040073 clientPrivateKey,
giolekva8aa73e82022-07-09 11:34:39 +040074 }, nil
75}
76
gioe72b54f2024-04-22 10:44:41 +040077type ClientGetter interface {
78 Get(addr string, clientPrivateKey []byte, log *log.Logger) (Client, error)
79}
80
81type RealClientGetter struct{}
82
83func (c RealClientGetter) Get(addr string, clientPrivateKey []byte, log *log.Logger) (Client, error) {
84 var client Client
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040085 err := backoff.RetryNotify(func() error {
86 var err error
87 client, err = NewClient(addr, clientPrivateKey, log)
88 if err != nil {
89 return err
90 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040091 if _, err := client.GetPublicKeys(); err != nil {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040092 return err
93 }
94 return nil
95 }, backoff.NewConstantBackOff(5*time.Second), func(err error, _ time.Duration) {
96 log.Printf("Failed to create client: %s\n", err.Error())
97 })
98 return client, err
99}
100
gioe72b54f2024-04-22 10:44:41 +0400101func (ss *realClient) Address() string {
102 return ss.addr
103}
104
105func (ss *realClient) Signer() ssh.Signer {
106 return ss.signer
107}
108
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400109func (ss *realClient) GetAllUsers() ([]string, error) {
110 log.Printf("Getting all users")
111 out, err := ss.RunCommand("user", "list")
112 if err != nil {
113 return nil, err
114 }
115 return strings.Fields(out), nil
116}
117
gio33059762024-07-05 13:19:07 +0400118func (ss *realClient) UserExists(name string) (bool, error) {
gio11617ac2024-07-15 16:09:04 +0400119 log.Printf("Checking user exists %s", name)
gio33059762024-07-05 13:19:07 +0400120 out, err := ss.RunCommand("user", "list")
121 if err != nil {
122 return false, err
123 }
124 return slices.Contains(strings.Fields(out), name), nil
125}
126
127func (ss *realClient) FindUser(pubKey string) (string, error) {
128 log.Printf("Finding user %s", pubKey)
129 pk := strings.Join(strings.Fields(pubKey)[:2], " ")
130 out, err := ss.RunCommand("user", "list")
131 if err != nil {
132 return "", err
133 }
134 for _, user := range strings.Fields(out) {
135 info, err := ss.RunCommand("user", "info", user)
136 if err != nil {
137 return "", err
138 }
139 if strings.Contains(info, pk) {
140 return user, nil
141 }
142 }
143 return "", nil
144}
145
gioe72b54f2024-04-22 10:44:41 +0400146func (ss *realClient) AddUser(name, pubKey string) error {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400147 log.Printf("Adding user %s", name)
gio266c04f2024-07-03 14:18:45 +0400148 if _, err := ss.RunCommand("user", "create", name); err != nil {
giolekva8aa73e82022-07-09 11:34:39 +0400149 return err
150 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400151 return ss.AddPublicKey(name, pubKey)
giolekva8aa73e82022-07-09 11:34:39 +0400152}
153
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400154func (ss *realClient) RemoveUser(user string) error {
155 log.Printf("Removing user: %s\n", user)
156 _, err := ss.RunCommand("user", "delete", user)
157 return err
158}
159
gioe72b54f2024-04-22 10:44:41 +0400160func (ss *realClient) MakeUserAdmin(name string) error {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400161 log.Printf("Making user %s admin", name)
gio266c04f2024-07-03 14:18:45 +0400162 _, err := ss.RunCommand("user", "set-admin", name, "true")
163 return err
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400164}
165
gioe72b54f2024-04-22 10:44:41 +0400166func (ss *realClient) AddPublicKey(user string, pubKey string) error {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400167 log.Printf("Adding public key: %s %s\n", user, pubKey)
gio266c04f2024-07-03 14:18:45 +0400168 _, err := ss.RunCommand("user", "add-pubkey", user, pubKey)
169 return err
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400170}
171
gioe72b54f2024-04-22 10:44:41 +0400172func (ss *realClient) RemovePublicKey(user string, pubKey string) error {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400173 log.Printf("Removing public key: %s %s\n", user, pubKey)
gio266c04f2024-07-03 14:18:45 +0400174 _, err := ss.RunCommand("user", "remove-pubkey", user, pubKey)
175 return err
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400176}
177
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400178func (ss *realClient) GetUserPublicKeys(user string) ([]string, error) {
179 log.Printf("Getting public keys for user: %s\n", user)
180 out, err := ss.RunCommand("user", "info", user)
181 if err != nil {
182 return nil, err
183 }
184 return extractPublicKeys(out), nil
185}
186
187func extractPublicKeys(userInfo string) []string {
188 var keys []string
189 lines := strings.Split(userInfo, "\n")
190 gettingKeys := false
191 for _, line := range lines {
192 if strings.HasPrefix(line, "Public keys:") {
193 gettingKeys = true
194 continue
195 }
196 if gettingKeys {
Davit Tabidze4aaa27b2024-08-05 20:23:50 +0400197 if key := CleanKey(line); key != "" {
198 keys = append(keys, key)
199 }
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400200 }
201 }
202 return keys
203}
204
Davit Tabidze4aaa27b2024-08-05 20:23:50 +0400205func CleanKey(key string) string {
206 k := strings.TrimSpace(key)
207 fields := strings.Fields(k)
208 if len(fields) < 2 {
209 return k
210 }
211 return fields[0] + " " + fields[1]
212}
213
gio266c04f2024-07-03 14:18:45 +0400214func (ss *realClient) RunCommand(args ...string) (string, error) {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400215 cmd := strings.Join(args, " ")
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400216 log.Printf("Running command %s", cmd)
gioe72b54f2024-04-22 10:44:41 +0400217 client, err := ssh.Dial("tcp", ss.addr, ss.sshClientConfig())
giolekva8aa73e82022-07-09 11:34:39 +0400218 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400219 return "", err
giolekva8aa73e82022-07-09 11:34:39 +0400220 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400221 defer client.Close()
giolekva8aa73e82022-07-09 11:34:39 +0400222 session, err := client.NewSession()
223 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400224 return "", err
giolekva8aa73e82022-07-09 11:34:39 +0400225 }
226 defer session.Close()
gio266c04f2024-07-03 14:18:45 +0400227 var buf strings.Builder
228 session.Stdout = &buf
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400229 session.Stderr = os.Stderr
gio266c04f2024-07-03 14:18:45 +0400230 err = session.Run(cmd)
231 return buf.String(), err
232}
233
gio33059762024-07-05 13:19:07 +0400234func (ss *realClient) RepoExists(name string) (bool, error) {
gio266c04f2024-07-03 14:18:45 +0400235 out, err := ss.RunCommand("repo", "list")
236 if err != nil {
237 return false, err
238 }
239 return slices.Contains(strings.Fields(out), name), nil
giolekva8aa73e82022-07-09 11:34:39 +0400240}
241
gioe72b54f2024-04-22 10:44:41 +0400242func (ss *realClient) AddRepository(name string) error {
giolekva8aa73e82022-07-09 11:34:39 +0400243 log.Printf("Adding repository %s", name)
gio33059762024-07-05 13:19:07 +0400244 if ok, err := ss.RepoExists(name); ok {
gio0eaf2712024-04-14 13:08:46 +0400245 return ErrorAlreadyExists
gio266c04f2024-07-03 14:18:45 +0400246 } else if err != nil {
247 return err
gio0eaf2712024-04-14 13:08:46 +0400248 }
gio266c04f2024-07-03 14:18:45 +0400249 _, err := ss.RunCommand("repo", "create", name)
250 return err
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400251}
252
gioe72b54f2024-04-22 10:44:41 +0400253func (ss *realClient) AddReadWriteCollaborator(repo, user string) error {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400254 log.Printf("Adding read-write collaborator %s %s", repo, user)
gio266c04f2024-07-03 14:18:45 +0400255 _, err := ss.RunCommand("repo", "collab", "add", repo, user, "read-write")
256 return err
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400257}
258
gioe72b54f2024-04-22 10:44:41 +0400259func (ss *realClient) AddReadOnlyCollaborator(repo, user string) error {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400260 log.Printf("Adding read-only collaborator %s %s", repo, user)
gio266c04f2024-07-03 14:18:45 +0400261 _, err := ss.RunCommand("repo", "collab", "add", repo, user, "read-only")
262 return err
giolekva8aa73e82022-07-09 11:34:39 +0400263}
264
gio0eaf2712024-04-14 13:08:46 +0400265func (ss *realClient) AddWebhook(repo, url string, opts ...string) error {
266 log.Printf("Adding webhook %s %s", repo, url)
gio266c04f2024-07-03 14:18:45 +0400267 _, err := ss.RunCommand(append(
gio0eaf2712024-04-14 13:08:46 +0400268 []string{"repo", "webhook", "create", repo, url},
269 opts...,
270 )...)
gio266c04f2024-07-03 14:18:45 +0400271 return err
gio0eaf2712024-04-14 13:08:46 +0400272}
273
gio7fbd4ad2024-08-27 10:06:39 +0400274func (ss *realClient) DisableAnonAccess() error {
275 log.Printf("Disabling anon access")
276 _, err := ss.RunCommand("settings", "anon-access", "no-access")
277 return err
278}
279
280func (ss *realClient) DisableKeyless() error {
281 log.Printf("Disabling anon access")
282 _, err := ss.RunCommand("settings", "allow-keyless", "false")
283 return err
284}
285
gio5887caa2024-10-03 15:07:23 +0400286func (ss *realClient) DeleteRepoBranch(name, branch string) error {
287 log.Printf("Deleting branch %s %s", name, branch)
288 _, err := ss.RunCommand("repo", "branch", "delete", name, branch)
289 return err
290}
291
292func (ss *realClient) DeleteRepo(name string) error {
293 log.Printf("Deleting repo %s", name)
294 _, err := ss.RunCommand("repo", "delete", name)
295 return err
296}
297
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400298type Repository struct {
299 *git.Repository
300 Addr RepositoryAddress
gio7fbd4ad2024-08-27 10:06:39 +0400301 Ref string
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400302}
303
gioe72b54f2024-04-22 10:44:41 +0400304func (ss *realClient) GetRepo(name string) (RepoIO, error) {
gio7fbd4ad2024-08-27 10:06:39 +0400305 return ss.GetRepoBranch(name, "master")
306}
307
308func (ss *realClient) GetRepoBranch(name, branch string) (RepoIO, error) {
309 r, err := CloneRepositoryBranch(RepositoryAddress{ss.addr, name}, branch, ss.signer)
gioe72b54f2024-04-22 10:44:41 +0400310 if err != nil {
311 return nil, err
312 }
313 return NewRepoIO(r, ss.signer)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400314}
315
giocafd4e62024-07-31 10:53:40 +0400316func (ss *realClient) GetAllRepos() ([]string, error) {
317 log.Printf("Getting all repos")
318 out, err := ss.RunCommand("repo", "list")
319 if err != nil {
320 return nil, err
321 }
322 return strings.Fields(out), nil
323}
324
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400325type RepositoryAddress struct {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400326 Addr string
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400327 Name string
328}
329
330func ParseRepositoryAddress(addr string) (RepositoryAddress, error) {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400331 items := regexp.MustCompile(`ssh://(.*)/(.*)`).FindStringSubmatch(addr)
332 if len(items) != 3 {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400333 return RepositoryAddress{}, fmt.Errorf("Invalid address")
334 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400335 return RepositoryAddress{items[1], items[2]}, nil
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400336}
337
338func (r RepositoryAddress) FullAddress() string {
339 return fmt.Sprintf("ssh://%s/%s", r.Addr, r.Name)
340}
341
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400342func CloneRepository(addr RepositoryAddress, signer ssh.Signer) (*Repository, error) {
gio7fbd4ad2024-08-27 10:06:39 +0400343 return CloneRepositoryBranch(addr, "master", signer)
344}
345
346func CloneRepositoryBranch(addr RepositoryAddress, branch string, signer ssh.Signer) (*Repository, error) {
347 fmt.Printf("Cloning repository: %s %s %s\n", addr.Addr, addr.Name, branch)
348 ref := fmt.Sprintf("refs/heads/%s", branch)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400349 c, err := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
350 URL: addr.FullAddress(),
351 Auth: &gitssh.PublicKeys{
352 User: "git",
353 Signer: signer,
354 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
355 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
356 // TODO(giolekva): verify server public key
357 fmt.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
358 return nil
359 },
360 },
361 },
Giorgi Lekveishvili87be4ae2023-06-11 23:41:09 +0400362 RemoteName: "origin",
gio7fbd4ad2024-08-27 10:06:39 +0400363 ReferenceName: plumbing.ReferenceName(ref),
giof5ffedb2024-06-19 14:14:43 +0400364 SingleBranch: true,
Giorgi Lekveishvili3550b432023-06-09 19:37:51 +0400365 Depth: 1,
366 InsecureSkipTLS: true,
367 Progress: os.Stdout,
368 })
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400369 if err != nil && !errors.Is(err, transport.ErrEmptyRemoteRepository) {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400370 return nil, err
371 }
giof5ffedb2024-06-19 14:14:43 +0400372 wt, err := c.Worktree()
373 if err != nil {
374 return nil, err
375 }
376 sb, err := wt.Submodules()
377 if err != nil {
378 return nil, err
379 }
380 if err := sb.Init(); err != nil {
381 return nil, err
382 }
383 if err := sb.Update(&git.SubmoduleUpdateOptions{
384 Depth: 1,
385 }); err != nil {
386 return nil, err
387 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400388 return &Repository{
389 Repository: c,
390 Addr: addr,
gio7fbd4ad2024-08-27 10:06:39 +0400391 Ref: ref,
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400392 }, nil
Giorgi Lekveishvili3550b432023-06-09 19:37:51 +0400393}
394
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400395// TODO(giolekva): dead code
gioe72b54f2024-04-22 10:44:41 +0400396func (ss *realClient) authSSH() gitssh.AuthMethod {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400397 a, err := gitssh.NewPublicKeys("git", ss.pemBytes, "")
giolekva8aa73e82022-07-09 11:34:39 +0400398 if err != nil {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400399 panic(err)
giolekva8aa73e82022-07-09 11:34:39 +0400400 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400401 a.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
402 // TODO(giolekva): verify server public key
403 ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
404 return nil
giolekva8aa73e82022-07-09 11:34:39 +0400405 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400406 return a
407 // return &gitssh.PublicKeys{
408 // User: "git",
409 // Signer: ss.Signer,
410 // HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
411 // HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
412 // // TODO(giolekva): verify server public key
413 // ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
414 // return nil
415 // },
416 // },
417 // }
giolekva8aa73e82022-07-09 11:34:39 +0400418}
419
gioe72b54f2024-04-22 10:44:41 +0400420func (ss *realClient) authGit() *gitssh.PublicKeys {
giolekva8aa73e82022-07-09 11:34:39 +0400421 return &gitssh.PublicKeys{
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400422 User: "git",
gioe72b54f2024-04-22 10:44:41 +0400423 Signer: ss.signer,
giolekva8aa73e82022-07-09 11:34:39 +0400424 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
425 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
426 // TODO(giolekva): verify server public key
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400427 ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
giolekva8aa73e82022-07-09 11:34:39 +0400428 return nil
429 },
430 },
431 }
432}
433
gioe72b54f2024-04-22 10:44:41 +0400434func (ss *realClient) GetPublicKeys() ([]string, error) {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400435 var ret []string
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400436 config := &ssh.ClientConfig{
437 Auth: []ssh.AuthMethod{
gioe72b54f2024-04-22 10:44:41 +0400438 ssh.PublicKeys(ss.signer),
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400439 },
440 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400441 ret = append(ret, string(ssh.MarshalAuthorizedKey(key)))
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400442 return nil
443 },
444 }
gioe72b54f2024-04-22 10:44:41 +0400445 client, err := ssh.Dial("tcp", ss.addr, config)
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400446 if err != nil {
447 return nil, err
448 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400449 defer client.Close()
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400450 return ret, nil
451}
452
gioe72b54f2024-04-22 10:44:41 +0400453func (ss *realClient) sshClientConfig() *ssh.ClientConfig {
giolekva8aa73e82022-07-09 11:34:39 +0400454 return &ssh.ClientConfig{
455 Auth: []ssh.AuthMethod{
gioe72b54f2024-04-22 10:44:41 +0400456 ssh.PublicKeys(ss.signer),
giolekva8aa73e82022-07-09 11:34:39 +0400457 },
458 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
459 // TODO(giolekva): verify server public key
460 // fmt.Printf("## %s || %s -- \n", serverPubKey, ssh.MarshalAuthorizedKey(key))
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400461 fmt.Printf("%s %s %s", hostname, remote, ssh.MarshalAuthorizedKey(key))
giolekva8aa73e82022-07-09 11:34:39 +0400462 return nil
463 },
464 }
465}
466
gioe72b54f2024-04-22 10:44:41 +0400467func (ss *realClient) GetRepoAddress(name string) string {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400468 return fmt.Sprintf("%s/%s", ss.addressGit(), name)
469}
470
gioe72b54f2024-04-22 10:44:41 +0400471func (ss *realClient) addressGit() string {
472 return fmt.Sprintf("ssh://%s", ss.addr)
giolekva8aa73e82022-07-09 11:34:39 +0400473}