Allow addition of gpg keyring with multiple keys (#12487)
Related #6778 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		
							parent
							
								
									ae23bbdae3
								
							
						
					
					
						commit
						7c2cf236f8
					
				
					 4 changed files with 86 additions and 68 deletions
				
			
		|  | @ -106,12 +106,12 @@ func GetGPGImportByKeyID(keyID string) (*GPGKeyImport, error) { | ||||||
| 
 | 
 | ||||||
| // checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
 | // checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
 | ||||||
| // The function returns the actual public key on success
 | // The function returns the actual public key on success
 | ||||||
| func checkArmoredGPGKeyString(content string) (*openpgp.Entity, error) { | func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) { | ||||||
| 	list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content)) | 	list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, ErrGPGKeyParsing{err} | 		return nil, ErrGPGKeyParsing{err} | ||||||
| 	} | 	} | ||||||
| 	return list[0], nil | 	return list, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //addGPGKey add key, import and subkeys to database
 | //addGPGKey add key, import and subkeys to database
 | ||||||
|  | @ -152,14 +152,20 @@ func addGPGSubKey(e Engine, key *GPGKey) (err error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AddGPGKey adds new public key to database.
 | // AddGPGKey adds new public key to database.
 | ||||||
| func AddGPGKey(ownerID int64, content string) (*GPGKey, error) { | func AddGPGKey(ownerID int64, content string) ([]*GPGKey, error) { | ||||||
| 	ekey, err := checkArmoredGPGKeyString(content) | 	ekeys, err := checkArmoredGPGKeyString(content) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 	sess := x.NewSession() | ||||||
|  | 	defer sess.Close() | ||||||
|  | 	if err = sess.Begin(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	keys := make([]*GPGKey, 0, len(ekeys)) | ||||||
|  | 	for _, ekey := range ekeys { | ||||||
| 		// Key ID cannot be duplicated.
 | 		// Key ID cannot be duplicated.
 | ||||||
| 	has, err := x.Where("key_id=?", ekey.PrimaryKey.KeyIdString()). | 		has, err := sess.Where("key_id=?", ekey.PrimaryKey.KeyIdString()). | ||||||
| 			Get(new(GPGKey)) | 			Get(new(GPGKey)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
|  | @ -168,11 +174,6 @@ func AddGPGKey(ownerID int64, content string) (*GPGKey, error) { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		//Get DB session
 | 		//Get DB session
 | ||||||
| 	sess := x.NewSession() |  | ||||||
| 	defer sess.Close() |  | ||||||
| 	if err = sess.Begin(); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 		key, err := parseGPGKey(ownerID, ekey) | 		key, err := parseGPGKey(ownerID, ekey) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -182,8 +183,9 @@ func AddGPGKey(ownerID int64, content string) (*GPGKey, error) { | ||||||
| 		if err = addGPGKey(sess, key, content); err != nil { | 		if err = addGPGKey(sess, key, content); err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 
 | 		keys = append(keys, key) | ||||||
| 	return key, sess.Commit() | 	} | ||||||
|  | 	return keys, sess.Commit() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //base64EncPubKey encode public key content to base 64
 | //base64EncPubKey encode public key content to base 64
 | ||||||
|  | @ -221,7 +223,11 @@ func GPGKeyToEntity(k *GPGKey) (*openpgp.Entity, error) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return checkArmoredGPGKeyString(impKey.Content) | 	keys, err := checkArmoredGPGKeyString(impKey.Content) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return keys[0], err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //parseSubGPGKey parse a sub Key
 | //parseSubGPGKey parse a sub Key
 | ||||||
|  | @ -761,7 +767,7 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Otherwise we have to parse the key
 | 	// Otherwise we have to parse the key
 | ||||||
| 	ekey, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent) | 	ekeys, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("Unable to get default signing key: %v", err) | 		log.Error("Unable to get default signing key: %v", err) | ||||||
| 		return &CommitVerification{ | 		return &CommitVerification{ | ||||||
|  | @ -770,6 +776,7 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, | ||||||
| 			Reason:         "gpg.error.generate_hash", | 			Reason:         "gpg.error.generate_hash", | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	for _, ekey := range ekeys { | ||||||
| 		pubkey := ekey.PrimaryKey | 		pubkey := ekey.PrimaryKey | ||||||
| 		content, err := base64EncPubKey(pubkey) | 		content, err := base64EncPubKey(pubkey) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -814,6 +821,7 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, | ||||||
| 				Reason:         BadSignature, | 				Reason:         BadSignature, | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -102,7 +102,8 @@ Av844q/BfRuVsJsK1NDNG09LC30B0l3LKBqlrRmRTUMHtgchdX2dY+p7GPOoSzlR | ||||||
| MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg== | MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg== | ||||||
| =i9b7 | =i9b7 | ||||||
| -----END PGP PUBLIC KEY BLOCK-----` | -----END PGP PUBLIC KEY BLOCK-----` | ||||||
| 	ekey, err := checkArmoredGPGKeyString(testGPGArmor) | 	keys, err := checkArmoredGPGKeyString(testGPGArmor) | ||||||
|  | 	ekey := keys[0] | ||||||
| 	assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey) | 	assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey) | ||||||
| 
 | 
 | ||||||
| 	pubkey := ekey.PrimaryKey | 	pubkey := ekey.PrimaryKey | ||||||
|  | @ -219,9 +220,9 @@ Q0KHb+QcycSgbDx0ZAvdIacuKvBBcbxrsmFUI4LR+oIup0G9gUc0roPvr014jYQL | ||||||
| =zHo9 | =zHo9 | ||||||
| -----END PGP PUBLIC KEY BLOCK-----` | -----END PGP PUBLIC KEY BLOCK-----` | ||||||
| 
 | 
 | ||||||
| 	key, err := AddGPGKey(1, testEmailWithUpperCaseLetters) | 	keys, err := AddGPGKey(1, testEmailWithUpperCaseLetters) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 	key := keys[0] | ||||||
| 	if assert.Len(t, key.Emails, 1) { | 	if assert.Len(t, key.Emails, 1) { | ||||||
| 		assert.Equal(t, "user1@example.com", key.Emails[0].Email) | 		assert.Equal(t, "user1@example.com", key.Emails[0].Email) | ||||||
| 	} | 	} | ||||||
|  | @ -371,8 +372,9 @@ epiDVQ== | ||||||
| =VSKJ | =VSKJ | ||||||
| -----END PGP PUBLIC KEY BLOCK----- | -----END PGP PUBLIC KEY BLOCK----- | ||||||
| ` | ` | ||||||
| 	ekey, err := checkArmoredGPGKeyString(testIssue6599) | 	keys, err := checkArmoredGPGKeyString(testIssue6599) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  | 	ekey := keys[0] | ||||||
| 	expire := getExpiryTime(ekey) | 	expire := getExpiryTime(ekey) | ||||||
| 	assert.Equal(t, time.Unix(1586105389, 0), expire) | 	assert.Equal(t, time.Unix(1586105389, 0), expire) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -118,12 +118,12 @@ func GetGPGKey(ctx *context.APIContext) { | ||||||
| 
 | 
 | ||||||
| // CreateUserGPGKey creates new GPG key to given user by ID.
 | // CreateUserGPGKey creates new GPG key to given user by ID.
 | ||||||
| func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) { | func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) { | ||||||
| 	key, err := models.AddGPGKey(uid, form.ArmoredKey) | 	keys, err := models.AddGPGKey(uid, form.ArmoredKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		HandleAddGPGKeyError(ctx, err) | 		HandleAddGPGKeyError(ctx, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.JSON(http.StatusCreated, convert.ToGPGKey(key)) | 	ctx.JSON(http.StatusCreated, convert.ToGPGKey(keys[0])) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // swagger:parameters userCurrentPostGPGKey
 | // swagger:parameters userCurrentPostGPGKey
 | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) { | ||||||
| 	} | 	} | ||||||
| 	switch form.Type { | 	switch form.Type { | ||||||
| 	case "gpg": | 	case "gpg": | ||||||
| 		key, err := models.AddGPGKey(ctx.User.ID, form.Content) | 		keys, err := models.AddGPGKey(ctx.User.ID, form.Content) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.Data["HasGPGError"] = true | 			ctx.Data["HasGPGError"] = true | ||||||
| 			switch { | 			switch { | ||||||
|  | @ -63,7 +63,15 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) { | ||||||
| 			} | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", key.KeyID)) | 		keyIDs := "" | ||||||
|  | 		for _, key := range keys { | ||||||
|  | 			keyIDs += key.KeyID | ||||||
|  | 			keyIDs += ", " | ||||||
|  | 		} | ||||||
|  | 		if len(keyIDs) > 0 { | ||||||
|  | 			keyIDs = keyIDs[:len(keyIDs)-2] | ||||||
|  | 		} | ||||||
|  | 		ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", keyIDs)) | ||||||
| 		ctx.Redirect(setting.AppSubURL + "/user/settings/keys") | 		ctx.Redirect(setting.AppSubURL + "/user/settings/keys") | ||||||
| 	case "ssh": | 	case "ssh": | ||||||
| 		content, err := models.CheckPublicKeyString(form.Content) | 		content, err := models.CheckPublicKeyString(form.Content) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue