blob: 5163f23543d076e3b0629a5921cd691074700e15 [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)
giocafd4e62024-07-31 10:53:40 +040034 GetAllRepos() ([]string, error)
gioe72b54f2024-04-22 10:44:41 +040035 GetRepoAddress(name string) string
36 AddRepository(name string) error
gio33059762024-07-05 13:19:07 +040037 UserExists(name string) (bool, error)
38 FindUser(pubKey string) (string, error)
Davit Tabidzea5ea5092024-08-01 15:28:09 +040039 GetAllUsers() ([]string, error)
gioe72b54f2024-04-22 10:44:41 +040040 AddUser(name, pubKey string) error
Davit Tabidzea5ea5092024-08-01 15:28:09 +040041 RemoveUser(user string) error
gioe72b54f2024-04-22 10:44:41 +040042 AddPublicKey(user string, pubKey string) error
43 RemovePublicKey(user string, pubKey string) error
Davit Tabidzea5ea5092024-08-01 15:28:09 +040044 GetUserPublicKeys(user string) ([]string, error)
gioe72b54f2024-04-22 10:44:41 +040045 MakeUserAdmin(name string) error
46 AddReadWriteCollaborator(repo, user string) error
47 AddReadOnlyCollaborator(repo, user string) error
gio0eaf2712024-04-14 13:08:46 +040048 AddWebhook(repo, url string, opts ...string) error
gio7fbd4ad2024-08-27 10:06:39 +040049 DisableAnonAccess() error
50 DisableKeyless() error
gioe72b54f2024-04-22 10:44:41 +040051}
52
53type realClient struct {
54 addr string
55 signer ssh.Signer
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040056 log *log.Logger
57 pemBytes []byte
giolekva8aa73e82022-07-09 11:34:39 +040058}
59
gioe72b54f2024-04-22 10:44:41 +040060func NewClient(addr string, clientPrivateKey []byte, log *log.Logger) (Client, error) {
giolekva8aa73e82022-07-09 11:34:39 +040061 signer, err := ssh.ParsePrivateKey(clientPrivateKey)
62 if err != nil {
63 return nil, err
64 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040065 log.SetPrefix("SOFT-SERVE: ")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040066 log.Printf("Created signer")
gioe72b54f2024-04-22 10:44:41 +040067 return &realClient{
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040068 addr,
giolekva8aa73e82022-07-09 11:34:39 +040069 signer,
70 log,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040071 clientPrivateKey,
giolekva8aa73e82022-07-09 11:34:39 +040072 }, nil
73}
74
gioe72b54f2024-04-22 10:44:41 +040075type ClientGetter interface {
76 Get(addr string, clientPrivateKey []byte, log *log.Logger) (Client, error)
77}
78
79type RealClientGetter struct{}
80
81func (c RealClientGetter) Get(addr string, clientPrivateKey []byte, log *log.Logger) (Client, error) {
82 var client Client
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040083 err := backoff.RetryNotify(func() error {
84 var err error
85 client, err = NewClient(addr, clientPrivateKey, log)
86 if err != nil {
87 return err
88 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040089 if _, err := client.GetPublicKeys(); err != nil {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040090 return err
91 }
92 return nil
93 }, backoff.NewConstantBackOff(5*time.Second), func(err error, _ time.Duration) {
94 log.Printf("Failed to create client: %s\n", err.Error())
95 })
96 return client, err
97}
98
gioe72b54f2024-04-22 10:44:41 +040099func (ss *realClient) Address() string {
100 return ss.addr
101}
102
103func (ss *realClient) Signer() ssh.Signer {
104 return ss.signer
105}
106
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400107func (ss *realClient) GetAllUsers() ([]string, error) {
108 log.Printf("Getting all users")
109 out, err := ss.RunCommand("user", "list")
110 if err != nil {
111 return nil, err
112 }
113 return strings.Fields(out), nil
114}
115
gio33059762024-07-05 13:19:07 +0400116func (ss *realClient) UserExists(name string) (bool, error) {
gio11617ac2024-07-15 16:09:04 +0400117 log.Printf("Checking user exists %s", name)
gio33059762024-07-05 13:19:07 +0400118 out, err := ss.RunCommand("user", "list")
119 if err != nil {
120 return false, err
121 }
122 return slices.Contains(strings.Fields(out), name), nil
123}
124
125func (ss *realClient) FindUser(pubKey string) (string, error) {
126 log.Printf("Finding user %s", pubKey)
127 pk := strings.Join(strings.Fields(pubKey)[:2], " ")
128 out, err := ss.RunCommand("user", "list")
129 if err != nil {
130 return "", err
131 }
132 for _, user := range strings.Fields(out) {
133 info, err := ss.RunCommand("user", "info", user)
134 if err != nil {
135 return "", err
136 }
137 if strings.Contains(info, pk) {
138 return user, nil
139 }
140 }
141 return "", nil
142}
143
gioe72b54f2024-04-22 10:44:41 +0400144func (ss *realClient) AddUser(name, pubKey string) error {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400145 log.Printf("Adding user %s", name)
gio266c04f2024-07-03 14:18:45 +0400146 if _, err := ss.RunCommand("user", "create", name); err != nil {
giolekva8aa73e82022-07-09 11:34:39 +0400147 return err
148 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400149 return ss.AddPublicKey(name, pubKey)
giolekva8aa73e82022-07-09 11:34:39 +0400150}
151
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400152func (ss *realClient) RemoveUser(user string) error {
153 log.Printf("Removing user: %s\n", user)
154 _, err := ss.RunCommand("user", "delete", user)
155 return err
156}
157
gioe72b54f2024-04-22 10:44:41 +0400158func (ss *realClient) MakeUserAdmin(name string) error {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400159 log.Printf("Making user %s admin", name)
gio266c04f2024-07-03 14:18:45 +0400160 _, err := ss.RunCommand("user", "set-admin", name, "true")
161 return err
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400162}
163
gioe72b54f2024-04-22 10:44:41 +0400164func (ss *realClient) AddPublicKey(user string, pubKey string) error {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400165 log.Printf("Adding public key: %s %s\n", user, pubKey)
gio266c04f2024-07-03 14:18:45 +0400166 _, err := ss.RunCommand("user", "add-pubkey", user, pubKey)
167 return err
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400168}
169
gioe72b54f2024-04-22 10:44:41 +0400170func (ss *realClient) RemovePublicKey(user string, pubKey string) error {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400171 log.Printf("Removing public key: %s %s\n", user, pubKey)
gio266c04f2024-07-03 14:18:45 +0400172 _, err := ss.RunCommand("user", "remove-pubkey", user, pubKey)
173 return err
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400174}
175
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400176func (ss *realClient) GetUserPublicKeys(user string) ([]string, error) {
177 log.Printf("Getting public keys for user: %s\n", user)
178 out, err := ss.RunCommand("user", "info", user)
179 if err != nil {
180 return nil, err
181 }
182 return extractPublicKeys(out), nil
183}
184
185func extractPublicKeys(userInfo string) []string {
186 var keys []string
187 lines := strings.Split(userInfo, "\n")
188 gettingKeys := false
189 for _, line := range lines {
190 if strings.HasPrefix(line, "Public keys:") {
191 gettingKeys = true
192 continue
193 }
194 if gettingKeys {
Davit Tabidze4aaa27b2024-08-05 20:23:50 +0400195 if key := CleanKey(line); key != "" {
196 keys = append(keys, key)
197 }
Davit Tabidzea5ea5092024-08-01 15:28:09 +0400198 }
199 }
200 return keys
201}
202
Davit Tabidze4aaa27b2024-08-05 20:23:50 +0400203func CleanKey(key string) string {
204 k := strings.TrimSpace(key)
205 fields := strings.Fields(k)
206 if len(fields) < 2 {
207 return k
208 }
209 return fields[0] + " " + fields[1]
210}
211
gio266c04f2024-07-03 14:18:45 +0400212func (ss *realClient) RunCommand(args ...string) (string, error) {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400213 cmd := strings.Join(args, " ")
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400214 log.Printf("Running command %s", cmd)
gioe72b54f2024-04-22 10:44:41 +0400215 client, err := ssh.Dial("tcp", ss.addr, ss.sshClientConfig())
giolekva8aa73e82022-07-09 11:34:39 +0400216 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400217 return "", err
giolekva8aa73e82022-07-09 11:34:39 +0400218 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400219 defer client.Close()
giolekva8aa73e82022-07-09 11:34:39 +0400220 session, err := client.NewSession()
221 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400222 return "", err
giolekva8aa73e82022-07-09 11:34:39 +0400223 }
224 defer session.Close()
gio266c04f2024-07-03 14:18:45 +0400225 var buf strings.Builder
226 session.Stdout = &buf
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400227 session.Stderr = os.Stderr
gio266c04f2024-07-03 14:18:45 +0400228 err = session.Run(cmd)
229 return buf.String(), err
230}
231
gio33059762024-07-05 13:19:07 +0400232func (ss *realClient) RepoExists(name string) (bool, error) {
gio266c04f2024-07-03 14:18:45 +0400233 out, err := ss.RunCommand("repo", "list")
234 if err != nil {
235 return false, err
236 }
237 return slices.Contains(strings.Fields(out), name), nil
giolekva8aa73e82022-07-09 11:34:39 +0400238}
239
gioe72b54f2024-04-22 10:44:41 +0400240func (ss *realClient) AddRepository(name string) error {
giolekva8aa73e82022-07-09 11:34:39 +0400241 log.Printf("Adding repository %s", name)
gio33059762024-07-05 13:19:07 +0400242 if ok, err := ss.RepoExists(name); ok {
gio0eaf2712024-04-14 13:08:46 +0400243 return ErrorAlreadyExists
gio266c04f2024-07-03 14:18:45 +0400244 } else if err != nil {
245 return err
gio0eaf2712024-04-14 13:08:46 +0400246 }
gio266c04f2024-07-03 14:18:45 +0400247 _, err := ss.RunCommand("repo", "create", name)
248 return err
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400249}
250
gioe72b54f2024-04-22 10:44:41 +0400251func (ss *realClient) AddReadWriteCollaborator(repo, user string) error {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400252 log.Printf("Adding read-write collaborator %s %s", repo, user)
gio266c04f2024-07-03 14:18:45 +0400253 _, err := ss.RunCommand("repo", "collab", "add", repo, user, "read-write")
254 return err
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400255}
256
gioe72b54f2024-04-22 10:44:41 +0400257func (ss *realClient) AddReadOnlyCollaborator(repo, user string) error {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400258 log.Printf("Adding read-only collaborator %s %s", repo, user)
gio266c04f2024-07-03 14:18:45 +0400259 _, err := ss.RunCommand("repo", "collab", "add", repo, user, "read-only")
260 return err
giolekva8aa73e82022-07-09 11:34:39 +0400261}
262
gio0eaf2712024-04-14 13:08:46 +0400263func (ss *realClient) AddWebhook(repo, url string, opts ...string) error {
264 log.Printf("Adding webhook %s %s", repo, url)
gio266c04f2024-07-03 14:18:45 +0400265 _, err := ss.RunCommand(append(
gio0eaf2712024-04-14 13:08:46 +0400266 []string{"repo", "webhook", "create", repo, url},
267 opts...,
268 )...)
gio266c04f2024-07-03 14:18:45 +0400269 return err
gio0eaf2712024-04-14 13:08:46 +0400270}
271
gio7fbd4ad2024-08-27 10:06:39 +0400272func (ss *realClient) DisableAnonAccess() error {
273 log.Printf("Disabling anon access")
274 _, err := ss.RunCommand("settings", "anon-access", "no-access")
275 return err
276}
277
278func (ss *realClient) DisableKeyless() error {
279 log.Printf("Disabling anon access")
280 _, err := ss.RunCommand("settings", "allow-keyless", "false")
281 return err
282}
283
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400284type Repository struct {
285 *git.Repository
286 Addr RepositoryAddress
gio7fbd4ad2024-08-27 10:06:39 +0400287 Ref string
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400288}
289
gioe72b54f2024-04-22 10:44:41 +0400290func (ss *realClient) GetRepo(name string) (RepoIO, error) {
gio7fbd4ad2024-08-27 10:06:39 +0400291 return ss.GetRepoBranch(name, "master")
292}
293
294func (ss *realClient) GetRepoBranch(name, branch string) (RepoIO, error) {
295 r, err := CloneRepositoryBranch(RepositoryAddress{ss.addr, name}, branch, ss.signer)
gioe72b54f2024-04-22 10:44:41 +0400296 if err != nil {
297 return nil, err
298 }
299 return NewRepoIO(r, ss.signer)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400300}
301
giocafd4e62024-07-31 10:53:40 +0400302func (ss *realClient) GetAllRepos() ([]string, error) {
303 log.Printf("Getting all repos")
304 out, err := ss.RunCommand("repo", "list")
305 if err != nil {
306 return nil, err
307 }
308 return strings.Fields(out), nil
309}
310
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400311type RepositoryAddress struct {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400312 Addr string
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400313 Name string
314}
315
316func ParseRepositoryAddress(addr string) (RepositoryAddress, error) {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400317 items := regexp.MustCompile(`ssh://(.*)/(.*)`).FindStringSubmatch(addr)
318 if len(items) != 3 {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400319 return RepositoryAddress{}, fmt.Errorf("Invalid address")
320 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400321 return RepositoryAddress{items[1], items[2]}, nil
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400322}
323
324func (r RepositoryAddress) FullAddress() string {
325 return fmt.Sprintf("ssh://%s/%s", r.Addr, r.Name)
326}
327
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400328func CloneRepository(addr RepositoryAddress, signer ssh.Signer) (*Repository, error) {
gio7fbd4ad2024-08-27 10:06:39 +0400329 return CloneRepositoryBranch(addr, "master", signer)
330}
331
332func CloneRepositoryBranch(addr RepositoryAddress, branch string, signer ssh.Signer) (*Repository, error) {
333 fmt.Printf("Cloning repository: %s %s %s\n", addr.Addr, addr.Name, branch)
334 ref := fmt.Sprintf("refs/heads/%s", branch)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400335 c, err := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
336 URL: addr.FullAddress(),
337 Auth: &gitssh.PublicKeys{
338 User: "git",
339 Signer: signer,
340 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
341 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
342 // TODO(giolekva): verify server public key
343 fmt.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
344 return nil
345 },
346 },
347 },
Giorgi Lekveishvili87be4ae2023-06-11 23:41:09 +0400348 RemoteName: "origin",
gio7fbd4ad2024-08-27 10:06:39 +0400349 ReferenceName: plumbing.ReferenceName(ref),
giof5ffedb2024-06-19 14:14:43 +0400350 SingleBranch: true,
Giorgi Lekveishvili3550b432023-06-09 19:37:51 +0400351 Depth: 1,
352 InsecureSkipTLS: true,
353 Progress: os.Stdout,
354 })
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400355 if err != nil && !errors.Is(err, transport.ErrEmptyRemoteRepository) {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400356 return nil, err
357 }
giof5ffedb2024-06-19 14:14:43 +0400358 wt, err := c.Worktree()
359 if err != nil {
360 return nil, err
361 }
362 sb, err := wt.Submodules()
363 if err != nil {
364 return nil, err
365 }
366 if err := sb.Init(); err != nil {
367 return nil, err
368 }
369 if err := sb.Update(&git.SubmoduleUpdateOptions{
370 Depth: 1,
371 }); err != nil {
372 return nil, err
373 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400374 return &Repository{
375 Repository: c,
376 Addr: addr,
gio7fbd4ad2024-08-27 10:06:39 +0400377 Ref: ref,
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400378 }, nil
Giorgi Lekveishvili3550b432023-06-09 19:37:51 +0400379}
380
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400381// TODO(giolekva): dead code
gioe72b54f2024-04-22 10:44:41 +0400382func (ss *realClient) authSSH() gitssh.AuthMethod {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400383 a, err := gitssh.NewPublicKeys("git", ss.pemBytes, "")
giolekva8aa73e82022-07-09 11:34:39 +0400384 if err != nil {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400385 panic(err)
giolekva8aa73e82022-07-09 11:34:39 +0400386 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400387 a.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
388 // TODO(giolekva): verify server public key
389 ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
390 return nil
giolekva8aa73e82022-07-09 11:34:39 +0400391 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400392 return a
393 // return &gitssh.PublicKeys{
394 // User: "git",
395 // Signer: ss.Signer,
396 // HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
397 // HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
398 // // TODO(giolekva): verify server public key
399 // ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
400 // return nil
401 // },
402 // },
403 // }
giolekva8aa73e82022-07-09 11:34:39 +0400404}
405
gioe72b54f2024-04-22 10:44:41 +0400406func (ss *realClient) authGit() *gitssh.PublicKeys {
giolekva8aa73e82022-07-09 11:34:39 +0400407 return &gitssh.PublicKeys{
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400408 User: "git",
gioe72b54f2024-04-22 10:44:41 +0400409 Signer: ss.signer,
giolekva8aa73e82022-07-09 11:34:39 +0400410 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
411 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
412 // TODO(giolekva): verify server public key
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400413 ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
giolekva8aa73e82022-07-09 11:34:39 +0400414 return nil
415 },
416 },
417 }
418}
419
gioe72b54f2024-04-22 10:44:41 +0400420func (ss *realClient) GetPublicKeys() ([]string, error) {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400421 var ret []string
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400422 config := &ssh.ClientConfig{
423 Auth: []ssh.AuthMethod{
gioe72b54f2024-04-22 10:44:41 +0400424 ssh.PublicKeys(ss.signer),
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400425 },
426 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400427 ret = append(ret, string(ssh.MarshalAuthorizedKey(key)))
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400428 return nil
429 },
430 }
gioe72b54f2024-04-22 10:44:41 +0400431 client, err := ssh.Dial("tcp", ss.addr, config)
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400432 if err != nil {
433 return nil, err
434 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400435 defer client.Close()
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400436 return ret, nil
437}
438
gioe72b54f2024-04-22 10:44:41 +0400439func (ss *realClient) sshClientConfig() *ssh.ClientConfig {
giolekva8aa73e82022-07-09 11:34:39 +0400440 return &ssh.ClientConfig{
441 Auth: []ssh.AuthMethod{
gioe72b54f2024-04-22 10:44:41 +0400442 ssh.PublicKeys(ss.signer),
giolekva8aa73e82022-07-09 11:34:39 +0400443 },
444 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
445 // TODO(giolekva): verify server public key
446 // fmt.Printf("## %s || %s -- \n", serverPubKey, ssh.MarshalAuthorizedKey(key))
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400447 fmt.Printf("%s %s %s", hostname, remote, ssh.MarshalAuthorizedKey(key))
giolekva8aa73e82022-07-09 11:34:39 +0400448 return nil
449 },
450 }
451}
452
gioe72b54f2024-04-22 10:44:41 +0400453func (ss *realClient) GetRepoAddress(name string) string {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400454 return fmt.Sprintf("%s/%s", ss.addressGit(), name)
455}
456
gioe72b54f2024-04-22 10:44:41 +0400457func (ss *realClient) addressGit() string {
458 return fmt.Sprintf("ssh://%s", ss.addr)
giolekva8aa73e82022-07-09 11:34:39 +0400459}