diff options
Diffstat (limited to 'models/publickey.go')
-rw-r--r-- | models/publickey.go | 147 |
1 files changed, 128 insertions, 19 deletions
diff --git a/models/publickey.go b/models/publickey.go index 49d7f308..ee6bd531 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -1,21 +1,32 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package models import ( + "bufio" + "errors" "fmt" + "io" "os" "os/exec" + "path" "path/filepath" + "strings" + "sync" "time" "github.com/Unknwon/com" ) var ( + sshOpLocker = sync.Mutex{} //publicKeyRootPath string - sshPath string - appPath string - tmplPublicKey = "### autogenerated by gitgos, DO NOT EDIT\n" + - "command=\"%s serv key-%d\",no-port-forwarding," + + sshPath string + appPath string + // "### autogenerated by gitgos, DO NOT EDIT\n" + tmplPublicKey = "command=\"%s serv key-%d\",no-port-forwarding," + "no-X11-forwarding,no-agent-forwarding,no-pty %s\n" ) @@ -47,29 +58,60 @@ func init() { } type PublicKey struct { - Id int64 - OwnerId int64 `xorm:"index"` - Name string `xorm:"unique not null"` - Content string `xorm:"text not null"` - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` + Id int64 + OwnerId int64 `xorm:"index"` + Name string `xorm:"unique not null"` + Fingerprint string + Content string `xorm:"text not null"` + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` } +var ( + ErrKeyAlreadyExist = errors.New("Public key already exist") +) + func GenAuthorizedKey(keyId int64, key string) string { return fmt.Sprintf(tmplPublicKey, appPath, keyId, key) } -func AddPublicKey(key *PublicKey) error { - _, err := orm.Insert(key) +func AddPublicKey(key *PublicKey) (err error) { + // Check if public key name has been used. + has, err := orm.Get(key) if err != nil { return err + } else if has { + return ErrKeyAlreadyExist } - err = SaveAuthorizedKeyFile(key) + // Calculate fingerprint. + tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), + "id_rsa.pub") + os.MkdirAll(path.Dir(tmpPath), os.ModePerm) + f, err := os.Create(tmpPath) + if err != nil { + return + } + if _, err = f.WriteString(key.Content); err != nil { + return err + } + f.Close() + stdout, _, err := com.ExecCmd("ssh-keygen", "-l", "-f", tmpPath) if err != nil { - _, err2 := orm.Delete(key) - if err2 != nil { - // TODO: log the error + return err + } else if len(stdout) < 2 { + return errors.New("Not enough output for calculating fingerprint") + } + key.Fingerprint = strings.Split(stdout, " ")[1] + + // Save SSH key. + if _, err = orm.Insert(key); err != nil { + return err + } + + if err = SaveAuthorizedKeyFile(key); err != nil { + if _, err2 := orm.Delete(key); err2 != nil { + return err2 } return err } @@ -77,9 +119,71 @@ func AddPublicKey(key *PublicKey) error { return nil } -func DeletePublicKey(key *PublicKey) error { - _, err := orm.Delete(key) - return err +// DeletePublicKey deletes SSH key information both in database and authorized_keys file. +func DeletePublicKey(key *PublicKey) (err error) { + has, err := orm.Id(key.Id).Get(key) + if err != nil { + return err + } else if !has { + return errors.New("Public key does not exist") + } + if _, err = orm.Delete(key); err != nil { + return err + } + + sshOpLocker.Lock() + defer sshOpLocker.Unlock() + + p := filepath.Join(sshPath, "authorized_keys") + tmpP := filepath.Join(sshPath, "authorized_keys.tmp") + fr, err := os.Open(p) + if err != nil { + return err + } + defer fr.Close() + + fw, err := os.Create(tmpP) + if err != nil { + return err + } + defer fw.Close() + + buf := bufio.NewReader(fr) + for { + line, errRead := buf.ReadString('\n') + line = strings.TrimSpace(line) + + if errRead != nil { + if errRead != io.EOF { + return errRead + } + + // Reached end of file, if nothing to read then break, + // otherwise handle the last line. + if len(line) == 0 { + break + } + } + + // Found the line and copy rest of file. + if strings.Contains(line, fmt.Sprintf("key-%d", key.Id)) && strings.Contains(line, key.Content) { + continue + } + // Still finding the line, copy the line that currently read. + if _, err = fw.WriteString(line + "\n"); err != nil { + return err + } + + if errRead == io.EOF { + break + } + } + + if err = os.Remove(p); err != nil { + return err + } + + return os.Rename(tmpP, p) } func ListPublicKey(userId int64) ([]PublicKey, error) { @@ -89,11 +193,16 @@ func ListPublicKey(userId int64) ([]PublicKey, error) { } func SaveAuthorizedKeyFile(key *PublicKey) error { + sshOpLocker.Lock() + defer sshOpLocker.Unlock() + p := filepath.Join(sshPath, "authorized_keys") f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return err } + defer f.Close() + //os.Chmod(p, 0600) _, err = f.WriteString(GenAuthorizedKey(key.Id, key.Content)) return err |