Hash App token (#6724)
This commit is contained in:
		
							parent
							
								
									1fa9662946
								
							
						
					
					
						commit
						46373e7657
					
				
					 16 changed files with 239 additions and 44 deletions
				
			
		
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							|  | @ -3,7 +3,7 @@ module code.gitea.io/gitea | ||||||
| go 1.12 | go 1.12 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	code.gitea.io/sdk v0.0.0-20190416172854-7d954d775498 | 	code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 | ||||||
| 	github.com/BurntSushi/toml v0.3.1 // indirect | 	github.com/BurntSushi/toml v0.3.1 // indirect | ||||||
| 	github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 | 	github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 | ||||||
| 	github.com/RoaringBitmap/roaring v0.4.7 // indirect | 	github.com/RoaringBitmap/roaring v0.4.7 // indirect | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							|  | @ -1,6 +1,6 @@ | ||||||
| cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||||
| code.gitea.io/sdk v0.0.0-20190416172854-7d954d775498 h1:rcjwXMYIjYts88akPiyy/GB+imecpf159jojChciEEw= | code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 h1:YggbbCVgggcOjKYmcB2wVOsEtJHgHUNFFJZDB6QcYTg= | ||||||
| code.gitea.io/sdk v0.0.0-20190416172854-7d954d775498/go.mod h1:5bZt0dRznpn2JysytQnV0yCru3FwDv9O5G91jo+lDAk= | code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7/go.mod h1:5bZt0dRznpn2JysytQnV0yCru3FwDv9O5G91jo+lDAk= | ||||||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8= | github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34 h1:UsHpWO0Elp6NaWVARdZHjiYwkhrspHVEGsyIKPb9OI8= | ||||||
|  |  | ||||||
|  | @ -26,10 +26,10 @@ func TestAPICreateAndDeleteToken(t *testing.T) { | ||||||
| 	var newAccessToken api.AccessToken | 	var newAccessToken api.AccessToken | ||||||
| 	DecodeJSON(t, resp, &newAccessToken) | 	DecodeJSON(t, resp, &newAccessToken) | ||||||
| 	models.AssertExistsAndLoadBean(t, &models.AccessToken{ | 	models.AssertExistsAndLoadBean(t, &models.AccessToken{ | ||||||
| 		ID:   newAccessToken.ID, | 		ID:    newAccessToken.ID, | ||||||
| 		Name: newAccessToken.Name, | 		Name:  newAccessToken.Name, | ||||||
| 		Sha1: newAccessToken.Sha1, | 		Token: newAccessToken.Token, | ||||||
| 		UID:  user.ID, | 		UID:   user.ID, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID) | 	req = NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", newAccessToken.ID) | ||||||
|  |  | ||||||
|  | @ -507,7 +507,7 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string { | ||||||
| 
 | 
 | ||||||
| // ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
 | // ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
 | ||||||
| type ErrAccessTokenNotExist struct { | type ErrAccessTokenNotExist struct { | ||||||
| 	SHA string | 	Token string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
 | // IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
 | ||||||
|  | @ -517,7 +517,7 @@ func IsErrAccessTokenNotExist(err error) bool { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (err ErrAccessTokenNotExist) Error() string { | func (err ErrAccessTokenNotExist) Error() string { | ||||||
| 	return fmt.Sprintf("access token does not exist [sha: %s]", err.SHA) | 	return fmt.Sprintf("access token does not exist [sha: %s]", err.Token) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
 | // ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,10 @@ | ||||||
|   id: 1 |   id: 1 | ||||||
|   uid: 1 |   uid: 1 | ||||||
|   name: Token A |   name: Token A | ||||||
|   sha1: hash1 |   #token: d2c6c1ba3890b309189a8e618c72a162e4efbf36 | ||||||
|  |   token_hash: 2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f | ||||||
|  |   token_salt: QuSiZr1byZ | ||||||
|  |   token_last_eight: e4efbf36 | ||||||
|   created_unix: 946687980 |   created_unix: 946687980 | ||||||
|   updated_unix: 946687980 |   updated_unix: 946687980 | ||||||
| 
 | 
 | ||||||
|  | @ -10,7 +13,10 @@ | ||||||
|   id: 2 |   id: 2 | ||||||
|   uid: 1 |   uid: 1 | ||||||
|   name: Token B |   name: Token B | ||||||
|   sha1: hash2 |   #token: 4c6f36e6cf498e2a448662f915d932c09c5a146c | ||||||
|  |   token_hash: 1a0e32a231ebbd582dc626c1543a42d3c63d4fa76c07c72862721467c55e8f81c923d60700f0528b5f5f443f055559d3a279 | ||||||
|  |   token_salt: Lfwopukrq5 | ||||||
|  |   token_last_eight: 9c5a146c | ||||||
|   created_unix: 946687980 |   created_unix: 946687980 | ||||||
|   updated_unix: 946687980 |   updated_unix: 946687980 | ||||||
| 
 | 
 | ||||||
|  | @ -18,6 +24,10 @@ | ||||||
|   id: 3 |   id: 3 | ||||||
|   uid: 2 |   uid: 2 | ||||||
|   name: Token A |   name: Token A | ||||||
|   sha1: hash3 |   #token: 90a18faa671dc43924b795806ffe4fd169d28c91 | ||||||
|  |   token_hash: d6d404048048812d9e911d93aefbe94fc768d4876fdf75e3bef0bdc67828e0af422846d3056f2f25ec35c51dc92075685ec5 | ||||||
|  |   token_salt: 99ArgXKlQQ | ||||||
|  |   token_last_eight: 69d28c91 | ||||||
|   created_unix: 946687980 |   created_unix: 946687980 | ||||||
|   updated_unix: 946687980 |   updated_unix: 946687980 | ||||||
|  | #commented out tokens so you can see what they are in plaintext | ||||||
|  | @ -223,6 +223,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("add uploader id for table attachment", addUploaderIDForAttachment), | 	NewMigration("add uploader id for table attachment", addUploaderIDForAttachment), | ||||||
| 	// v84 -> v85
 | 	// v84 -> v85
 | ||||||
| 	NewMigration("add table to store original imported gpg keys", addGPGKeyImport), | 	NewMigration("add table to store original imported gpg keys", addGPGKeyImport), | ||||||
|  | 	// v85 -> v86
 | ||||||
|  | 	NewMigration("hash application token", hashAppToken), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Migrate database to current version
 | // Migrate database to current version
 | ||||||
|  |  | ||||||
							
								
								
									
										135
									
								
								models/migrations/v85.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								models/migrations/v85.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,135 @@ | ||||||
|  | // 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 ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-xorm/core" | ||||||
|  | 	"github.com/go-xorm/xorm" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/modules/generate" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func hashAppToken(x *xorm.Engine) error { | ||||||
|  | 	// AccessToken see models/token.go
 | ||||||
|  | 	type AccessToken struct { | ||||||
|  | 		ID             int64 `xorm:"pk autoincr"` | ||||||
|  | 		UID            int64 `xorm:"INDEX"` | ||||||
|  | 		Name           string | ||||||
|  | 		Sha1           string | ||||||
|  | 		Token          string `xorm:"-"` | ||||||
|  | 		TokenHash      string `xorm:"UNIQUE"` // sha256 of token
 | ||||||
|  | 		TokenSalt      string | ||||||
|  | 		TokenLastEight string `xorm:"token_last_eight"` | ||||||
|  | 
 | ||||||
|  | 		CreatedUnix       util.TimeStamp `xorm:"INDEX created"` | ||||||
|  | 		UpdatedUnix       util.TimeStamp `xorm:"INDEX updated"` | ||||||
|  | 		HasRecentActivity bool           `xorm:"-"` | ||||||
|  | 		HasUsed           bool           `xorm:"-"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// First remove the index
 | ||||||
|  | 	sess := x.NewSession() | ||||||
|  | 	defer sess.Close() | ||||||
|  | 	if err := sess.Begin(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var err error | ||||||
|  | 	if models.DbCfg.Type == core.POSTGRES || models.DbCfg.Type == core.SQLITE { | ||||||
|  | 		_, err = sess.Exec("DROP INDEX IF EXISTS UQE_access_token_sha1") | ||||||
|  | 	} else if models.DbCfg.Type == core.MSSQL { | ||||||
|  | 		_, err = sess.Exec(`DECLARE @ConstraintName VARCHAR(256) | ||||||
|  | 		DECLARE @SQL NVARCHAR(256) | ||||||
|  | 		SELECT @ConstraintName = obj.name FROM sys.columns col LEFT OUTER JOIN sys.objects obj ON obj.object_id = col.default_object_id AND obj.type = 'D' WHERE col.object_id = OBJECT_ID('access_token') AND obj.name IS NOT NULL AND col.name = 'sha1' | ||||||
|  | 		SET @SQL = N'ALTER TABLE [access_token] DROP CONSTRAINT [' + @ConstraintName + N']' | ||||||
|  | 		EXEC sp_executesql @SQL`) | ||||||
|  | 	} else if models.DbCfg.Type == core.MYSQL { | ||||||
|  | 		indexes, err := sess.QueryString(`SHOW INDEX FROM access_token WHERE KEY_NAME = 'UQE_access_token_sha1'`) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if len(indexes) >= 1 { | ||||||
|  | 			_, err = sess.Exec("DROP INDEX UQE_access_token_sha1 ON access_token") | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		_, err = sess.Exec("DROP INDEX UQE_access_token_sha1 ON access_token") | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("Drop index failed: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = sess.Commit(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := sess.Begin(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := x.Sync2(new(AccessToken)); err != nil { | ||||||
|  | 		return fmt.Errorf("Sync2: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = sess.Commit(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := sess.Begin(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// transform all tokens to hashes
 | ||||||
|  | 	const batchSize = 100 | ||||||
|  | 	for start := 0; ; start += batchSize { | ||||||
|  | 		tokens := make([]*AccessToken, 0, batchSize) | ||||||
|  | 		if err := sess.Limit(batchSize, start).Find(&tokens); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if len(tokens) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, token := range tokens { | ||||||
|  | 			// generate salt
 | ||||||
|  | 			salt, err := generate.GetRandomString(10) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			token.TokenSalt = salt | ||||||
|  | 			token.TokenHash = hashToken(token.Sha1, salt) | ||||||
|  | 			if len(token.Sha1) < 8 { | ||||||
|  | 				log.Warn("Unable to transform token %s with name %s belonging to user ID %d, skipping transformation", token.Sha1, token.Name, token.UID) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			token.TokenLastEight = token.Sha1[len(token.Sha1)-8:] | ||||||
|  | 			token.Sha1 = "" // ensure to blank out column in case drop column doesn't work
 | ||||||
|  | 
 | ||||||
|  | 			if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil { | ||||||
|  | 				return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %v", err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Commit and begin new transaction for dropping columns
 | ||||||
|  | 	if err := sess.Commit(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := sess.Begin(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := dropTableColumns(sess, "access_token", "sha1"); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return sess.Commit() | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -1,24 +1,30 @@ | ||||||
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | // Copyright 2014 The Gogs Authors. All rights reserved.
 | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package models | package models | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/subtle" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	gouuid "github.com/satori/go.uuid" | 	gouuid "github.com/satori/go.uuid" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/generate" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // AccessToken represents a personal access token.
 | // AccessToken represents a personal access token.
 | ||||||
| type AccessToken struct { | type AccessToken struct { | ||||||
| 	ID   int64 `xorm:"pk autoincr"` | 	ID             int64 `xorm:"pk autoincr"` | ||||||
| 	UID  int64 `xorm:"INDEX"` | 	UID            int64 `xorm:"INDEX"` | ||||||
| 	Name string | 	Name           string | ||||||
| 	Sha1 string `xorm:"UNIQUE VARCHAR(40)"` | 	Token          string `xorm:"-"` | ||||||
|  | 	TokenHash      string `xorm:"UNIQUE"` // sha256 of token
 | ||||||
|  | 	TokenSalt      string | ||||||
|  | 	TokenLastEight string `xorm:"token_last_eight"` | ||||||
| 
 | 
 | ||||||
| 	CreatedUnix       util.TimeStamp `xorm:"INDEX created"` | 	CreatedUnix       util.TimeStamp `xorm:"INDEX created"` | ||||||
| 	UpdatedUnix       util.TimeStamp `xorm:"INDEX updated"` | 	UpdatedUnix       util.TimeStamp `xorm:"INDEX updated"` | ||||||
|  | @ -34,24 +40,41 @@ func (t *AccessToken) AfterLoad() { | ||||||
| 
 | 
 | ||||||
| // NewAccessToken creates new access token.
 | // NewAccessToken creates new access token.
 | ||||||
| func NewAccessToken(t *AccessToken) error { | func NewAccessToken(t *AccessToken) error { | ||||||
| 	t.Sha1 = base.EncodeSha1(gouuid.NewV4().String()) | 	salt, err := generate.GetRandomString(10) | ||||||
| 	_, err := x.Insert(t) | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	t.TokenSalt = salt | ||||||
|  | 	t.Token = base.EncodeSha1(gouuid.NewV4().String()) | ||||||
|  | 	t.TokenHash = hashToken(t.Token, t.TokenSalt) | ||||||
|  | 	t.TokenLastEight = t.Token[len(t.Token)-8:] | ||||||
|  | 	_, err = x.Insert(t) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetAccessTokenBySHA returns access token by given sha1.
 | // GetAccessTokenBySHA returns access token by given token value
 | ||||||
| func GetAccessTokenBySHA(sha string) (*AccessToken, error) { | func GetAccessTokenBySHA(token string) (*AccessToken, error) { | ||||||
| 	if sha == "" { | 	if token == "" { | ||||||
| 		return nil, ErrAccessTokenEmpty{} | 		return nil, ErrAccessTokenEmpty{} | ||||||
| 	} | 	} | ||||||
| 	t := &AccessToken{Sha1: sha} | 	if len(token) < 8 { | ||||||
| 	has, err := x.Get(t) | 		return nil, ErrAccessTokenNotExist{token} | ||||||
|  | 	} | ||||||
|  | 	var tokens []AccessToken | ||||||
|  | 	lastEight := token[len(token)-8:] | ||||||
|  | 	err := x.Table(&AccessToken{}).Where("token_last_eight = ?", lastEight).Find(&tokens) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} else if !has { | 	} else if len(tokens) == 0 { | ||||||
| 		return nil, ErrAccessTokenNotExist{sha} | 		return nil, ErrAccessTokenNotExist{token} | ||||||
| 	} | 	} | ||||||
| 	return t, nil | 	for _, t := range tokens { | ||||||
|  | 		tempHash := hashToken(token, t.TokenSalt) | ||||||
|  | 		if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 { | ||||||
|  | 			return &t, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, ErrAccessTokenNotExist{token} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListAccessTokens returns a list of access tokens belongs to given user.
 | // ListAccessTokens returns a list of access tokens belongs to given user.
 | ||||||
|  |  | ||||||
|  | @ -29,11 +29,12 @@ func TestNewAccessToken(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func TestGetAccessTokenBySHA(t *testing.T) { | func TestGetAccessTokenBySHA(t *testing.T) { | ||||||
| 	assert.NoError(t, PrepareTestDatabase()) | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
| 	token, err := GetAccessTokenBySHA("hash1") | 	token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(1), token.UID) | 	assert.Equal(t, int64(1), token.UID) | ||||||
| 	assert.Equal(t, "Token A", token.Name) | 	assert.Equal(t, "Token A", token.Name) | ||||||
| 	assert.Equal(t, "hash1", token.Sha1) | 	assert.Equal(t, "2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f", token.TokenHash) | ||||||
|  | 	assert.Equal(t, "e4efbf36", token.TokenLastEight) | ||||||
| 
 | 
 | ||||||
| 	token, err = GetAccessTokenBySHA("notahash") | 	token, err = GetAccessTokenBySHA("notahash") | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
|  | @ -69,7 +70,7 @@ func TestListAccessTokens(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| func TestUpdateAccessToken(t *testing.T) { | func TestUpdateAccessToken(t *testing.T) { | ||||||
| 	assert.NoError(t, PrepareTestDatabase()) | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
| 	token, err := GetAccessTokenBySHA("hash2") | 	token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	token.Name = "Token Z" | 	token.Name = "Token Z" | ||||||
| 
 | 
 | ||||||
|  | @ -80,7 +81,7 @@ func TestUpdateAccessToken(t *testing.T) { | ||||||
| func TestDeleteAccessTokenByID(t *testing.T) { | func TestDeleteAccessTokenByID(t *testing.T) { | ||||||
| 	assert.NoError(t, PrepareTestDatabase()) | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	token, err := GetAccessTokenBySHA("hash2") | 	token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(1), token.UID) | 	assert.Equal(t, int64(1), token.UID) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import ( | ||||||
| 	"crypto/md5" | 	"crypto/md5" | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| 	"crypto/sha1" | 	"crypto/sha1" | ||||||
|  | 	"crypto/sha256" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"encoding/hex" | 	"encoding/hex" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | @ -54,6 +55,13 @@ func EncodeSha1(str string) string { | ||||||
| 	return hex.EncodeToString(h.Sum(nil)) | 	return hex.EncodeToString(h.Sum(nil)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // EncodeSha256 string to sha1 hex value.
 | ||||||
|  | func EncodeSha256(str string) string { | ||||||
|  | 	h := sha256.New() | ||||||
|  | 	h.Write([]byte(str)) | ||||||
|  | 	return hex.EncodeToString(h.Sum(nil)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ShortSha is basically just truncating.
 | // ShortSha is basically just truncating.
 | ||||||
| // It is DEPRECATED and will be removed in the future.
 | // It is DEPRECATED and will be removed in the future.
 | ||||||
| func ShortSha(sha1 string) string { | func ShortSha(sha1 string) string { | ||||||
|  |  | ||||||
|  | @ -53,6 +53,13 @@ func TestEncodeSha1(t *testing.T) { | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestEncodeSha256(t *testing.T) { | ||||||
|  | 	assert.Equal(t, | ||||||
|  | 		"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2", | ||||||
|  | 		EncodeSha256("foobar"), | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestShortSha(t *testing.T) { | func TestShortSha(t *testing.T) { | ||||||
| 	assert.Equal(t, "veryverylo", ShortSha("veryverylong")) | 	assert.Equal(t, "veryverylo", ShortSha("veryverylong")) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -37,9 +37,9 @@ func ListAccessTokens(ctx *context.APIContext) { | ||||||
| 	apiTokens := make([]*api.AccessToken, len(tokens)) | 	apiTokens := make([]*api.AccessToken, len(tokens)) | ||||||
| 	for i := range tokens { | 	for i := range tokens { | ||||||
| 		apiTokens[i] = &api.AccessToken{ | 		apiTokens[i] = &api.AccessToken{ | ||||||
| 			ID:   tokens[i].ID, | 			ID:             tokens[i].ID, | ||||||
| 			Name: tokens[i].Name, | 			Name:           tokens[i].Name, | ||||||
| 			Sha1: tokens[i].Sha1, | 			TokenLastEight: tokens[i].TokenLastEight, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	ctx.JSON(200, &apiTokens) | 	ctx.JSON(200, &apiTokens) | ||||||
|  | @ -81,9 +81,9 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.JSON(201, &api.AccessToken{ | 	ctx.JSON(201, &api.AccessToken{ | ||||||
| 		Name: t.Name, | 		Name:  t.Name, | ||||||
| 		Sha1: t.Sha1, | 		Token: t.Token, | ||||||
| 		ID:   t.ID, | 		ID:    t.ID, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -49,7 +49,7 @@ func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx.Flash.Success(ctx.Tr("settings.generate_token_success")) | 	ctx.Flash.Success(ctx.Tr("settings.generate_token_success")) | ||||||
| 	ctx.Flash.Info(t.Sha1) | 	ctx.Flash.Info(t.Token) | ||||||
| 
 | 
 | ||||||
| 	ctx.Redirect(setting.AppSubURL + "/user/settings/applications") | 	ctx.Redirect(setting.AppSubURL + "/user/settings/applications") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9458,8 +9458,11 @@ | ||||||
|   }, |   }, | ||||||
|   "responses": { |   "responses": { | ||||||
|     "AccessToken": { |     "AccessToken": { | ||||||
|       "description": "AccessToken represents a API access token.", |       "description": "AccessToken represents an API access token.", | ||||||
|       "headers": { |       "headers": { | ||||||
|  |         "hashed_token": { | ||||||
|  |           "type": "string" | ||||||
|  |         }, | ||||||
|         "id": { |         "id": { | ||||||
|           "type": "integer", |           "type": "integer", | ||||||
|           "format": "int64" |           "format": "int64" | ||||||
|  | @ -9467,7 +9470,10 @@ | ||||||
|         "name": { |         "name": { | ||||||
|           "type": "string" |           "type": "string" | ||||||
|         }, |         }, | ||||||
|         "sha1": { |         "token": { | ||||||
|  |           "type": "string" | ||||||
|  |         }, | ||||||
|  |         "token_last_eight": { | ||||||
|           "type": "string" |           "type": "string" | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								vendor/code.gitea.io/sdk/gitea/user_app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/code.gitea.io/sdk/gitea/user_app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,4 +1,5 @@ | ||||||
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | // Copyright 2014 The Gogs Authors. All rights reserved.
 | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
|  | @ -17,12 +18,14 @@ func BasicAuthEncode(user, pass string) string { | ||||||
| 	return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) | 	return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AccessToken represents a API access token.
 | // AccessToken represents an API access token.
 | ||||||
| // swagger:response AccessToken
 | // swagger:response AccessToken
 | ||||||
| type AccessToken struct { | type AccessToken struct { | ||||||
| 	ID   int64  `json:"id"` | 	ID             int64  `json:"id"` | ||||||
| 	Name string `json:"name"` | 	Name           string `json:"name"` | ||||||
| 	Sha1 string `json:"sha1"` | 	Token          string `json:"token"` | ||||||
|  | 	HashedToken    string `json:"hashed_token"` | ||||||
|  | 	TokenLastEight string `json:"token_last_eight"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AccessTokenList represents a list of API access token.
 | // AccessTokenList represents a list of API access token.
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							|  | @ -1,4 +1,4 @@ | ||||||
| # code.gitea.io/sdk v0.0.0-20190416172854-7d954d775498 | # code.gitea.io/sdk v0.0.0-20190419065346-2858b80da5f7 | ||||||
| code.gitea.io/sdk/gitea | code.gitea.io/sdk/gitea | ||||||
| # github.com/BurntSushi/toml v0.3.1 | # github.com/BurntSushi/toml v0.3.1 | ||||||
| github.com/BurntSushi/toml | github.com/BurntSushi/toml | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue