Compare commits

...

24 Commits

Author SHA1 Message Date
Lauris BH
3022681432 Changelog for 1.4.0-rc3 (#3679) 2018-03-16 22:11:34 +02:00
Lauris BH
c0e0fb7d39 Refactor and simplify redirect to url (#3674) (#3676) 2018-03-16 19:59:47 +08:00
Lauris BH
0c612124f9 Update markbates/goth libary to fix OAuth2 support (#3661) (#3663) 2018-03-13 09:08:26 +02:00
Lauris BH
5d0c9872a9 Fix MySQL and PostgreSQL column drop SQL (#3649) (#3651) 2018-03-10 10:27:19 +08:00
Lauris BH
92a3061753 Fix column removal in MSSQL (#3638) (#3640)
* Fix column removal in MSSQL

* Use xorm session in MSSQL drop column operations

* Add transaction as MSSQL alter table is transactional
2018-03-07 11:59:20 +02:00
Lauris BH
efc5a7171b Fix wiki inter-links with spaces (#3560) (#3632) 2018-03-06 16:43:50 +08:00
Lauris BH
93f34fd8a2 Changelog for release 1.4.0-rc2 (#3610) 2018-03-03 00:10:37 +02:00
Wendell Sun
dd784396ce Fix query protected branch bug (#3563) (#3571)
Signed-off-by: Wendell Sun <iwendellsun@gmail.com>
2018-02-24 00:07:25 +02:00
Wendell Sun
4a0ce6896b Fix remove team member issue (#3566) (#3570)
Signed-off-by: Wendell Sun <iwendellsun@gmail.com>
2018-02-23 20:02:45 +02:00
Wendell Sun
d87fb0a6fd Fix the protected branch panic issue (#3567) (#3569)
Signed-off-by: Wendell Sun <iwendellsun@gmail.com>
2018-02-23 19:27:34 +02:00
Bo-Yi Wu
e55eaa8545 if Mirrors repo no content is fetched, updated time should not be changed #3551 (#3565)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2018-02-23 13:48:40 +02:00
Wendell Sun
423c642fdb Bug fix for repo releases sorted (#3522) (#3555)
Signed-off-by: Wendell Sun <iwendellsun@gmail.com>

Use TimeStampNow function
2018-02-22 09:56:38 +08:00
Bo-Yi Wu
ed2ba84525 refactor: reduce sql query in retrieveFeeds (#3554) 2018-02-21 18:15:00 +02:00
Lauris BH
e8015a59bb Add issue closed time column to fix activity closed issues list (#3537) (#3540)
Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
2018-02-19 21:51:37 +02:00
Lauris BH
8327300809 Update markbates/goth library (#3533) (#3539)
Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
2018-02-19 20:10:38 +08:00
Lauris BH
ade183957d Force remove test repo root path in case previous test is still locking it (#3528) (#3536)
Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
2018-02-19 08:29:37 +02:00
Lauris BH
c503eac206 Fix escaping changed title in comments (#3530) (#3534)
* Fix escaping changed title in comments

* Fix escaping of wiki page titile

Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
2018-02-19 10:33:51 +08:00
Jonas Bröms
221e502297 Clarify Indexer MAX_FILE_SIZE (#3469) (#3474) 2018-02-15 11:20:41 +02:00
Codruț Constantin Gușoi
1c3d712fe7 Fixes missing avatars in offline mode (#3471) (#3477)
Signed-off-by: Codruț Constantin Gușoi <codrut.gusoi@gmail.com>
2018-02-14 23:37:35 +02:00
Ethan Koenig
85f90187f3 Improve wiki test (#3493) (#3511) 2018-02-14 22:40:59 +02:00
Jonas Franz
c0675ef6c2 Escape search query (Backport 1.4) (#3488)
* Escape search query

Signed-off-by: Jonas Franz <info@jonasfranz.de>

(cherry picked from commit 2970889)

* Reordered imports

Signed-off-by: Jonas Franz <info@jonasfranz.de>
2018-02-11 21:25:02 +02:00
Ethan Koenig
4e27cc4813 Fix synchronization bug in repo indexer (#3455) (#3461) 2018-02-08 08:42:45 +02:00
Lauris BH
f61ef28f26 Fix rendering of wiki page list if wiki repo contains other files (#3454) (#3463)
* Fix rendering of wiki page list if wiki repo contains other files

* Improve wiki filename tests
2018-02-06 07:59:44 +02:00
Lauris Bukšis-Haberkorns
b27e10d6e4 Change 1.4.0-rc1 version release date
Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
2018-02-01 10:22:24 +02:00
49 changed files with 906 additions and 375 deletions

View File

@@ -4,7 +4,31 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.4.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.4.0-rc1) - 2018-01-31
## [1.4.0-rc3](https://github.com/go-gitea/gitea/releases/tag/v1.4.0-rc3) - 2018-03-16
* SECURITY
* Refactor and simplify to correctly validate redirect to URL (#3674) (#3676)
* BUGFIXES
* Update markbates/goth library to fix OAuth2 support (#3661) (#3663)
* Fix column removal in MSSQL (#3638) (#3640)
* Fix wiki inter-links with spaces (#3560) (#3632)
## [1.4.0-rc2](https://github.com/go-gitea/gitea/releases/tag/v1.4.0-rc2) - 2018-03-02
* SECURITY
* Fix escaping changed title in comments (#3530) (#3534)
 * Escape search query (#3486) (#3488)
* BUGFIXES
* Fix query protected branch bug (#3563) (#3571)
* Fix remove team member issue (#3566) (#3570)
* Fix the protected branch panic issue (#3567) (#3569)
* If Mirrors repository no content is fetched, updated time should not be changed (#3551) (#3565)
* Bug fix for mirrored repository releases sorted (#3522) (#3555)
* Add issue closed time column to fix activity closed issues list (#3537) (#3540)
 * Update markbates/goth library to support OAuth2 with new dropbox API (#3533) (#3539)
 * Fixes missing avatars in offline mode (#3471) (#3477)
 * Fix synchronization bug in repo indexer (#3455) (#3461)
 * Fix rendering of wiki page list if wiki repo contains other files (#3454) (#3463)
## [1.4.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.4.0-rc1) - 2018-02-01
* BREAKING
* Drop deprecated GOGS\_WORK\_DIR use (#2946)
* Fix API status code for hook creation (#2814)

View File

@@ -128,7 +128,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `REPO_INDEXER_ENABLED`: **false**: Enables code search (uses a lot of disk space).
- `REPO_INDEXER_PATH`: **indexers/repos.bleve**: Index file used for code search.
- `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request.
- `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of each index files.
- `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of files to be indexed.
## Security (`security`)

View File

@@ -14,7 +14,7 @@ import (
"github.com/stretchr/testify/assert"
)
func testPullCreate(t *testing.T, session *TestSession, user, repo, branch string) *httptest.ResponseRecorder {
func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, title string) *httptest.ResponseRecorder {
req := NewRequest(t, "GET", path.Join(user, repo))
resp := session.MakeRequest(t, req, http.StatusOK)
@@ -35,7 +35,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"title": "This is a pull title",
"title": title,
})
resp = session.MakeRequest(t, req, http.StatusFound)
@@ -47,7 +47,7 @@ func TestPullCreate(t *testing.T) {
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
// check the redirected URL
url := resp.HeaderMap.Get("Location")
@@ -68,3 +68,38 @@ func TestPullCreate(t *testing.T) {
assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body)
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
}
func TestPullCreate_TitleEscape(t *testing.T) {
prepareTestEnv(t)
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master", "<i>XSS PR</i>")
// check the redirected URL
url := resp.HeaderMap.Get("Location")
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
// Edit title
req := NewRequest(t, "GET", url)
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url")
assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"title": "<u>XSS PR</u>",
})
session.MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", url)
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html()
assert.NoError(t, err)
assert.Equal(t, "&lt;i&gt;XSS PR&lt;/i&gt;", titleHTML)
titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html()
assert.NoError(t, err)
assert.Equal(t, "&lt;u&gt;XSS PR&lt;/u&gt;", titleHTML)
}

View File

@@ -56,7 +56,7 @@ func TestPullMerge(t *testing.T) {
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
@@ -69,7 +69,7 @@ func TestPullRebase(t *testing.T) {
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
@@ -83,7 +83,7 @@ func TestPullSquash(t *testing.T) {
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
@@ -96,7 +96,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "feature/test")
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])

View File

@@ -22,16 +22,16 @@ func TestRepoActivity(t *testing.T) {
// Create PRs (1 merged & 2 proposed)
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
testPullCreate(t, session, "user1", "repo1", "feat/better_readme")
testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n")
testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme")
testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title")
// Create issues (3 new issues)
testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1")

View File

@@ -742,5 +742,14 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
}
actions := make([]*Action, 0, 20)
return actions, x.Limit(20).Desc("id").Where(cond).Find(&actions)
if err := x.Limit(20).Desc("id").Where(cond).Find(&actions); err != nil {
return nil, fmt.Errorf("Find: %v", err)
}
if err := ActionList(actions).LoadAttributes(); err != nil {
return nil, fmt.Errorf("LoadAttributes: %v", err)
}
return actions, nil
}

98
models/action_list.go Normal file
View File

@@ -0,0 +1,98 @@
// Copyright 2018 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 models
import "fmt"
// ActionList defines a list of actions
type ActionList []*Action
func (actions ActionList) getUserIDs() []int64 {
userIDs := make(map[int64]struct{}, len(actions))
for _, action := range actions {
if _, ok := userIDs[action.ActUserID]; !ok {
userIDs[action.ActUserID] = struct{}{}
}
}
return keysInt64(userIDs)
}
func (actions ActionList) loadUsers(e Engine) ([]*User, error) {
if len(actions) == 0 {
return nil, nil
}
userIDs := actions.getUserIDs()
userMaps := make(map[int64]*User, len(userIDs))
err := e.
In("id", userIDs).
Find(&userMaps)
if err != nil {
return nil, fmt.Errorf("find user: %v", err)
}
for _, action := range actions {
action.ActUser = userMaps[action.ActUserID]
}
return valuesUser(userMaps), nil
}
// LoadUsers loads actions' all users
func (actions ActionList) LoadUsers() ([]*User, error) {
return actions.loadUsers(x)
}
func (actions ActionList) getRepoIDs() []int64 {
repoIDs := make(map[int64]struct{}, len(actions))
for _, action := range actions {
if _, ok := repoIDs[action.RepoID]; !ok {
repoIDs[action.RepoID] = struct{}{}
}
}
return keysInt64(repoIDs)
}
func (actions ActionList) loadRepositories(e Engine) ([]*Repository, error) {
if len(actions) == 0 {
return nil, nil
}
repoIDs := actions.getRepoIDs()
repoMaps := make(map[int64]*Repository, len(repoIDs))
err := e.
In("id", repoIDs).
Find(&repoMaps)
if err != nil {
return nil, fmt.Errorf("find repository: %v", err)
}
for _, action := range actions {
action.Repo = repoMaps[action.RepoID]
}
return valuesRepository(repoMaps), nil
}
// LoadRepositories loads actions' all repositories
func (actions ActionList) LoadRepositories() ([]*Repository, error) {
return actions.loadRepositories(x)
}
// loadAttributes loads all attributes
func (actions ActionList) loadAttributes(e Engine) (err error) {
if _, err = actions.loadUsers(e); err != nil {
return
}
if _, err = actions.loadRepositories(e); err != nil {
return
}
return nil
}
// LoadAttributes loads attributes of the actions
func (actions ActionList) LoadAttributes() error {
return actions.loadAttributes(x)
}

View File

@@ -6,7 +6,6 @@ package models
import (
"fmt"
"strings"
"time"
"code.gitea.io/gitea/modules/base"
@@ -70,7 +69,7 @@ func GetProtectedBranchByRepoID(RepoID int64) ([]*ProtectedBranch, error) {
// GetProtectedBranchBy getting protected branch by ID/Name
func GetProtectedBranchBy(repoID int64, BranchName string) (*ProtectedBranch, error) {
rel := &ProtectedBranch{RepoID: repoID, BranchName: strings.ToLower(BranchName)}
rel := &ProtectedBranch{RepoID: repoID, BranchName: BranchName}
has, err := x.Get(rel)
if err != nil {
return nil, err
@@ -156,6 +155,10 @@ func (repo *Repository) GetProtectedBranches() ([]*ProtectedBranch, error) {
// IsProtectedBranch checks if branch is protected
func (repo *Repository) IsProtectedBranch(branchName string, doer *User) (bool, error) {
if doer == nil {
return true, nil
}
protectedBranch := &ProtectedBranch{
RepoID: repo.ID,
BranchName: branchName,

View File

@@ -216,6 +216,21 @@ func (err ErrWikiReservedName) Error() string {
return fmt.Sprintf("wiki title is reserved: %s", err.Title)
}
// ErrWikiInvalidFileName represents an invalid wiki file name.
type ErrWikiInvalidFileName struct {
FileName string
}
// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
func IsErrWikiInvalidFileName(err error) bool {
_, ok := err.(ErrWikiInvalidFileName)
return ok
}
func (err ErrWikiInvalidFileName) Error() string {
return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
}
// __________ ___. .__ .__ ____ __.
// \______ \__ _\_ |__ | | |__| ____ | |/ _|____ ___.__.
// | ___/ | \ __ \| | | |/ ___\ | <_/ __ < | |

View File

@@ -49,6 +49,7 @@ type Issue struct {
DeadlineUnix util.TimeStamp `xorm:"INDEX"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
ClosedUnix util.TimeStamp `xorm:"INDEX"`
Attachments []*Attachment `xorm:"-"`
Comments []*Comment `xorm:"-"`
@@ -612,8 +613,13 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository,
return nil
}
issue.IsClosed = isClosed
if isClosed {
issue.ClosedUnix = util.TimeStampNow()
} else {
issue.ClosedUnix = 0
}
if err = updateIssueCols(e, issue, "is_closed"); err != nil {
if err = updateIssueCols(e, issue, "is_closed", "closed_unix"); err != nil {
return err
}

View File

@@ -214,13 +214,15 @@ func TestChangeMilestoneIssueStats(t *testing.T) {
"is_closed=0").(*Issue)
issue.IsClosed = true
_, err := x.Cols("is_closed").Update(issue)
issue.ClosedUnix = util.TimeStampNow()
_, err := x.Cols("is_closed", "closed_unix").Update(issue)
assert.NoError(t, err)
assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
CheckConsistencyFor(t, &Milestone{})
issue.IsClosed = false
_, err = x.Cols("is_closed").Update(issue)
issue.ClosedUnix = 0
_, err = x.Cols("is_closed", "closed_unix").Update(issue)
assert.NoError(t, err)
assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
CheckConsistencyFor(t, &Milestone{})

View File

@@ -166,6 +166,8 @@ var migrations = []Migration{
NewMigration("add writable deploy keys", addModeToDeploKeys),
// v56 -> v57
NewMigration("remove is_owner, num_teams columns from org_user", removeIsOwnerColumnFromOrgUser),
// v57 -> v58
NewMigration("add closed_unix column for issues", addIssueClosedTime),
}
// Migrate database to current version
@@ -215,6 +217,66 @@ Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to curr
return nil
}
func dropTableColumns(x *xorm.Engine, tableName string, columnNames ...string) (err error) {
if tableName == "" || len(columnNames) == 0 {
return nil
}
switch {
case setting.UseSQLite3:
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UseTiDB, setting.UsePostgreSQL:
cols := ""
for _, col := range columnNames {
if cols != "" {
cols += ", "
}
cols += "DROP COLUMN `" + col + "`"
}
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
}
case setting.UseMSSQL:
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
cols := ""
for _, col := range columnNames {
if cols != "" {
cols += ", "
}
cols += "`" + strings.ToLower(col) + "`"
}
sql := fmt.Sprintf("SELECT Name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('%[1]s') AND PARENT_COLUMN_ID IN (SELECT column_id FROM sys.columns WHERE lower(NAME) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
tableName, strings.Replace(cols, "`", "'", -1))
constraints := make([]string, 0)
if err := sess.SQL(sql).Find(&constraints); err != nil {
sess.Rollback()
return fmt.Errorf("Find constraints: %v", err)
}
for _, constraint := range constraints {
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT `%s`", tableName, constraint)); err != nil {
sess.Rollback()
return fmt.Errorf("Drop table `%s` constraint `%s`: %v", tableName, constraint, err)
}
}
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s", tableName, cols)); err != nil {
sess.Rollback()
return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
}
return sess.Commit()
default:
log.Fatal(4, "Unrecognized DB")
}
return nil
}
func fixLocaleFileLoadPanic(_ *xorm.Engine) error {
cfg, err := ini.Load(setting.CustomConf)
if err != nil {

View File

@@ -5,29 +5,9 @@
package migrations
import (
"fmt"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm"
)
func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) {
switch {
case setting.UseSQLite3:
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UseTiDB, setting.UsePostgreSQL:
if _, err := x.Exec("ALTER TABLE org_user DROP COLUMN is_owner, DROP COLUMN num_teams"); err != nil {
return fmt.Errorf("DROP COLUMN org_user.is_owner, org_user.num_teams: %v", err)
}
case setting.UseMSSQL:
if _, err := x.Exec("ALTER TABLE org_user DROP COLUMN is_owner, num_teams"); err != nil {
return fmt.Errorf("DROP COLUMN org_user.is_owner, org_user.num_teams: %v", err)
}
default:
log.Fatal(4, "Unrecognized DB")
}
return nil
return dropTableColumns(x, "org_user", "is_owner", "num_teams")
}

30
models/migrations/v57.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright 2017 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"
"code.gitea.io/gitea/modules/util"
"github.com/go-xorm/xorm"
)
func addIssueClosedTime(x *xorm.Engine) error {
// Issue see models/issue.go
type Issue struct {
ClosedUnix util.TimeStamp `xorm:"INDEX"`
}
if err := x.Sync2(new(Issue)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
if _, err := x.Exec("UPDATE `issue` SET `closed_unix` = `updated_unix` WHERE `is_closed` = ?", true); err != nil {
return err
}
return nil
}

View File

@@ -436,8 +436,7 @@ func AddOrgUser(orgID, uid int64) error {
return sess.Commit()
}
// RemoveOrgUser removes user from given organization.
func RemoveOrgUser(orgID, userID int64) error {
func removeOrgUser(sess *xorm.Session, orgID, userID int64) error {
ou := new(OrgUser)
has, err := x.
@@ -473,12 +472,6 @@ func RemoveOrgUser(orgID, userID int64) error {
}
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
@@ -520,6 +513,19 @@ func RemoveOrgUser(orgID, userID int64) error {
}
}
return nil
}
// RemoveOrgUser removes user from given organization.
func RemoveOrgUser(orgID, userID int64) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := removeOrgUser(sess, orgID, userID); err != nil {
return err
}
return sess.Commit()
}

View File

@@ -10,6 +10,8 @@ import (
"strings"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
const ownerTeamName = "Owners"
@@ -521,7 +523,7 @@ func AddTeamMember(team *Team, userID int64) error {
return sess.Commit()
}
func removeTeamMember(e Engine, team *Team, userID int64) error {
func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
isMember, err := isTeamMember(e, team.OrgID, team.ID, userID)
if err != nil || !isMember {
return err
@@ -558,6 +560,16 @@ func removeTeamMember(e Engine, team *Team, userID int64) error {
}
}
// Check if the user is a member of any team in the organization.
if count, err := e.Count(&TeamUser{
UID: userID,
OrgID: team.OrgID,
}); err != nil {
return err
} else if count == 0 {
return removeOrgUser(e, team.OrgID, userID)
}
return nil
}

View File

@@ -36,7 +36,7 @@ type Release struct {
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
IsTag bool `xorm:"NOT NULL DEFAULT false"`
Attachments []*Attachment `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"created INDEX"`
CreatedUnix util.TimeStamp `xorm:"INDEX"`
}
func (r *Release) loadAttributes(e Engine) error {
@@ -134,6 +134,8 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
if err != nil {
return fmt.Errorf("CommitsCount: %v", err)
}
} else {
rel.CreatedUnix = util.TimeStampNow()
}
return nil
}

View File

@@ -176,7 +176,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error {
// Closed issues
sess := issuesForActivityStatement(repoID, fromTime, true, false)
sess.OrderBy("issue.updated_unix DESC")
sess.OrderBy("issue.closed_unix DESC")
stats.ClosedIssues = make(IssueList, 0)
if err = sess.Find(&stats.ClosedIssues); err != nil {
return err
@@ -228,7 +228,11 @@ func issuesForActivityStatement(repoID int64, fromTime time.Time, closed, unreso
if !unresolved {
sess.And("issue.is_pull = ?", false)
sess.And("issue.created_unix >= ?", fromTime.Unix())
if closed {
sess.And("issue.closed_unix >= ?", fromTime.Unix())
} else {
sess.And("issue.created_unix >= ?", fromTime.Unix())
}
} else {
sess.And("issue.created_unix < ?", fromTime.Unix())
sess.And("issue.updated_unix >= ?", fromTime.Unix())

View File

@@ -5,9 +5,7 @@
package models
import (
"io/ioutil"
"os"
"path"
"fmt"
"strconv"
"strings"
@@ -16,8 +14,6 @@ import (
"code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/Unknwon/com"
)
// RepoIndexerStatus status of a repo's entry in the repo indexer
@@ -132,7 +128,11 @@ func populateRepoIndexer(maxRepoID int64) {
}
func updateRepoIndexer(repo *Repository) error {
changes, err := getRepoChanges(repo)
sha, err := getDefaultBranchSha(repo)
if err != nil {
return err
}
changes, err := getRepoChanges(repo, sha)
if err != nil {
return err
} else if changes == nil {
@@ -140,12 +140,12 @@ func updateRepoIndexer(repo *Repository) error {
}
batch := indexer.RepoIndexerBatch()
for _, filename := range changes.UpdatedFiles {
if err := addUpdate(filename, repo, batch); err != nil {
for _, update := range changes.Updates {
if err := addUpdate(update, repo, batch); err != nil {
return err
}
}
for _, filename := range changes.RemovedFiles {
for _, filename := range changes.RemovedFilenames {
if err := addDelete(filename, repo, batch); err != nil {
return err
}
@@ -153,56 +153,61 @@ func updateRepoIndexer(repo *Repository) error {
if err = batch.Flush(); err != nil {
return err
}
return updateLastIndexSync(repo)
return repo.updateIndexerStatus(sha)
}
// repoChanges changes (file additions/updates/removals) to a repo
type repoChanges struct {
UpdatedFiles []string
RemovedFiles []string
Updates []fileUpdate
RemovedFilenames []string
}
type fileUpdate struct {
Filename string
BlobSha string
}
func getDefaultBranchSha(repo *Repository) (string, error) {
stdout, err := git.NewCommand("show-ref", "-s", repo.DefaultBranch).RunInDir(repo.RepoPath())
if err != nil {
return "", err
}
return strings.TrimSpace(stdout), nil
}
// getRepoChanges returns changes to repo since last indexer update
func getRepoChanges(repo *Repository) (*repoChanges, error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
if err := repo.UpdateLocalCopyBranch(""); err != nil {
return nil, err
} else if !git.IsBranchExist(repo.LocalCopyPath(), repo.DefaultBranch) {
// repo does not have any commits yet, so nothing to update
return nil, nil
} else if err = repo.UpdateLocalCopyBranch(repo.DefaultBranch); err != nil {
return nil, err
} else if err = repo.getIndexerStatus(); err != nil {
func getRepoChanges(repo *Repository, revision string) (*repoChanges, error) {
if err := repo.getIndexerStatus(); err != nil {
return nil, err
}
if len(repo.IndexerStatus.CommitSha) == 0 {
return genesisChanges(repo)
return genesisChanges(repo, revision)
}
return nonGenesisChanges(repo)
return nonGenesisChanges(repo, revision)
}
func addUpdate(filename string, repo *Repository, batch *indexer.Batch) error {
filepath := path.Join(repo.LocalCopyPath(), filename)
if stat, err := os.Stat(filepath); err != nil {
func addUpdate(update fileUpdate, repo *Repository, batch *indexer.Batch) error {
stdout, err := git.NewCommand("cat-file", "-s", update.BlobSha).
RunInDir(repo.RepoPath())
if err != nil {
return err
} else if stat.Size() > setting.Indexer.MaxIndexerFileSize {
return nil
} else if stat.IsDir() {
// file could actually be a directory, if it is the root of a submodule.
// We do not index submodule contents, so don't do anything.
}
if size, err := strconv.Atoi(strings.TrimSpace(stdout)); err != nil {
return fmt.Errorf("Misformatted git cat-file output: %v", err)
} else if int64(size) > setting.Indexer.MaxIndexerFileSize {
return nil
}
fileContents, err := ioutil.ReadFile(filepath)
fileContents, err := git.NewCommand("cat-file", "blob", update.BlobSha).
RunInDirBytes(repo.RepoPath())
if err != nil {
return err
} else if !base.IsTextFile(fileContents) {
return nil
}
return batch.Add(indexer.RepoIndexerUpdate{
Filepath: filename,
Filepath: update.Filename,
Op: indexer.RepoIndexerOpUpdate,
Data: &indexer.RepoIndexerData{
RepoID: repo.ID,
@@ -221,42 +226,76 @@ func addDelete(filename string, repo *Repository, batch *indexer.Batch) error {
})
}
// genesisChanges get changes to add repo to the indexer for the first time
func genesisChanges(repo *Repository) (*repoChanges, error) {
var changes repoChanges
stdout, err := git.NewCommand("ls-files").RunInDir(repo.LocalCopyPath())
if err != nil {
return nil, err
}
for _, line := range strings.Split(stdout, "\n") {
filename := strings.TrimSpace(line)
if len(filename) == 0 {
// parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
func parseGitLsTreeOutput(stdout string) ([]fileUpdate, error) {
lines := strings.Split(stdout, "\n")
updates := make([]fileUpdate, 0, len(lines))
for _, line := range lines {
// expect line to be "<mode> <object-type> <object-sha>\t<filename>"
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
} else if filename[0] == '"' {
}
firstSpaceIndex := strings.IndexByte(line, ' ')
if firstSpaceIndex < 0 {
log.Error(4, "Misformatted git ls-tree output: %s", line)
continue
}
tabIndex := strings.IndexByte(line, '\t')
if tabIndex < 42+firstSpaceIndex || tabIndex == len(line)-1 {
log.Error(4, "Misformatted git ls-tree output: %s", line)
continue
}
if objectType := line[firstSpaceIndex+1 : tabIndex-41]; objectType != "blob" {
// submodules appear as commit objects, we do not index submodules
continue
}
blobSha := line[tabIndex-40 : tabIndex]
filename := line[tabIndex+1:]
if filename[0] == '"' {
var err error
filename, err = strconv.Unquote(filename)
if err != nil {
return nil, err
}
}
changes.UpdatedFiles = append(changes.UpdatedFiles, filename)
updates = append(updates, fileUpdate{
Filename: filename,
BlobSha: blobSha,
})
}
return &changes, nil
return updates, nil
}
// genesisChanges get changes to add repo to the indexer for the first time
func genesisChanges(repo *Repository, revision string) (*repoChanges, error) {
var changes repoChanges
stdout, err := git.NewCommand("ls-tree", "--full-tree", "-r", revision).
RunInDir(repo.RepoPath())
if err != nil {
return nil, err
}
changes.Updates, err = parseGitLsTreeOutput(stdout)
return &changes, err
}
// nonGenesisChanges get changes since the previous indexer update
func nonGenesisChanges(repo *Repository) (*repoChanges, error) {
func nonGenesisChanges(repo *Repository, revision string) (*repoChanges, error) {
diffCmd := git.NewCommand("diff", "--name-status",
repo.IndexerStatus.CommitSha, "HEAD")
stdout, err := diffCmd.RunInDir(repo.LocalCopyPath())
repo.IndexerStatus.CommitSha, revision)
stdout, err := diffCmd.RunInDir(repo.RepoPath())
if err != nil {
// previous commit sha may have been removed by a force push, so
// try rebuilding from scratch
log.Warn("git diff: %v", err)
if err = indexer.DeleteRepoFromIndexer(repo.ID); err != nil {
return nil, err
}
return genesisChanges(repo)
return genesisChanges(repo, revision)
}
var changes repoChanges
updatedFilenames := make([]string, 0, 10)
for _, line := range strings.Split(stdout, "\n") {
line = strings.TrimSpace(line)
if len(line) == 0 {
@@ -274,23 +313,22 @@ func nonGenesisChanges(repo *Repository) (*repoChanges, error) {
switch status := line[0]; status {
case 'M', 'A':
changes.UpdatedFiles = append(changes.UpdatedFiles, filename)
updatedFilenames = append(updatedFilenames, filename)
case 'D':
changes.RemovedFiles = append(changes.RemovedFiles, filename)
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
default:
log.Warn("Unrecognized status: %c (line=%s)", status, line)
}
}
return &changes, nil
}
func updateLastIndexSync(repo *Repository) error {
stdout, err := git.NewCommand("rev-parse", "HEAD").RunInDir(repo.LocalCopyPath())
cmd := git.NewCommand("ls-tree", "--full-tree", revision, "--")
cmd.AddArguments(updatedFilenames...)
stdout, err = cmd.RunInDir(repo.RepoPath())
if err != nil {
return err
return nil, err
}
sha := strings.TrimSpace(stdout)
return repo.updateIndexerStatus(sha)
changes.Updates, err = parseGitLsTreeOutput(stdout)
return &changes, err
}
func processRepoIndexerOperationQueue() {

View File

@@ -244,6 +244,8 @@ func MirrorUpdate() {
// SyncMirrors checks and syncs mirrors.
// TODO: sync more mirrors at same time.
func SyncMirrors() {
sess := x.NewSession()
defer sess.Close()
// Start listening on new sync requests.
for repoID := range MirrorQueue.Queue() {
log.Trace("SyncMirrors [repo_id: %v]", repoID)
@@ -260,10 +262,22 @@ func SyncMirrors() {
}
m.ScheduleNextUpdate()
if err = UpdateMirror(m); err != nil {
if err = updateMirror(sess, m); err != nil {
log.Error(4, "UpdateMirror [%s]: %v", repoID, err)
continue
}
// Get latest commit date and update to current repository updated time
commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath())
if err != nil {
log.Error(2, "GetLatestCommitDate [%s]: %v", m.RepoID, err)
continue
}
if _, err = sess.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", commitDate.Unix(), m.RepoID); err != nil {
log.Error(2, "Update repository 'updated_unix' [%s]: %v", m.RepoID, err)
continue
}
}
}

View File

@@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"testing"
"time"
"code.gitea.io/gitea/modules/setting"
@@ -72,6 +73,18 @@ func createTestEngine(fixturesDir string) error {
return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
}
func removeAllWithRetry(dir string) error {
var err error
for i := 0; i < 20; i++ {
err = os.RemoveAll(dir)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
return err
}
// PrepareTestDatabase load test fixtures into test database
func PrepareTestDatabase() error {
return LoadFixtures()
@@ -81,7 +94,7 @@ func PrepareTestDatabase() error {
// by tests that use the above MainTest(..) function.
func PrepareTestEnv(t testing.TB) {
assert.NoError(t, PrepareTestDatabase())
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
assert.NoError(t, removeAllWithRetry(setting.RepoRootPath))
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
assert.NoError(t, com.CopyDir(metaPath, setting.RepoRootPath))
}

View File

@@ -299,7 +299,9 @@ func (u *User) generateRandomAvatar(e Engine) error {
}
// NOTICE for random avatar, it still uses id as avatar name, but custom avatar use md5
// since random image is not a user's photo, there is no security for enumable
u.Avatar = fmt.Sprintf("%d", u.ID)
if u.Avatar == "" {
u.Avatar = fmt.Sprintf("%d", u.ID)
}
if err = os.MkdirAll(filepath.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
return fmt.Errorf("MkdirAll: %v", err)
}

View File

@@ -45,7 +45,7 @@ func WikiNameToFilename(name string) string {
// WikiFilenameToName converts a wiki filename to its corresponding page name.
func WikiFilenameToName(filename string) (string, error) {
if !strings.HasSuffix(filename, ".md") {
return "", fmt.Errorf("Invalid wiki filename: %s", filename)
return "", ErrWikiInvalidFileName{filename}
}
basename := filename[:len(filename)-3]
unescaped, err := url.QueryUnescape(basename)

View File

@@ -77,11 +77,14 @@ func TestWikiFilenameToName(t *testing.T) {
for _, badFilename := range []string{
"nofileextension",
"wrongfileextension.txt",
"badescaping%%.md",
} {
_, err := WikiFilenameToName(badFilename)
assert.Error(t, err)
assert.True(t, IsErrWikiInvalidFileName(err))
}
_, err := WikiFilenameToName("badescaping%%.md")
assert.Error(t, err)
assert.False(t, IsErrWikiInvalidFileName(err))
}
func TestWikiNameToFilenameToName(t *testing.T) {

View File

@@ -9,6 +9,7 @@ import (
"html/template"
"io"
"net/http"
"net/url"
"path"
"strings"
"time"
@@ -75,6 +76,26 @@ func (ctx *Context) HasValue(name string) bool {
return ok
}
// RedirectToFirst redirects to first not empty URL
func (ctx *Context) RedirectToFirst(location ...string) {
for _, loc := range location {
if len(loc) == 0 {
continue
}
u, err := url.Parse(loc)
if err != nil || (u.Scheme != "" && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
continue
}
ctx.Redirect(loc)
return
}
ctx.Redirect(setting.AppSubURL + "/")
return
}
// HTML calls Context.HTML and converts template name to string.
func (ctx *Context) HTML(status int, name base.TplName) {
log.Debug("Template: %s", name)

View File

@@ -391,7 +391,11 @@ func RenderShortLinks(rawBytes []byte, urlPrefix string, noLink bool, isWikiMark
}
absoluteLink := isLink([]byte(link))
if !absoluteLink {
link = strings.Replace(link, " ", "+", -1)
if image {
link = strings.Replace(link, " ", "+", -1)
} else {
link = strings.Replace(link, " ", "-", -1)
}
}
if image {
if !absoluteLink {

View File

@@ -10,6 +10,7 @@ import (
"encoding/json"
"errors"
"fmt"
"html"
"html/template"
"mime"
"net/url"
@@ -179,6 +180,7 @@ func NewFuncMap() []template.FuncMap {
return dict, nil
},
"Printf": fmt.Sprintf,
"Escape": Escape,
}}
}
@@ -197,6 +199,11 @@ func Str2html(raw string) template.HTML {
return template.HTML(markup.Sanitize(raw))
}
// Escape escapes a HTML string
func Escape(raw string) string {
return html.EscapeString(raw)
}
// List traversings the list
func List(l *list.List) chan interface{} {
e := l.Front()

View File

@@ -307,11 +307,7 @@ func Action(ctx *context.Context) {
return
}
redirectTo := ctx.Query("redirect_to")
if len(redirectTo) == 0 {
redirectTo = ctx.Repo.RepoLink
}
ctx.Redirect(redirectTo)
ctx.RedirectToFirst(ctx.Query("redirect_to"), ctx.Repo.RepoLink)
}
// Download download an archive of a repository

View File

@@ -128,6 +128,9 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi
}
wikiName, err := models.WikiFilenameToName(entry.Name())
if err != nil {
if models.IsErrWikiInvalidFileName(err) {
continue
}
ctx.ServerError("WikiFilenameToName", err)
return nil, nil
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
@@ -262,6 +265,9 @@ func WikiPages(ctx *context.Context) {
}
wikiName, err := models.WikiFilenameToName(entry.Name())
if err != nil {
if models.IsErrWikiInvalidFileName(err) {
continue
}
ctx.ServerError("WikiFilenameToName", err)
return
}

View File

@@ -7,36 +7,52 @@ package repo
import (
"io/ioutil"
"net/http"
"path/filepath"
"testing"
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/test"
"github.com/Unknwon/com"
"github.com/stretchr/testify/assert"
)
const content = "Wiki contents for unit tests"
const message = "Wiki commit message for unit tests"
func wikiPath(repo *models.Repository, wikiName string) string {
return filepath.Join(repo.LocalWikiPath(), models.WikiNameToFilename(wikiName))
func wikiEntry(t *testing.T, repo *models.Repository, wikiName string) *git.TreeEntry {
wikiRepo, err := git.OpenRepository(repo.WikiPath())
assert.NoError(t, err)
commit, err := wikiRepo.GetBranchCommit("master")
assert.NoError(t, err)
entries, err := commit.ListEntries()
assert.NoError(t, err)
for _, entry := range entries {
if entry.Name() == models.WikiNameToFilename(wikiName) {
return entry
}
}
return nil
}
func wikiContent(t *testing.T, repo *models.Repository, wikiName string) string {
bytes, err := ioutil.ReadFile(wikiPath(repo, wikiName))
entry := wikiEntry(t, repo, wikiName)
if !assert.NotNil(t, entry) {
return ""
}
reader, err := entry.Blob().Data()
assert.NoError(t, err)
bytes, err := ioutil.ReadAll(reader)
assert.NoError(t, err)
return string(bytes)
}
func assertWikiExists(t *testing.T, repo *models.Repository, wikiName string) {
assert.True(t, com.IsExist(wikiPath(repo, wikiName)))
assert.NotNil(t, wikiEntry(t, repo, wikiName))
}
func assertWikiNotExists(t *testing.T, repo *models.Repository, wikiName string) {
assert.False(t, com.IsExist(wikiPath(repo, wikiName)))
assert.Nil(t, wikiEntry(t, repo, wikiName))
}
func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {

View File

@@ -93,12 +93,8 @@ func checkAutoLogin(ctx *context.Context) bool {
}
if isSucceed {
if len(redirectTo) > 0 {
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
ctx.Redirect(redirectTo)
} else {
ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL))
}
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL))
return true
}
@@ -350,7 +346,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
if obeyRedirect {
ctx.Redirect(redirectTo)
ctx.RedirectToFirst(redirectTo)
}
return
}
@@ -439,7 +435,7 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
ctx.Redirect(redirectTo)
ctx.RedirectToFirst(redirectTo)
return
}

View File

@@ -49,12 +49,8 @@ func SignInOpenID(ctx *context.Context) {
}
if isSucceed {
if len(redirectTo) > 0 {
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
ctx.Redirect(redirectTo)
} else {
ctx.Redirect(setting.AppSubURL + "/")
}
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
ctx.RedirectToFirst(redirectTo)
return
}

View File

@@ -66,12 +66,14 @@ func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
if ctx.User != nil {
userCache[ctx.User.ID] = ctx.User
}
repoCache := map[int64]*models.Repository{}
for _, act := range actions {
// Cache results to reduce queries.
u, ok := userCache[act.ActUserID]
if act.ActUser != nil {
userCache[act.ActUserID] = act.ActUser
}
repoOwner, ok := userCache[act.Repo.OwnerID]
if !ok {
u, err = models.GetUserByID(act.ActUserID)
repoOwner, err = models.GetUserByID(act.Repo.OwnerID)
if err != nil {
if models.IsErrUserNotExist(err) {
continue
@@ -79,35 +81,9 @@ func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
ctx.ServerError("GetUserByID", err)
return
}
userCache[act.ActUserID] = u
userCache[repoOwner.ID] = repoOwner
}
act.ActUser = u
repo, ok := repoCache[act.RepoID]
if !ok {
repo, err = models.GetRepositoryByID(act.RepoID)
if err != nil {
if models.IsErrRepoNotExist(err) {
continue
}
ctx.ServerError("GetRepositoryByID", err)
return
}
}
act.Repo = repo
repoOwner, ok := userCache[repo.OwnerID]
if !ok {
repoOwner, err = models.GetUserByID(repo.OwnerID)
if err != nil {
if models.IsErrUserNotExist(err) {
continue
}
ctx.ServerError("GetUserByID", err)
return
}
}
repo.Owner = repoOwner
act.Repo.Owner = repoOwner
}
ctx.Data["Feeds"] = actions
}
@@ -154,7 +130,8 @@ func Dashboard(ctx *context.Context) {
ctx.Data["MirrorCount"] = len(mirrors)
ctx.Data["Mirrors"] = mirrors
retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
retrieveFeeds(ctx, models.GetFeedsOptions{
RequestedUser: ctxUser,
IncludePrivate: true,
OnlyPerformedBy: false,
IncludeDeleted: false,

View File

@@ -271,9 +271,5 @@ func Action(ctx *context.Context) {
return
}
redirectTo := ctx.Query("redirect_to")
if len(redirectTo) == 0 {
redirectTo = u.HomeLink()
}
ctx.Redirect(redirectTo)
ctx.RedirectToFirst(ctx.Query("redirect_to"), u.HomeLink())
}

View File

@@ -134,7 +134,7 @@
<p class="desc">
<div class="ui red label">{{$.i18n.Tr "repo.activity.closed_issue_label"}}</div>
#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/issues/{{.Index}}">{{.Title}}</a>
{{TimeSinceUnix .UpdatedUnix $.Lang}}
{{TimeSinceUnix .ClosedUnix $.Lang}}
</p>
{{end}}
</div>

View File

@@ -103,7 +103,7 @@
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | Safe}}{{end}}</span>
{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape) $createdStr | Safe}}{{end}}</span>
</div>
{{end}}
{{else if eq .Type 8}}
@@ -113,7 +113,7 @@
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" .OldMilestone.Name .Milestone.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" .OldMilestone.Name $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" .Milestone.Name $createdStr | Safe}}{{end}}</span>
{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}}</span>
</div>
{{else if eq .Type 9}}
<div class="event">
@@ -131,23 +131,23 @@
{{else if eq .Type 10}}
<div class="event">
<span class="octicon octicon-primitive-dot"></span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{$.i18n.Tr "repo.issues.change_title_at" (.OldTitle|Escape) (.NewTitle|Escape) $createdStr | Safe}}
</span>
</div>
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{$.i18n.Tr "repo.issues.change_title_at" .OldTitle .NewTitle $createdStr | Safe}}
</span>
{{else if eq .Type 11}}
<div class="event">
<span class="octicon octicon-primitive-dot"></span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{$.i18n.Tr "repo.issues.delete_branch_at" .CommitSHA $createdStr | Safe}}
</span>
</div>
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
{{$.i18n.Tr "repo.issues.delete_branch_at" .CommitSHA $createdStr | Safe}}
</span>
{{else if eq .Type 12}}
<div class="event">
<span class="octicon octicon-primitive-dot"></span>

View File

@@ -14,7 +14,7 @@
</div>
{{if .Keyword}}
<h3>
{{.i18n.Tr "repo.search.results" .Keyword .RepoLink .RepoName | Str2html}}
{{.i18n.Tr "repo.search.results" (.Keyword|Escape) .RepoLink .RepoName | Str2html }}
</h3>
<div class="repository search">
{{range $result := .SearchResults}}

View File

@@ -104,7 +104,7 @@
{{.i18n.Tr "repo.wiki.delete_page_button"}}
</div>
<div class="content">
<p>{{.i18n.Tr "repo.wiki.delete_page_notice_1" $title | Safe}}</p>
<p>{{.i18n.Tr "repo.wiki.delete_page_notice_1" ($title|Escape) | Safe}}</p>
</div>
{{template "base/delete_modal_actions" .}}
</div>

View File

@@ -8,6 +8,10 @@ protocol providers, as long as they implement the `Provider` and `Session` inter
This package was inspired by [https://github.com/intridea/omniauth](https://github.com/intridea/omniauth).
## Goth Needs a New Maintainer
[https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b](https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b) - TL;DR: I, @markbates, won't be responding to any more issues, PRs, etc... for this package. A new maintainer needs to be found ASAP. Is this you?
## Installation
```text
@@ -18,6 +22,8 @@ $ go get github.com/markbates/goth
* Amazon
* Auth0
* Azure AD
* Battle.net
* Bitbucket
* Box
* Cloud Foundry
@@ -26,6 +32,7 @@ $ go get github.com/markbates/goth
* Digital Ocean
* Discord
* Dropbox
* Eve Online
* Facebook
* Fitbit
* GitHub
@@ -38,6 +45,7 @@ $ go get github.com/markbates/goth
* Lastfm
* Linkedin
* Meetup
* MicrosoftOnline
* OneDrive
* OpenID Connect (auto discovery)
* Paypal
@@ -50,7 +58,9 @@ $ go get github.com/markbates/goth
* Twitch
* Twitter
* Uber
* VK
* Wepay
* Xero
* Yahoo
* Yammer
@@ -71,17 +81,51 @@ $ go get github.com/markbates/goth
```text
$ cd goth/examples
$ go get -v
$ go build
$ go build
$ ./examples
```
Now open up your browser and go to [http://localhost:3000](http://localhost:3000) to see the example.
To actually use the different providers, please make sure you configure them given the system environments as defined in the examples/main.go file
To actually use the different providers, please make sure you set environment variables. Example given in the examples/main.go file
## Security Notes
By default, gothic uses a `CookieStore` from the `gorilla/sessions` package to store session data.
As configured, this default store (`gothic.Store`) will generate cookies with `Options`:
```go
&Options{
Path: "/",
Domain: "",
MaxAge: 86400 * 30,
HttpOnly: true,
Secure: false,
}
```
To tailor these fields for your application, you can override the `gothic.Store` variable at startup.
The follow snippet show one way to do this:
```go
key := "" // Replace with your SESSION_SECRET or similar
maxAge := 86400 * 30 // 30 days
isProd := false // Set to true when serving over https
store := sessions.NewCookieStore([]byte(key))
store.MaxAge(maxAge)
store.Options.Path = "/"
store.Options.HttpOnly = true // HttpOnly should always be enabled
store.Options.Secure = isProd
gothic.Store = store
```
## Issues
Issues always stand a significantly better chance of getting fixed if the are accompanied by a
Issues always stand a significantly better chance of getting fixed if they are accompanied by a
pull request.
## Contributing
@@ -94,50 +138,3 @@ Would I love to see more providers? Certainly! Would you love to contribute one?
4. Commit your changes (git commit -am 'Add some feature')
5. Push to the branch (git push origin my-new-feature)
6. Create new Pull Request
## Contributors
* Mark Bates
* Tyler Bunnell
* Corey McGrillis
* willemvd
* Rakesh Goyal
* Andy Grunwald
* Glenn Walker
* Kevin Fitzpatrick
* Ben Tranter
* Sharad Ganapathy
* Andrew Chilton
* sharadgana
* Aurorae
* Craig P Jolicoeur
* Zac Bergquist
* Geoff Franks
* Raphael Geronimi
* Noah Shibley
* lumost
* oov
* Felix Lamouroux
* Rafael Quintela
* Tyler
* DenSm
* Samy KACIMI
* dante gray
* Noah
* Jacob Walker
* Marin Martinic
* Roy
* Omni Adams
* Sasa Brankovic
* dkhamsing
* Dante Swift
* Attila Domokos
* Albin Gilles
* Syed Zubairuddin
* Johnny Boursiquot
* Jerome Touffe-Blin
* bryanl
* Masanobu YOSHIOKA
* Jonathan Hall
* HaiMing.Yin
* Sairam Kunala

View File

@@ -8,10 +8,18 @@ See https://github.com/markbates/goth/examples/main.go to see this in action.
package gothic
import (
"bytes"
"compress/gzip"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
@@ -27,15 +35,21 @@ var defaultStore sessions.Store
var keySet = false
var gothicRand *rand.Rand
func init() {
key := []byte(os.Getenv("SESSION_SECRET"))
keySet = len(key) != 0
Store = sessions.NewCookieStore([]byte(key))
cookieStore := sessions.NewCookieStore([]byte(key))
cookieStore.Options.HttpOnly = true
Store = cookieStore
defaultStore = Store
gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
}
/*
BeginAuthHandler is a convienence handler for starting the authentication process.
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".
@@ -65,8 +79,16 @@ var SetState = func(req *http.Request) string {
return state
}
return "state"
// If a state query param is not passed in, generate a random
// base64-encoded nonce so that the state on the auth URL
// is unguessable, preventing CSRF attacks, as described in
//
// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
nonceBytes := make([]byte, 64)
for i := 0; i < 64; i++ {
nonceBytes[i] = byte(gothicRand.Int63() % 256)
}
return base64.URLEncoding.EncodeToString(nonceBytes)
}
// GetState gets the state returned by the provider during the callback.
@@ -87,7 +109,6 @@ I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
if !keySet && defaultStore == Store {
fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
}
@@ -111,7 +132,7 @@ func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
return "", err
}
err = storeInSession(providerName, sess.Marshal(), req, res)
err = StoreInSession(providerName, sess.Marshal(), req, res)
if err != nil {
return "", err
@@ -130,7 +151,7 @@ as either "provider" or ":provider".
See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
defer Logout(res, req)
if !keySet && defaultStore == Store {
fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
}
@@ -145,7 +166,7 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
return goth.User{}, err
}
value, err := getFromSession(providerName, req)
value, err := GetFromSession(providerName, req)
if err != nil {
return goth.User{}, err
}
@@ -155,6 +176,11 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
return goth.User{}, err
}
err = validateState(req, sess)
if err != nil {
return goth.User{}, err
}
user, err := provider.FetchUser(sess)
if err == nil {
// user can be found with existing session data
@@ -167,13 +193,49 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
return goth.User{}, err
}
err = storeInSession(providerName, sess.Marshal(), req, res)
err = StoreInSession(providerName, sess.Marshal(), req, res)
if err != nil {
return goth.User{}, err
}
return provider.FetchUser(sess)
gu, err := provider.FetchUser(sess)
return gu, err
}
// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
rawAuthURL, err := sess.GetAuthURL()
if err != nil {
return err
}
authURL, err := url.Parse(rawAuthURL)
if err != nil {
return err
}
originalState := authURL.Query().Get("state")
if originalState != "" && (originalState != req.URL.Query().Get("state")) {
return errors.New("state token mismatch")
}
return nil
}
// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
session, err := Store.Get(req, SessionName)
if err != nil {
return err
}
session.Options.MaxAge = -1
session.Values = make(map[interface{}]interface{})
err = session.Save(req, res)
if err != nil {
return errors.New("Could not delete user session ")
}
return nil
}
// GetProviderName is a function used to get the name of a provider
@@ -184,36 +246,99 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
var GetProviderName = getProviderName
func getProviderName(req *http.Request) (string, error) {
provider := req.URL.Query().Get("provider")
if provider == "" {
if p, ok := mux.Vars(req)["provider"]; ok {
// get all the used providers
providers := goth.GetProviders()
// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
for _, provider := range providers {
p := provider.Name()
session, _ := Store.Get(req, p+SessionName)
value := session.Values[p]
if _, ok := value.(string); ok {
return p, nil
}
}
if provider == "" {
provider = req.URL.Query().Get(":provider")
// try to get it from the url param "provider"
if p := req.URL.Query().Get("provider"); p != "" {
return p, nil
}
if provider == "" {
return provider, errors.New("you must select a provider")
// try to get it from the url param ":provider"
if p := req.URL.Query().Get(":provider"); p != "" {
return p, nil
}
return provider, nil
// try to get it from the context's value of "provider" key
if p, ok := mux.Vars(req)["provider"]; ok {
return p, nil
}
// try to get it from the go-context's value of "provider" key
if p, ok := req.Context().Value("provider").(string); ok {
return p, nil
}
// if not found then return an empty string with the corresponding error
return "", errors.New("you must select a provider")
}
func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
session, _ := Store.Get(req, key + SessionName)
// StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
session, _ := Store.New(req, SessionName)
session.Values[key] = value
if err := updateSessionValue(session, key, value); err != nil {
return err
}
return session.Save(req, res)
}
func getFromSession(key string, req *http.Request) (string, error) {
session, _ := Store.Get(req, key + SessionName)
value := session.Values[key]
if value == nil {
// GetFromSession retrieves a previously-stored value from the session.
// If no value has previously been stored at the specified key, it will return an error.
func GetFromSession(key string, req *http.Request) (string, error) {
session, _ := Store.Get(req, SessionName)
value, err := getSessionValue(session, key)
if err != nil {
return "", errors.New("could not find a matching session for this request")
}
return value.(string), nil
}
return value, nil
}
func getSessionValue(session *sessions.Session, key string) (string, error) {
value := session.Values[key]
if value == nil {
return "", fmt.Errorf("could not find a matching session for this request")
}
rdata := strings.NewReader(value.(string))
r, err := gzip.NewReader(rdata)
if err != nil {
return "", err
}
s, err := ioutil.ReadAll(r)
if err != nil {
return "", err
}
return string(s), nil
}
func updateSessionValue(session *sessions.Session, key, value string) error {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
if _, err := gz.Write([]byte(value)); err != nil {
return err
}
if err := gz.Flush(); err != nil {
return err
}
if err := gz.Close(); err != nil {
return err
}
session.Values[key] = b.String()
return nil
}

View File

@@ -9,9 +9,9 @@ import (
"net/http"
"net/url"
"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
"fmt"
)
const (
@@ -26,10 +26,10 @@ const (
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "bitbucket",
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "bitbucket",
}
p.config = newConfig(p, scopes)
return p
@@ -125,7 +125,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
func userFromReader(reader io.Reader, user *goth.User) error {
u := struct {
ID string `json:"uuid"`
ID string `json:"uuid"`
Links struct {
Avatar struct {
URL string `json:"href"`

View File

@@ -2,21 +2,24 @@
package dropbox
import (
"bytes"
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"strings"
"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
"fmt"
)
const (
authURL = "https://www.dropbox.com/1/oauth2/authorize"
tokenURL = "https://api.dropbox.com/1/oauth2/token"
accountURL = "https://api.dropbox.com/1/account/info"
authURL = "https://www.dropbox.com/oauth2/authorize"
tokenURL = "https://api.dropbox.com/oauth2/token"
accountURL = "https://api.dropbox.com/2/users/get_current_account"
)
// Provider is the implementation of `goth.Provider` for accessing Dropbox.
@@ -24,6 +27,7 @@ type Provider struct {
ClientKey string
Secret string
CallbackURL string
AccountURL string
HTTPClient *http.Client
config *oauth2.Config
providerName string
@@ -40,10 +44,11 @@ type Session struct {
// create one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "dropbox",
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
AccountURL: accountURL,
providerName: "dropbox",
}
p.config = newConfig(p, scopes)
return p
@@ -86,7 +91,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
}
req, err := http.NewRequest("GET", accountURL, nil)
req, err := http.NewRequest("POST", p.AccountURL, nil)
if err != nil {
return user, err
}
@@ -101,7 +106,17 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
}
err = userFromReader(resp.Body, &user)
bits, err := ioutil.ReadAll(resp.Body)
if err != nil {
return user, err
}
err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
if err != nil {
return user, err
}
err = userFromReader(bytes.NewReader(bits), &user)
return user, err
}
@@ -161,22 +176,29 @@ func newConfig(p *Provider, scopes []string) *oauth2.Config {
func userFromReader(r io.Reader, user *goth.User) error {
u := struct {
Name string `json:"display_name"`
NameDetails struct {
NickName string `json:"familiar_name"`
} `json:"name_details"`
Location string `json:"country"`
Email string `json:"email"`
AccountID string `json:"account_id"`
Name struct {
GivenName string `json:"given_name"`
Surname string `json:"surname"`
DisplayName string `json:"display_name"`
} `json:"name"`
Country string `json:"country"`
Email string `json:"email"`
ProfilePhotoURL string `json:"profile_photo_url"`
}{}
err := json.NewDecoder(r).Decode(&u)
if err != nil {
return err
}
user.UserID = u.AccountID // The user's unique Dropbox ID.
user.FirstName = u.Name.GivenName
user.LastName = u.Name.Surname
user.Name = strings.TrimSpace(fmt.Sprintf("%s %s", u.Name.GivenName, u.Name.Surname))
user.Description = u.Name.DisplayName // Full name plus parenthetical team naem
user.Email = u.Email
user.Name = u.Name
user.NickName = u.NameDetails.NickName
user.UserID = u.Email // Dropbox doesn't provide a separate user ID
user.Location = u.Location
user.NickName = u.Email // Email is the dropbox username
user.Location = u.Country
user.AvatarURL = u.ProfilePhotoURL // May be blank
return nil
}

View File

@@ -11,12 +11,12 @@ import (
"net/http"
"net/url"
"github.com/markbates/goth"
"golang.org/x/oauth2"
"fmt"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
)
const (
@@ -30,10 +30,10 @@ const (
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "facebook",
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "facebook",
}
p.config = newConfig(p, scopes)
return p
@@ -129,7 +129,7 @@ func userFromReader(reader io.Reader, user *goth.User) error {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Link string `json:"link"`
Picture struct {
Picture struct {
Data struct {
URL string `json:"url"`
} `json:"data"`

View File

@@ -11,9 +11,9 @@ import (
"net/url"
"strconv"
"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
"fmt"
)
// These vars define the Authentication, Token, and Profile URLS for Gitlab. If

View File

@@ -11,9 +11,9 @@ import (
"net/url"
"strings"
"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
"fmt"
)
const (
@@ -27,10 +27,10 @@ const (
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "gplus",
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "gplus",
}
p.config = newConfig(p, scopes)
return p

View File

@@ -1,17 +1,17 @@
package openidConnect
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
"io/ioutil"
"net/http"
"strings"
"fmt"
"encoding/json"
"encoding/base64"
"io/ioutil"
"errors"
"golang.org/x/oauth2"
"github.com/markbates/goth"
"time"
"bytes"
)
const (
@@ -89,14 +89,14 @@ func New(clientKey, secret, callbackURL, openIDAutoDiscoveryURL string, scopes .
Secret: secret,
CallbackURL: callbackURL,
UserIdClaims: []string{subjectClaim},
NameClaims: []string{NameClaim},
NickNameClaims: []string{NicknameClaim, PreferredUsernameClaim},
EmailClaims: []string{EmailClaim},
AvatarURLClaims:[]string{PictureClaim},
FirstNameClaims:[]string{GivenNameClaim},
LastNameClaims: []string{FamilyNameClaim},
LocationClaims: []string{AddressClaim},
UserIdClaims: []string{subjectClaim},
NameClaims: []string{NameClaim},
NickNameClaims: []string{NicknameClaim, PreferredUsernameClaim},
EmailClaims: []string{EmailClaim},
AvatarURLClaims: []string{PictureClaim},
FirstNameClaims: []string{GivenNameClaim},
LastNameClaims: []string{FamilyNameClaim},
LocationClaims: []string{AddressClaim},
providerName: "openid-connect",
}

View File

@@ -1,12 +1,12 @@
package openidConnect
import (
"encoding/json"
"errors"
"github.com/markbates/goth"
"encoding/json"
"golang.org/x/oauth2"
"strings"
"time"
"golang.org/x/oauth2"
)
// Session stores data during the auth process with the OpenID Connect provider.

View File

@@ -9,10 +9,11 @@ import (
"io/ioutil"
"net/http"
"fmt"
"github.com/markbates/goth"
"github.com/mrjones/oauth"
"golang.org/x/oauth2"
"fmt"
)
var (
@@ -30,10 +31,10 @@ var (
// If you'd like to use authenticate instead of authorize, use NewAuthenticate instead.
func New(clientKey, secret, callbackURL string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "twitter",
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "twitter",
}
p.consumer = newConsumer(p, authorizeURL)
return p
@@ -43,10 +44,10 @@ func New(clientKey, secret, callbackURL string) *Provider {
// NewAuthenticate uses the authenticate URL instead of the authorize URL.
func NewAuthenticate(clientKey, secret, callbackURL string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "twitter",
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "twitter",
}
p.consumer = newConsumer(p, authenticateURL)
return p
@@ -107,7 +108,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
response, err := p.consumer.Get(
endpointProfile,
map[string]string{"include_entities": "false", "skip_status": "true"},
map[string]string{"include_entities": "false", "skip_status": "true", "include_email": "true"},
sess.AccessToken)
if err != nil {
return user, err
@@ -126,6 +127,9 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
user.Name = user.RawData["name"].(string)
user.NickName = user.RawData["screen_name"].(string)
if user.RawData["email"] != nil {
user.Email = user.RawData["email"].(string)
}
user.Description = user.RawData["description"].(string)
user.AvatarURL = user.RawData["profile_image_url"].(string)
user.UserID = user.RawData["id_str"].(string)

68
vendor/vendor.json vendored
View File

@@ -654,64 +654,74 @@
"revisionTime": "2017-10-25T03:15:54Z"
},
{
"checksumSHA1": "O3KUfEXQPfdQ+tCMpP2RAIRJJqY=",
"checksumSHA1": "q9MD1ienC+kmKq5i51oAktQEV1E=",
"origin": "github.com/go-gitea/goth",
"path": "github.com/markbates/goth",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "MkFKwLV3icyUo4oP0BgEs+7+R1Y=",
"checksumSHA1": "FISfgOkoMtn98wglLUvfBTZ6baE=",
"origin": "github.com/go-gitea/goth/gothic",
"path": "github.com/markbates/goth/gothic",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "crNSlQADjX6hcxykON2tFCqY4iw=",
"checksumSHA1": "pJ+Cws/TU22K6tZ/ALFOvvH1K5U=",
"origin": "github.com/go-gitea/goth/providers/bitbucket",
"path": "github.com/markbates/goth/providers/bitbucket",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "1Kp4DKkJNVn135Xg8H4a6CFBNy8=",
"checksumSHA1": "XsF5HI4240QHbFXbtWWnGgTsoq8=",
"origin": "github.com/go-gitea/goth/providers/dropbox",
"path": "github.com/markbates/goth/providers/dropbox",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "cGs1da29iOBJh5EAH0icKDbN8CA=",
"checksumSHA1": "VzbroIA9R00Ig3iGnOlZLU7d4ls=",
"origin": "github.com/go-gitea/goth/providers/facebook",
"path": "github.com/markbates/goth/providers/facebook",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "P6nBZ850aaekpOcoXNdRhK86bH8=",
"origin": "github.com/go-gitea/goth/providers/github",
"path": "github.com/markbates/goth/providers/github",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "o/109paSRy9HqV87gR4zUZMMSzs=",
"checksumSHA1": "ld488t+yGoTwtmiCSSggEX4fxVk=",
"origin": "github.com/go-gitea/goth/providers/gitlab",
"path": "github.com/markbates/goth/providers/gitlab",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "cX6kR9y94BWFZvI/7UFrsFsP3FQ=",
"checksumSHA1": "qXEulD7vnwY9hFrxh91Pm5YrvTM=",
"origin": "github.com/go-gitea/goth/providers/gplus",
"path": "github.com/markbates/goth/providers/gplus",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "sMYKhqAUZXM1+T/TjlMhWh8Vveo=",
"checksumSHA1": "wsOBzyp4LKDhfCPmX1LLP7T0S3U=",
"origin": "github.com/go-gitea/goth/providers/openidConnect",
"path": "github.com/markbates/goth/providers/openidConnect",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "1w0V6jYXaGlEtZcMeYTOAAucvgw=",
"checksumSHA1": "o6RqMbbE8QNZhNT9TsAIRMPI8tg=",
"origin": "github.com/go-gitea/goth/providers/twitter",
"path": "github.com/markbates/goth/providers/twitter",
"revision": "90362394a367f9d77730911973462a53d69662ba",
"revisionTime": "2017-02-23T14:12:10Z"
"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
"revisionTime": "2018-03-12T06:32:04Z"
},
{
"checksumSHA1": "61HNjGetaBoMp8HBOpuEZRSim8g=",