mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	add .gpg url (match github behaviour) (#6610)
* add .gpg url (match github behaviour) * wildcard * test to export maximum data * working POC * add comment for old imported keys * cleaning * Update routers/user/profile.go Co-Authored-By: sapk <sapk@users.noreply.github.com> * add migration script * add integration tests
This commit is contained in:
		
				
					committed by
					
						
						techknowlogick
					
				
			
			
				
	
			
			
			
						parent
						
							38889f09cb
						
					
				
				
					commit
					d699de32f2
				
			@@ -101,3 +101,90 @@ func TestRenameReservedUsername(t *testing.T) {
 | 
			
		||||
		models.AssertNotExistsBean(t, &models.User{Name: reservedUsername})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExportUserGPGKeys(t *testing.T) {
 | 
			
		||||
	prepareTestEnv(t)
 | 
			
		||||
	//Export empty key list
 | 
			
		||||
	testExportUserGPGKeys(t, "user1", `-----BEGIN PGP PUBLIC KEY BLOCK-----
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=twTO
 | 
			
		||||
-----END PGP PUBLIC KEY BLOCK-----
 | 
			
		||||
`)
 | 
			
		||||
	//Import key
 | 
			
		||||
	//User1 <user1@example.com>
 | 
			
		||||
	session := loginUser(t, "user1")
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
	testCreateGPGKey(t, session.MakeRequest, token, http.StatusCreated, `-----BEGIN PGP PUBLIC KEY BLOCK-----
 | 
			
		||||
 | 
			
		||||
mQENBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo
 | 
			
		||||
QzubgzvMPITDy7nfWxgSf83E23DoHQ1ACFbQh/6eFSRrjsusp3YQ/08NSfPPbcu8
 | 
			
		||||
0M5G+VGwSfzS5uEcwBVQmHyKdcOZIERTNMtYZx1C3bjLD1XVJHvWz9D72Uq4qeO3
 | 
			
		||||
8SR+lzp5n6ppUakcmRnxt3nGRBj1+hEGkdgzyPo93iy+WioegY2lwCA9xMEo5dah
 | 
			
		||||
BmYxWx51zyiXYlReTaxlyb3/nuSUt8IcW3Q8zjdtJj4Nu8U1SpV8EdaA1I9IPbHW
 | 
			
		||||
510OSLmD3XhqHH5m6mIxL1YoWxk3V7gpDROtABEBAAG0GVVzZXIxIDx1c2VyMUBl
 | 
			
		||||
eGFtcGxlLmNvbT6JAU4EEwEIADgWIQTQEbrYxmXsp1z3j7z9+v0I6RSEHwUCXLL9
 | 
			
		||||
VQIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD9+v0I6RSEH22YCACFqL5+
 | 
			
		||||
6M0m18AMC/pumcpnnmvAS1GrrKTF8nOROA1augZwp1WCNuKw2R6uOJIHANrYECSn
 | 
			
		||||
u7+j6GBP2gbIW8mSAzS6HWCs7GGiPpVtT4wcu8wljUI6BxjpyZtoEkriyBjt6HfK
 | 
			
		||||
rkegbkuySoJvjq4IcO5D1LB1JWgsUjMYQJj/ZpBIzVtjG9QtFSOiT1Hct4PoZHdC
 | 
			
		||||
nsdSgyCkwRZXG+u3kT/wP9F663ba4o16vYlz3dCGo66lF2tyoG3qcyZ1OUzUrnuv
 | 
			
		||||
96ytAzT6XIhrE0nVoBprMxFF5zExotJD3bHjcGBFNLf944bhjKee3U6t9+OsfJVC
 | 
			
		||||
l7N5xxIawCuTQdbfuQENBFyy/VUBCADe61yGEoTwKfsOKIhxLaNoRmD883O0tiWt
 | 
			
		||||
soO/HPj9dPQLTOiwXgSgSCd8C+LNxGKct87wgFozpah4tDLC6c0nALuHJ0SLbkfz
 | 
			
		||||
55aRhLeOOcrAydatDp72GroXzqpZ0xZBk5wjIWdgEol2GmVRM8QGbeuakU/HVz5y
 | 
			
		||||
lPzxUUocgdbSi3GE3zbzijQzVJdyL/kw/KP7pKT/PPKKJ2C5NQDLy0XGKEHddXGR
 | 
			
		||||
EWKkVlRalxq/TjfaMR0bi3MpezBsQmp99ATPO/d7trayZUxQHRtXzGFiOXfDHATr
 | 
			
		||||
qN730sODjqvU+mpc/SHCRwh9qWDjZRHSuKU5YDBjb5jIQJivZsQ/ABEBAAGJATYE
 | 
			
		||||
GAEIACAWIQTQEbrYxmXsp1z3j7z9+v0I6RSEHwUCXLL9VQIbDAAKCRD9+v0I6RSE
 | 
			
		||||
H7WoB/4tXl+97rQ6owPCGSVp1Xbwt2521V7COgsOFRVTRTryEWxRW8mm0S7wQvax
 | 
			
		||||
C0TLXKur6NVYQMn01iyL+FZzRpEWNuYF3f9QeeLJ/+l2DafESNhNTy17+RPmacK6
 | 
			
		||||
21dccpqchByVw/UMDeHSyjQLiG2lxzt8Gfx2gHmSbrq3aWovTGyz6JTffZvfy/n2
 | 
			
		||||
0Hm437OBPazO0gZyXhdV2PE5RSUfvAgm44235tcV5EV0d32TJDfv61+Vr2GUbah6
 | 
			
		||||
7XhJ1v6JYuh8kaYaEz8OpZDeh7f6Ho6PzJrsy/TKTKhGgZNINj1iaPFyOkQgKR5M
 | 
			
		||||
GrE0MHOxUbc9tbtyk0F1SuzREUBH
 | 
			
		||||
=DDXw
 | 
			
		||||
-----END PGP PUBLIC KEY BLOCK-----
 | 
			
		||||
`)
 | 
			
		||||
	//Export new key
 | 
			
		||||
	testExportUserGPGKeys(t, "user1", `-----BEGIN PGP PUBLIC KEY BLOCK-----
 | 
			
		||||
 | 
			
		||||
xsBNBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo
 | 
			
		||||
QzubgzvMPITDy7nfWxgSf83E23DoHQ1ACFbQh/6eFSRrjsusp3YQ/08NSfPPbcu8
 | 
			
		||||
0M5G+VGwSfzS5uEcwBVQmHyKdcOZIERTNMtYZx1C3bjLD1XVJHvWz9D72Uq4qeO3
 | 
			
		||||
8SR+lzp5n6ppUakcmRnxt3nGRBj1+hEGkdgzyPo93iy+WioegY2lwCA9xMEo5dah
 | 
			
		||||
BmYxWx51zyiXYlReTaxlyb3/nuSUt8IcW3Q8zjdtJj4Nu8U1SpV8EdaA1I9IPbHW
 | 
			
		||||
510OSLmD3XhqHH5m6mIxL1YoWxk3V7gpDROtABEBAAHNGVVzZXIxIDx1c2VyMUBl
 | 
			
		||||
eGFtcGxlLmNvbT7CwI4EEwEIADgWIQTQEbrYxmXsp1z3j7z9+v0I6RSEHwUCXLL9
 | 
			
		||||
VQIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD9+v0I6RSEH22YCACFqL5+
 | 
			
		||||
6M0m18AMC/pumcpnnmvAS1GrrKTF8nOROA1augZwp1WCNuKw2R6uOJIHANrYECSn
 | 
			
		||||
u7+j6GBP2gbIW8mSAzS6HWCs7GGiPpVtT4wcu8wljUI6BxjpyZtoEkriyBjt6HfK
 | 
			
		||||
rkegbkuySoJvjq4IcO5D1LB1JWgsUjMYQJj/ZpBIzVtjG9QtFSOiT1Hct4PoZHdC
 | 
			
		||||
nsdSgyCkwRZXG+u3kT/wP9F663ba4o16vYlz3dCGo66lF2tyoG3qcyZ1OUzUrnuv
 | 
			
		||||
96ytAzT6XIhrE0nVoBprMxFF5zExotJD3bHjcGBFNLf944bhjKee3U6t9+OsfJVC
 | 
			
		||||
l7N5xxIawCuTQdbfzsBNBFyy/VUBCADe61yGEoTwKfsOKIhxLaNoRmD883O0tiWt
 | 
			
		||||
soO/HPj9dPQLTOiwXgSgSCd8C+LNxGKct87wgFozpah4tDLC6c0nALuHJ0SLbkfz
 | 
			
		||||
55aRhLeOOcrAydatDp72GroXzqpZ0xZBk5wjIWdgEol2GmVRM8QGbeuakU/HVz5y
 | 
			
		||||
lPzxUUocgdbSi3GE3zbzijQzVJdyL/kw/KP7pKT/PPKKJ2C5NQDLy0XGKEHddXGR
 | 
			
		||||
EWKkVlRalxq/TjfaMR0bi3MpezBsQmp99ATPO/d7trayZUxQHRtXzGFiOXfDHATr
 | 
			
		||||
qN730sODjqvU+mpc/SHCRwh9qWDjZRHSuKU5YDBjb5jIQJivZsQ/ABEBAAHCwHYE
 | 
			
		||||
GAEIACAWIQTQEbrYxmXsp1z3j7z9+v0I6RSEHwUCXLL9VQIbDAAKCRD9+v0I6RSE
 | 
			
		||||
H7WoB/4tXl+97rQ6owPCGSVp1Xbwt2521V7COgsOFRVTRTryEWxRW8mm0S7wQvax
 | 
			
		||||
C0TLXKur6NVYQMn01iyL+FZzRpEWNuYF3f9QeeLJ/+l2DafESNhNTy17+RPmacK6
 | 
			
		||||
21dccpqchByVw/UMDeHSyjQLiG2lxzt8Gfx2gHmSbrq3aWovTGyz6JTffZvfy/n2
 | 
			
		||||
0Hm437OBPazO0gZyXhdV2PE5RSUfvAgm44235tcV5EV0d32TJDfv61+Vr2GUbah6
 | 
			
		||||
7XhJ1v6JYuh8kaYaEz8OpZDeh7f6Ho6PzJrsy/TKTKhGgZNINj1iaPFyOkQgKR5M
 | 
			
		||||
GrE0MHOxUbc9tbtyk0F1SuzREUBH
 | 
			
		||||
=WFf5
 | 
			
		||||
-----END PGP PUBLIC KEY BLOCK-----
 | 
			
		||||
`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testExportUserGPGKeys(t *testing.T, user, expected string) {
 | 
			
		||||
	session := loginUser(t, user)
 | 
			
		||||
	t.Logf("Testing username %s export gpg keys", user)
 | 
			
		||||
	req := NewRequest(t, "GET", "/"+user+".gpg")
 | 
			
		||||
	resp := session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
	//t.Log(resp.Body.String())
 | 
			
		||||
	assert.Equal(t, expected, resp.Body.String())
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -379,6 +379,21 @@ func (err ErrGPGKeyNotExist) Error() string {
 | 
			
		||||
	return fmt.Sprintf("public gpg key does not exist [id: %d]", err.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrGPGKeyImportNotExist represents a "GPGKeyImportNotExist" kind of error.
 | 
			
		||||
type ErrGPGKeyImportNotExist struct {
 | 
			
		||||
	ID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsErrGPGKeyImportNotExist checks if an error is a ErrGPGKeyImportNotExist.
 | 
			
		||||
func IsErrGPGKeyImportNotExist(err error) bool {
 | 
			
		||||
	_, ok := err.(ErrGPGKeyImportNotExist)
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err ErrGPGKeyImportNotExist) Error() string {
 | 
			
		||||
	return fmt.Sprintf("public gpg key import does not exist [id: %s]", err.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrGPGKeyIDAlreadyUsed represents a "GPGKeyIDAlreadyUsed" kind of error.
 | 
			
		||||
type ErrGPGKeyIDAlreadyUsed struct {
 | 
			
		||||
	KeyID string
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								models/fixtures/gpg_key_import.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								models/fixtures/gpg_key_import.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
[] # empty
 | 
			
		||||
@@ -43,6 +43,12 @@ type GPGKey struct {
 | 
			
		||||
	CanCertify        bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//GPGKeyImport the original import of key
 | 
			
		||||
type GPGKeyImport struct {
 | 
			
		||||
	KeyID   string `xorm:"pk CHAR(16) NOT NULL"`
 | 
			
		||||
	Content string `xorm:"TEXT NOT NULL"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BeforeInsert will be invoked by XORM before inserting a record
 | 
			
		||||
func (key *GPGKey) BeforeInsert() {
 | 
			
		||||
	key.AddedUnix = util.TimeStampNow()
 | 
			
		||||
@@ -74,6 +80,18 @@ func GetGPGKeyByID(keyID int64) (*GPGKey, error) {
 | 
			
		||||
	return key, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGPGImportByKeyID returns the import public armored key by given KeyID.
 | 
			
		||||
func GetGPGImportByKeyID(keyID string) (*GPGKeyImport, error) {
 | 
			
		||||
	key := new(GPGKeyImport)
 | 
			
		||||
	has, err := x.ID(keyID).Get(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else if !has {
 | 
			
		||||
		return nil, ErrGPGKeyImportNotExist{keyID}
 | 
			
		||||
	}
 | 
			
		||||
	return key, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
 | 
			
		||||
// The function returns the actual public key on success
 | 
			
		||||
func checkArmoredGPGKeyString(content string) (*openpgp.Entity, error) {
 | 
			
		||||
@@ -84,15 +102,37 @@ func checkArmoredGPGKeyString(content string) (*openpgp.Entity, error) {
 | 
			
		||||
	return list[0], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//addGPGKey add key and subkeys to database
 | 
			
		||||
func addGPGKey(e Engine, key *GPGKey) (err error) {
 | 
			
		||||
//addGPGKey add key, import and subkeys to database
 | 
			
		||||
func addGPGKey(e Engine, key *GPGKey, content string) (err error) {
 | 
			
		||||
	//Add GPGKeyImport
 | 
			
		||||
	if _, err = e.Insert(GPGKeyImport{
 | 
			
		||||
		KeyID:   key.KeyID,
 | 
			
		||||
		Content: content,
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// Save GPG primary key.
 | 
			
		||||
	if _, err = e.Insert(key); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// Save GPG subs key.
 | 
			
		||||
	for _, subkey := range key.SubsKey {
 | 
			
		||||
		if err := addGPGKey(e, subkey); err != nil {
 | 
			
		||||
		if err := addGPGSubKey(e, subkey); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//addGPGSubKey add subkeys to database
 | 
			
		||||
func addGPGSubKey(e Engine, key *GPGKey) (err error) {
 | 
			
		||||
	// Save GPG primary key.
 | 
			
		||||
	if _, err = e.Insert(key); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// Save GPG subs key.
 | 
			
		||||
	for _, subkey := range key.SubsKey {
 | 
			
		||||
		if err := addGPGSubKey(e, subkey); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -127,14 +167,14 @@ func AddGPGKey(ownerID int64, content string) (*GPGKey, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = addGPGKey(sess, key); err != nil {
 | 
			
		||||
	if err = addGPGKey(sess, key, content); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return key, sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//base64EncPubKey encode public kay content to base 64
 | 
			
		||||
//base64EncPubKey encode public key content to base 64
 | 
			
		||||
func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
 | 
			
		||||
	var w bytes.Buffer
 | 
			
		||||
	err := pubkey.Serialize(&w)
 | 
			
		||||
@@ -144,6 +184,34 @@ func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(w.Bytes()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//base64DecPubKey decode public key content from base 64
 | 
			
		||||
func base64DecPubKey(content string) (*packet.PublicKey, error) {
 | 
			
		||||
	b, err := readerFromBase64(content)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	//Read key
 | 
			
		||||
	p, err := packet.Read(b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	//Check type
 | 
			
		||||
	pkey, ok := p.(*packet.PublicKey)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("key is not a public key")
 | 
			
		||||
	}
 | 
			
		||||
	return pkey, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//GPGKeyToEntity retrieve the imported key and the traducted entity
 | 
			
		||||
func GPGKeyToEntity(k *GPGKey) (*openpgp.Entity, error) {
 | 
			
		||||
	impKey, err := GetGPGImportByKeyID(k.KeyID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return checkArmoredGPGKeyString(impKey.Content)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//parseSubGPGKey parse a sub Key
 | 
			
		||||
func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
 | 
			
		||||
	content, err := base64EncPubKey(pubkey)
 | 
			
		||||
@@ -244,6 +312,11 @@ func deleteGPGKey(e *xorm.Session, keyID string) (int64, error) {
 | 
			
		||||
	if keyID == "" {
 | 
			
		||||
		return 0, fmt.Errorf("empty KeyId forbidden") //Should never happen but just to be sure
 | 
			
		||||
	}
 | 
			
		||||
	//Delete imported key
 | 
			
		||||
	n, err := e.Where("key_id=?", keyID).Delete(new(GPGKeyImport))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return n, err
 | 
			
		||||
	}
 | 
			
		||||
	return e.Where("key_id=?", keyID).Or("primary_key_id=?", keyID).Delete(new(GPGKey))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -339,22 +412,10 @@ func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
 | 
			
		||||
		return fmt.Errorf("key can not sign")
 | 
			
		||||
	}
 | 
			
		||||
	//Decode key
 | 
			
		||||
	b, err := readerFromBase64(k.Content)
 | 
			
		||||
	pkey, err := base64DecPubKey(k.Content)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	//Read key
 | 
			
		||||
	p, err := packet.Read(b)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Check type
 | 
			
		||||
	pkey, ok := p.(*packet.PublicKey)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return fmt.Errorf("key is not a public key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pkey.VerifySignature(h, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -221,6 +221,8 @@ var migrations = []Migration{
 | 
			
		||||
	NewMigration("hot fix for wrong release sha1 on release table", fixReleaseSha1OnReleaseTable),
 | 
			
		||||
	// v83 -> v84
 | 
			
		||||
	NewMigration("add uploader id for table attachment", addUploaderIDForAttachment),
 | 
			
		||||
	// v84 -> v85
 | 
			
		||||
	NewMigration("add table to store original imported gpg keys", addGPGKeyImport),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Migrate database to current version
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								models/migrations/v84.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								models/migrations/v84.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
// Copyright 2019 The Gitea 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 migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-xorm/xorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func addGPGKeyImport(x *xorm.Engine) error {
 | 
			
		||||
	type GPGKeyImport struct {
 | 
			
		||||
		KeyID   string `xorm:"pk CHAR(16) NOT NULL"`
 | 
			
		||||
		Content string `xorm:"TEXT NOT NULL"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return x.Sync2(new(GPGKeyImport))
 | 
			
		||||
}
 | 
			
		||||
@@ -108,6 +108,7 @@ func init() {
 | 
			
		||||
		new(LFSMetaObject),
 | 
			
		||||
		new(TwoFactor),
 | 
			
		||||
		new(GPGKey),
 | 
			
		||||
		new(GPGKeyImport),
 | 
			
		||||
		new(RepoUnit),
 | 
			
		||||
		new(RepoRedirect),
 | 
			
		||||
		new(ExternalLoginUser),
 | 
			
		||||
 
 | 
			
		||||
@@ -747,7 +747,7 @@ var (
 | 
			
		||||
		".",
 | 
			
		||||
		"..",
 | 
			
		||||
	}
 | 
			
		||||
	reservedUserPatterns = []string{"*.keys"}
 | 
			
		||||
	reservedUserPatterns = []string{"*.keys", "*.gpg"}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// isUsableName checks if name is reserved or pattern of name is not allowed
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,8 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/Unknwon/com"
 | 
			
		||||
	"github.com/Unknwon/paginater"
 | 
			
		||||
	"github.com/keybase/go-crypto/openpgp"
 | 
			
		||||
	"github.com/keybase/go-crypto/openpgp/armor"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -384,6 +386,45 @@ func ShowSSHKeys(ctx *context.Context, uid int64) {
 | 
			
		||||
	ctx.PlainText(200, buf.Bytes())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShowGPGKeys output all the public GPG keys of user by uid
 | 
			
		||||
func ShowGPGKeys(ctx *context.Context, uid int64) {
 | 
			
		||||
	keys, err := models.ListGPGKeys(uid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("ListGPGKeys", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	entities := make([]*openpgp.Entity, 0)
 | 
			
		||||
	failedEntitiesID := make([]string, 0)
 | 
			
		||||
	for _, k := range keys {
 | 
			
		||||
		e, err := models.GPGKeyToEntity(k)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrGPGKeyImportNotExist(err) {
 | 
			
		||||
				failedEntitiesID = append(failedEntitiesID, k.KeyID)
 | 
			
		||||
				continue //Skip previous import without backup of imported armored key
 | 
			
		||||
			}
 | 
			
		||||
			ctx.ServerError("ShowGPGKeys", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		entities = append(entities, e)
 | 
			
		||||
	}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	headers := make(map[string]string)
 | 
			
		||||
	if len(failedEntitiesID) > 0 { //If some key need re-import to be exported
 | 
			
		||||
		headers["Note"] = fmt.Sprintf("The keys with the following IDs couldn't be exported and need to be reuploaded %s", strings.Join(failedEntitiesID, ", "))
 | 
			
		||||
	}
 | 
			
		||||
	writer, _ := armor.Encode(&buf, "PGP PUBLIC KEY BLOCK", headers)
 | 
			
		||||
	for _, e := range entities {
 | 
			
		||||
		err = e.Serialize(writer) //TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("ShowGPGKeys", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	writer.Close()
 | 
			
		||||
	ctx.PlainText(200, buf.Bytes())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func showOrgProfile(ctx *context.Context) {
 | 
			
		||||
	ctx.SetParams(":org", ctx.Params(":username"))
 | 
			
		||||
	context.HandleOrgAssignment(ctx)
 | 
			
		||||
 
 | 
			
		||||
@@ -59,9 +59,16 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
	isShowKeys := false
 | 
			
		||||
	if strings.HasSuffix(uname, ".keys") {
 | 
			
		||||
		isShowKeys = true
 | 
			
		||||
		uname = strings.TrimSuffix(uname, ".keys")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctxUser := GetUserByName(ctx, strings.TrimSuffix(uname, ".keys"))
 | 
			
		||||
	isShowGPG := false
 | 
			
		||||
	if strings.HasSuffix(uname, ".gpg") {
 | 
			
		||||
		isShowGPG = true
 | 
			
		||||
		uname = strings.TrimSuffix(uname, ".gpg")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctxUser := GetUserByName(ctx, uname)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -72,6 +79,12 @@ func Profile(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Show GPG keys.
 | 
			
		||||
	if isShowGPG {
 | 
			
		||||
		ShowGPGKeys(ctx, ctxUser.ID)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctxUser.IsOrganization() {
 | 
			
		||||
		showOrgProfile(ctx)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user