Use fingerprint to check instead content for public key (#911)

* use fingerprint to check instead content for public key

* add fingerprint field for ErrKeyAlreadyExist
This commit is contained in:
Lunny Xiao 2017-02-14 14:12:52 +08:00 committed by GitHub
parent 55ae78208e
commit 7eb8daffa3
2 changed files with 47 additions and 26 deletions

View file

@ -213,8 +213,9 @@ func (err ErrKeyNotExist) Error() string {
// ErrKeyAlreadyExist represents a "KeyAlreadyExist" kind of error. // ErrKeyAlreadyExist represents a "KeyAlreadyExist" kind of error.
type ErrKeyAlreadyExist struct { type ErrKeyAlreadyExist struct {
OwnerID int64 OwnerID int64
Content string Fingerprint string
Content string
} }
// IsErrKeyAlreadyExist checks if an error is a ErrKeyAlreadyExist. // IsErrKeyAlreadyExist checks if an error is a ErrKeyAlreadyExist.
@ -224,7 +225,8 @@ func IsErrKeyAlreadyExist(err error) bool {
} }
func (err ErrKeyAlreadyExist) Error() string { func (err ErrKeyAlreadyExist) Error() string {
return fmt.Sprintf("public key already exists [owner_id: %d, content: %s]", err.OwnerID, err.Content) return fmt.Sprintf("public key already exists [owner_id: %d, finter_print: %s, content: %s]",
err.OwnerID, err.Fingerprint, err.Content)
} }
// ErrKeyNameAlreadyUsed represents a "KeyNameAlreadyUsed" kind of error. // ErrKeyNameAlreadyUsed represents a "KeyNameAlreadyUsed" kind of error.

View file

@ -354,41 +354,50 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
return nil return nil
} }
// checkKeyContent only checks if key content has been used as public key, // checkKeyFingerprint only checks if key fingerprint has been used as public key,
// it is OK to use same key as deploy key for multiple repositories/users. // it is OK to use same key as deploy key for multiple repositories/users.
func checkKeyContent(content string) error { func checkKeyFingerprint(e Engine, fingerprint string) error {
has, err := x.Get(&PublicKey{ has, err := e.Get(&PublicKey{
Content: content, Fingerprint: fingerprint,
Type: KeyTypeUser, Type: KeyTypeUser,
}) })
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
return ErrKeyAlreadyExist{0, content} return ErrKeyAlreadyExist{0, fingerprint, ""}
} }
return nil return nil
} }
func addKey(e Engine, key *PublicKey) (err error) { func calcFingerprint(publicKeyContent string) (string, error) {
// Calculate fingerprint. // Calculate fingerprint.
tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
"id_rsa.pub"), "\\", "/", -1) "id_rsa.pub"), "\\", "/", -1)
dir := path.Dir(tmpPath) dir := path.Dir(tmpPath)
if err := os.MkdirAll(dir, os.ModePerm); err != nil { if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return fmt.Errorf("Failed to create dir %s: %v", dir, err) return "", fmt.Errorf("Failed to create dir %s: %v", dir, err)
} }
if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil { if err := ioutil.WriteFile(tmpPath, []byte(publicKeyContent), 0644); err != nil {
return err return "", err
} }
stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath) stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
if err != nil { if err != nil {
return fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr) return "", fmt.Errorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
} else if len(stdout) < 2 { } else if len(stdout) < 2 {
return errors.New("not enough output for calculating fingerprint: " + stdout) return "", errors.New("not enough output for calculating fingerprint: " + stdout)
}
return strings.Split(stdout, " ")[1], nil
}
func addKey(e Engine, key *PublicKey) (err error) {
if len(key.Fingerprint) <= 0 {
key.Fingerprint, err = calcFingerprint(key.Content)
if err != nil {
return err
}
} }
key.Fingerprint = strings.Split(stdout, " ")[1]
// Save SSH key. // Save SSH key.
if _, err = e.Insert(key); err != nil { if _, err = e.Insert(key); err != nil {
@ -405,7 +414,13 @@ func addKey(e Engine, key *PublicKey) (err error) {
// AddPublicKey adds new public key to database and authorized_keys file. // AddPublicKey adds new public key to database and authorized_keys file.
func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) { func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
log.Trace(content) log.Trace(content)
if err := checkKeyContent(content); err != nil {
fingerprint, err := calcFingerprint(content)
if err != nil {
return nil, err
}
if err := checkKeyFingerprint(x, fingerprint); err != nil {
return nil, err return nil, err
} }
@ -426,11 +441,12 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
} }
key := &PublicKey{ key := &PublicKey{
OwnerID: ownerID, OwnerID: ownerID,
Name: name, Name: name,
Content: content, Fingerprint: fingerprint,
Mode: AccessModeWrite, Content: content,
Type: KeyTypeUser, Mode: AccessModeWrite,
Type: KeyTypeUser,
} }
if err = addKey(sess, key); err != nil { if err = addKey(sess, key); err != nil {
return nil, fmt.Errorf("addKey: %v", err) return nil, fmt.Errorf("addKey: %v", err)
@ -665,14 +681,15 @@ func HasDeployKey(keyID, repoID int64) bool {
// AddDeployKey add new deploy key to database and authorized_keys file. // AddDeployKey add new deploy key to database and authorized_keys file.
func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) { func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) {
if err := checkKeyContent(content); err != nil { fingerprint, err := calcFingerprint(content)
if err != nil {
return nil, err return nil, err
} }
pkey := &PublicKey{ pkey := &PublicKey{
Content: content, Fingerprint: fingerprint,
Mode: AccessModeRead, Mode: AccessModeRead,
Type: KeyTypeDeploy, Type: KeyTypeDeploy,
} }
has, err := x.Get(pkey) has, err := x.Get(pkey)
if err != nil { if err != nil {
@ -687,6 +704,8 @@ func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) {
// First time use this deploy key. // First time use this deploy key.
if !has { if !has {
pkey.Content = content
pkey.Name = name
if err = addKey(sess, pkey); err != nil { if err = addKey(sess, pkey); err != nil {
return nil, fmt.Errorf("addKey: %v", err) return nil, fmt.Errorf("addKey: %v", err)
} }