Compare commits

..

11 Commits

Author SHA1 Message Date
techknowlogick
f64b8eb009 1.8.0-rc3 changelog & 1.7.6 changelog backport (#6597) 2019-04-12 23:12:47 -04:00
zeripath
40f41dc694 Correctly adjust mirror url (#6593) (#6594) 2019-04-12 22:25:06 +01:00
John Olheiser
a63b9fbc70 Change summary to full message (#6591) (#6592)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-04-12 20:47:29 +03:00
Lunny Xiao
4b87aa367c fix mail notification when close/reopen issue (#6581) (#6588) 2019-04-12 13:01:34 +01:00
Jonas Franz
72f4cdf868 Add option to disable refresh token invalidation (#6584) (#6587)
* Add option to disable refresh token invalidation

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

* Add integration tests and remove wrong todos

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

* Fix typo

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

* Fix tests and add documentation

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

(cherry picked from commit 783cd64927)
Signed-off-by: Jonas Franz <info@jonasfranz.software>
2019-04-12 13:42:44 +03:00
Lunny Xiao
5be1b7df3f fix bug user search API pagesize didn't obey ExplorePagingNum (#6579) (#6586) 2019-04-11 21:45:35 -04:00
John Olheiser
95e12be30f Fix new repo alignment (#6583) (#6585)
* Fix new repo alignment (#6583)

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Kick Drone

* Make stylesheets again.

Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-04-11 19:29:00 -04:00
zeripath
245089b9c9 Prevent server 500 on compare branches with no common history (#6555) (#6558)
* Prevent server 500 on compare branches with no common history (#6555)
* Update code.gitea.io/git module
2019-04-11 21:31:17 +01:00
mrsdizzie
2551660f49 Properly escape release attachment URL (#6512) (#6523)
Make sure file attachments on a release get a properly escaped URL when
linking.

Fixes #6506
2019-04-05 13:00:20 -04:00
Lauris BH
3b28de7d8e fix bug when user login and want to resend register confirmation email (#6482) (#6486) 2019-04-02 09:36:56 +01:00
zeripath
3725eefb7f Hacky fix for alignment of the create-organization dialog (#6455) (#6462)
* Hacky fix for alignment of the create-organization dialog

* Mangle the post-scripted red-asterisk back in to follow the Visibility Header
2019-03-28 15:17:04 -04:00
27 changed files with 253 additions and 87 deletions

View File

@@ -4,6 +4,20 @@ 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 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). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.8.0-rc3](https://github.com/go-gitea/gitea/releases/tag/v1.8.0-rc3) - 2019-04-12
* SECURITY
* Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6594)
* BUGFIXES
* Allow resend of confirmation email when logged in (#6482) (#6486)
* Fix mail notification when close/reopen issue (#6581) (#6588)
* Change API commit summary to full message (#6591) (#6592)
* Add option to disable refresh token invalidation (#6584) (#6587)
* Fix bug user search API pagesize didn't obey ExplorePagingNum (#6579) (#6586)
* Fix new repo alignment (#6583) (#6585)
* Prevent server 500 on compare branches with no common history (#6555) (#6558)
* Properly escape release attachment URL (#6512) (#6523)
* Hacky fix for alignment of the create-organization dialog (#6455) (#6462)
## [1.8.0-rc2](https://github.com/go-gitea/gitea/releases/tag/v1.8.0-rc2) - 2019-03-27 ## [1.8.0-rc2](https://github.com/go-gitea/gitea/releases/tag/v1.8.0-rc2) - 2019-03-27
* BUGFIXES * BUGFIXES
* Disable benchmarking during tag events on DroneIO (#6365) (#6366) * Disable benchmarking during tag events on DroneIO (#6365) (#6366)
@@ -222,6 +236,12 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add missing GET teams endpoints (#5382) * Add missing GET teams endpoints (#5382)
* Migrate database if app.ini found (#5290) * Migrate database if app.ini found (#5290)
## [1.7.6](https://github.com/go-gitea/gitea/releases/tag/v1.7.6) - 2019-04-12
* SECURITY
* Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6595)
* BUGFIXES
* Allow resend of confirmation email when logged in (#6482) (#6487)
## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27 ## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27
* BUGFIXES * BUGFIXES
* Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423) * Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)

4
Gopkg.lock generated
View File

@@ -3,11 +3,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:e1fa64238b0a2dbf1edf98c4af8d1b8cb65179e286d7f28006b50fa9f508ee9d" digest = "1:c298eea5ff7f6ab40cda6fe75d2224e2dd271941abe2f66276063b39e43e5687"
name = "code.gitea.io/git" name = "code.gitea.io/git"
packages = ["."] packages = ["."]
pruneopts = "NUT" pruneopts = "NUT"
revision = "74d7c14dd4a3ed9c5def0dc3c1aeede399ddc5c5" revision = "63b74d438b29bb272fa9b4010abe3f50a832e7ef"
[[projects]] [[projects]]
branch = "master" branch = "master"

View File

@@ -661,6 +661,8 @@ ENABLED = true
ACCESS_TOKEN_EXPIRATION_TIME=3600 ACCESS_TOKEN_EXPIRATION_TIME=3600
; Lifetime of an OAuth2 access token in hours ; Lifetime of an OAuth2 access token in hours
REFRESH_TOKEN_EXPIRATION_TIME=730 REFRESH_TOKEN_EXPIRATION_TIME=730
; Check if refresh token got already used
INVALIDATE_REFRESH_TOKENS=false
; OAuth2 authentication secret for access and refresh tokens, change this a unique string. ; OAuth2 authentication secret for access and refresh tokens, change this a unique string.
JWT_SECRET=Bk0yK7Y9g_p56v86KaHqjSbxvNvu3SbKoOdOt2ZcXvU JWT_SECRET=Bk0yK7Y9g_p56v86KaHqjSbxvNvu3SbKoOdOt2ZcXvU

View File

@@ -350,6 +350,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `ENABLED`: **true**: Enables OAuth2 provider. - `ENABLED`: **true**: Enables OAuth2 provider.
- `ACCESS_TOKEN_EXPIRATION_TIME`: **3600**: Lifetime of an OAuth2 access token in seconds - `ACCESS_TOKEN_EXPIRATION_TIME`: **3600**: Lifetime of an OAuth2 access token in seconds
- `REFRESH_TOKEN_EXPIRATION_TIME`: **730**: Lifetime of an OAuth2 access token in hours - `REFRESH_TOKEN_EXPIRATION_TIME`: **730**: Lifetime of an OAuth2 access token in hours
- `INVALIDATE_REFRESH_TOKEN`: **false**: Check if refresh token got already used
- `JWT_SECRET`: **\<empty\>**: OAuth2 authentication secret for access and refresh tokens, change this a unique string. - `JWT_SECRET`: **\<empty\>**: OAuth2 authentication secret for access and refresh tokens, change this a unique string.
## i18n (`i18n`) ## i18n (`i18n`)

View File

@@ -8,6 +8,8 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -177,3 +179,42 @@ func TestAccessTokenExchangeWithBasicAuth(t *testing.T) {
}) })
resp = MakeRequest(t, req, 400) resp = MakeRequest(t, req, 400)
} }
func TestRefreshTokenInvalidation(t *testing.T) {
prepareTestEnv(t)
req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"grant_type": "authorization_code",
"client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
"client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
"redirect_uri": "a",
"code": "authcode",
"code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
})
resp := MakeRequest(t, req, 200)
type response struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
}
parsed := new(response)
assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
// test without invalidation
setting.OAuth2.InvalidateRefreshTokens = false
refreshReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
"grant_type": "refresh_token",
"client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
"client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
"redirect_uri": "a",
"refresh_token": parsed.RefreshToken,
})
MakeRequest(t, refreshReq, 200)
MakeRequest(t, refreshReq, 200)
// test with invalidation
setting.OAuth2.InvalidateRefreshTokens = true
MakeRequest(t, refreshReq, 200)
MakeRequest(t, refreshReq, 400)
}

View File

@@ -118,17 +118,25 @@ func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content
// MailParticipants sends new issue thread created emails to repository watchers // MailParticipants sends new issue thread created emails to repository watchers
// and mentioned people. // and mentioned people.
func (issue *Issue) MailParticipants() (err error) { func (issue *Issue) MailParticipants(opType ActionType) (err error) {
return issue.mailParticipants(x) return issue.mailParticipants(x, opType)
} }
func (issue *Issue) mailParticipants(e Engine) (err error) { func (issue *Issue) mailParticipants(e Engine, opType ActionType) (err error) {
mentions := markup.FindAllMentions(issue.Content) mentions := markup.FindAllMentions(issue.Content)
if err = UpdateIssueMentions(e, issue.ID, mentions); err != nil { if err = UpdateIssueMentions(e, issue.ID, mentions); err != nil {
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err) return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
} }
if err = mailIssueCommentToParticipants(e, issue, issue.Poster, issue.Content, nil, mentions); err != nil { var content = issue.Content
switch opType {
case ActionCloseIssue, ActionClosePullRequest:
content = fmt.Sprintf("Closed #%d", issue.Index)
case ActionReopenIssue, ActionReopenPullRequest:
content = fmt.Sprintf("Reopened #%d", issue.Index)
}
if err = mailIssueCommentToParticipants(e, issue, issue.Poster, content, nil, mentions); err != nil {
log.Error(4, "mailIssueCommentToParticipants: %v", err) log.Error(4, "mailIssueCommentToParticipants: %v", err)
} }

View File

@@ -616,9 +616,9 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
return nil, err return nil, err
} }
if !user.IsActive { // WARN: DON'T check user.IsActive, that will be checked on reqSign so that
return nil, ErrUserInactive{user.ID, user.Name} // user could be hint to resend confirm email.
} else if user.ProhibitLogin { if user.ProhibitLogin {
return nil, ErrUserProhibitLogin{user.ID, user.Name} return nil, ErrUserProhibitLogin{user.ID, user.Name}
} }
@@ -658,9 +658,9 @@ func UserSignIn(username, password string) (*User, error) {
switch user.LoginType { switch user.LoginType {
case LoginNoType, LoginPlain, LoginOAuth2: case LoginNoType, LoginPlain, LoginOAuth2:
if user.IsPasswordSet() && user.ValidatePassword(password) { if user.IsPasswordSet() && user.ValidatePassword(password) {
if !user.IsActive { // WARN: DON'T check user.IsActive, that will be checked on reqSign so that
return nil, ErrUserInactive{user.ID, user.Name} // user could be hint to resend confirm email.
} else if user.ProhibitLogin { if user.ProhibitLogin {
return nil, ErrUserProhibitLogin{user.ID, user.Name} return nil, ErrUserProhibitLogin{user.ID, user.Name}
} }

View File

@@ -1077,9 +1077,11 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
} }
} }
if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil { _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
} }
if repo.HasWiki() { if repo.HasWiki() {
if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil { if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err) return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)

View File

@@ -20,7 +20,6 @@ import (
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"gopkg.in/ini.v1"
) )
// MirrorQueue holds an UniqueQueue object of the mirror // MirrorQueue holds an UniqueQueue object of the mirror
@@ -71,11 +70,18 @@ func (m *Mirror) ScheduleNextUpdate() {
} }
func remoteAddress(repoPath string) (string, error) { func remoteAddress(repoPath string) (string, error) {
cfg, err := ini.Load(GitConfigPath(repoPath)) cmd := git.NewCommand("remote", "get-url", "origin")
result, err := cmd.RunInDir(repoPath)
if err != nil { if err != nil {
if strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return "", nil
}
return "", err return "", err
} }
return cfg.Section("remote \"origin\"").Key("url").Value(), nil if len(result) > 0 {
return result[:len(result)-1], nil
}
return "", nil
} }
func (m *Mirror) readAddress() { func (m *Mirror) readAddress() {
@@ -115,14 +121,15 @@ func (m *Mirror) FullAddress() string {
// SaveAddress writes new address to Git repository config. // SaveAddress writes new address to Git repository config.
func (m *Mirror) SaveAddress(addr string) error { func (m *Mirror) SaveAddress(addr string) error {
configPath := m.Repo.GitConfigPath() repoPath := m.Repo.RepoPath()
cfg, err := ini.Load(configPath) // Remove old origin
if err != nil { _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
return fmt.Errorf("Load: %v", err) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return err
} }
cfg.Section("remote \"origin\"").Key("url").SetValue(addr) _, err = git.NewCommand("remote", "add", "origin", addr).RunInDir(repoPath)
return cfg.SaveToIndent(configPath, "\t") return err
} }
// gitShortEmptySha Git short empty SHA // gitShortEmptySha Git short empty SHA

View File

@@ -172,7 +172,6 @@ type AccessTokenForm struct {
ClientID string ClientID string
ClientSecret string ClientSecret string
RedirectURI string RedirectURI string
// TODO Specify authentication code length to prevent against birthday attacks
Code string Code string
RefreshToken string RefreshToken string

View File

@@ -42,19 +42,34 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.
} }
func (m *mailNotifier) NotifyNewIssue(issue *models.Issue) { func (m *mailNotifier) NotifyNewIssue(issue *models.Issue) {
if err := issue.MailParticipants(); err != nil { if err := issue.MailParticipants(models.ActionCreateIssue); err != nil {
log.Error(4, "MailParticipants: %v", err) log.Error(4, "MailParticipants: %v", err)
} }
} }
func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, isClosed bool) { func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, isClosed bool) {
if err := issue.MailParticipants(); err != nil { var actionType models.ActionType
if issue.IsPull {
if isClosed {
actionType = models.ActionClosePullRequest
} else {
actionType = models.ActionReopenPullRequest
}
} else {
if isClosed {
actionType = models.ActionCloseIssue
} else {
actionType = models.ActionReopenIssue
}
}
if err := issue.MailParticipants(actionType); err != nil {
log.Error(4, "MailParticipants: %v", err) log.Error(4, "MailParticipants: %v", err)
} }
} }
func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest) { func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest) {
if err := pr.Issue.MailParticipants(); err != nil { if err := pr.Issue.MailParticipants(models.ActionCreatePullRequest); err != nil {
log.Error(4, "MailParticipants: %v", err) log.Error(4, "MailParticipants: %v", err)
} }
} }

View File

@@ -299,12 +299,14 @@ var (
Enable bool Enable bool
AccessTokenExpirationTime int64 AccessTokenExpirationTime int64
RefreshTokenExpirationTime int64 RefreshTokenExpirationTime int64
InvalidateRefreshTokens bool
JWTSecretBytes []byte `ini:"-"` JWTSecretBytes []byte `ini:"-"`
JWTSecretBase64 string `ini:"JWT_SECRET"` JWTSecretBase64 string `ini:"JWT_SECRET"`
}{ }{
Enable: true, Enable: true,
AccessTokenExpirationTime: 3600, AccessTokenExpirationTime: 3600,
RefreshTokenExpirationTime: 730, RefreshTokenExpirationTime: 730,
InvalidateRefreshTokens: false,
} }
U2F = struct { U2F = struct {

View File

@@ -566,7 +566,9 @@ mirror_prune_desc = Remove obsolete remote-tracking references
mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync. mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync.
mirror_interval_invalid = The mirror interval is not valid. mirror_interval_invalid = The mirror interval is not valid.
mirror_address = Clone From URL mirror_address = Clone From URL
mirror_address_desc = Include any required authorization credentials in the URL. mirror_address_desc = Include any required authorization credentials in the URL. These must be url escaped as appropriate
mirror_address_url_invalid = The provided url is invalid. You must escape all components of the url correctly.
mirror_address_protocol_invalid = The provided url is invalid. Only http(s):// or git:// locations can be mirrored from.
mirror_last_synced = Last Synchronized mirror_last_synced = Last Synchronized
watchers = Watchers watchers = Watchers
stargazers = Stargazers stargazers = Stargazers
@@ -683,6 +685,7 @@ editor.cannot_commit_to_protected_branch = Cannot commit to protected branch '%s
commits.desc = Browse source code change history. commits.desc = Browse source code change history.
commits.commits = Commits commits.commits = Commits
commits.no_commits = No commits in common. '%s' and '%s' have entirely different histories.
commits.search = Search commits… commits.search = Search commits…
commits.find = Search commits.find = Search
commits.search_all = All Branches commits.search_all = All Branches

File diff suppressed because one or more lines are too long

View File

@@ -300,6 +300,10 @@ pre, code {
font-size: .92857143rem; font-size: .92857143rem;
} }
&.menu .ui.dropdown.item .menu .item {
margin-right: auto;
}
&.dropdown .menu>.item>.floating.label { &.dropdown .menu>.item>.floating.label {
z-index: 11; z-index: 11;
} }

View File

@@ -106,7 +106,7 @@ func GetSingleCommit(ctx *context.APIContext) {
Email: commit.Committer.Email, Email: commit.Committer.Email,
Date: commit.Committer.When.Format(time.RFC3339), Date: commit.Committer.When.Format(time.RFC3339),
}, },
Message: commit.Summary(), Message: commit.Message(),
Tree: &api.CommitMeta{ Tree: &api.CommitMeta{
URL: ctx.Repo.Repository.APIURL() + "/trees/" + commit.ID.String(), URL: ctx.Repo.Repository.APIURL() + "/trees/" + commit.ID.String(),
SHA: commit.ID.String(), SHA: commit.ID.String(),

View File

@@ -55,9 +55,6 @@ func Search(ctx *context.APIContext) {
Type: models.UserTypeIndividual, Type: models.UserTypeIndividual,
PageSize: com.StrTo(ctx.Query("limit")).MustInt(), PageSize: com.StrTo(ctx.Query("limit")).MustInt(),
} }
if opts.PageSize <= 0 {
opts.PageSize = 10
}
users, _, err := models.SearchUsers(opts) users, _, err := models.SearchUsers(opts)
if err != nil { if err != nil {

View File

@@ -7,11 +7,12 @@ package repo
import ( import (
"errors" "errors"
"net/url"
"regexp"
"strings" "strings"
"time" "time"
"code.gitea.io/git" "code.gitea.io/git"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@@ -21,6 +22,8 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/routers/utils"
"github.com/mvdan/xurls"
) )
const ( const (
@@ -33,6 +36,8 @@ const (
tplProtectedBranch base.TplName = "repo/settings/protected_branch" tplProtectedBranch base.TplName = "repo/settings/protected_branch"
) )
var validFormAddress *regexp.Regexp
// Settings show a repository's settings page // Settings show a repository's settings page
func Settings(ctx *context.Context) { func Settings(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["Title"] = ctx.Tr("repo.settings")
@@ -146,7 +151,38 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
return return
} }
} }
if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil {
// Validate the form.MirrorAddress
u, err := url.Parse(form.MirrorAddress)
if err != nil {
ctx.Data["Err_MirrorAddress"] = true
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form)
return
}
if u.Opaque != "" || !(u.Scheme == "http" || u.Scheme == "https" || u.Scheme == "git") {
ctx.Data["Err_MirrorAddress"] = true
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, &form)
return
}
// Now use xurls
address := validFormAddress.FindString(form.MirrorAddress)
if address != form.MirrorAddress && form.MirrorAddress != "" {
ctx.Data["Err_MirrorAddress"] = true
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form)
return
}
if u.EscapedPath() == "" || u.Host == "" || !u.IsAbs() {
ctx.Data["Err_MirrorAddress"] = true
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form)
return
}
address = u.String()
if err := ctx.Repo.Mirror.SaveAddress(address); err != nil {
ctx.ServerError("SaveAddress", err) ctx.ServerError("SaveAddress", err)
return return
} }
@@ -683,3 +719,11 @@ func DeleteDeployKey(ctx *context.Context) {
"redirect": ctx.Repo.RepoLink + "/settings/keys", "redirect": ctx.Repo.RepoLink + "/settings/keys",
}) })
} }
func init() {
var err error
validFormAddress, err = xurls.StrictMatchingScheme(`(https?)|(git)://`)
if err != nil {
panic(err)
}
}

View File

@@ -339,7 +339,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/user", func() { m.Group("/user", func() {
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
m.Any("/activate", user.Activate) m.Any("/activate", user.Activate, reqSignIn)
m.Any("/activate_email", user.ActivateEmail) m.Any("/activate_email", user.ActivateEmail)
m.Get("/email2user", user.Email2User) m.Get("/email2user", user.Email2User)
m.Get("/forgot_password", user.ForgotPasswd) m.Get("/forgot_password", user.ForgotPasswd)

View File

@@ -102,18 +102,19 @@ const (
// AccessTokenResponse represents a successful access token response // AccessTokenResponse represents a successful access token response
type AccessTokenResponse struct { type AccessTokenResponse struct {
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
TokenType TokenType `json:"token_type"` TokenType TokenType `json:"token_type"`
ExpiresIn int64 `json:"expires_in"` ExpiresIn int64 `json:"expires_in"`
// TODO implement RefreshToken RefreshToken string `json:"refresh_token"`
RefreshToken string `json:"refresh_token"`
} }
func newAccessTokenResponse(grant *models.OAuth2Grant) (*AccessTokenResponse, *AccessTokenError) { func newAccessTokenResponse(grant *models.OAuth2Grant) (*AccessTokenResponse, *AccessTokenError) {
if err := grant.IncreaseCounter(); err != nil { if setting.OAuth2.InvalidateRefreshTokens {
return nil, &AccessTokenError{ if err := grant.IncreaseCounter(); err != nil {
ErrorCode: AccessTokenErrorCodeInvalidGrant, return nil, &AccessTokenError{
ErrorDescription: "cannot increase the grant counter", ErrorCode: AccessTokenErrorCodeInvalidGrant,
ErrorDescription: "cannot increase the grant counter",
}
} }
} }
// generate access token to access the API // generate access token to access the API
@@ -366,7 +367,7 @@ func handleRefreshToken(ctx *context.Context, form auth.AccessTokenForm) {
} }
// check if token got already used // check if token got already used
if grant.Counter != token.Counter || token.Counter == 0 { if setting.OAuth2.InvalidateRefreshTokens && (grant.Counter != token.Counter || token.Counter == 0) {
handleAccessTokenError(ctx, AccessTokenError{ handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient, ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
ErrorDescription: "token was already used", ErrorDescription: "token was already used",

View File

@@ -15,28 +15,27 @@
<span class="help">{{.i18n.Tr "org.org_name_helper"}}</span> <span class="help">{{.i18n.Tr "org.org_name_helper"}}</span>
</div> </div>
<div class="inline required field {{if .Err_OrgVisibility}}error{{end}}"> <div class="inline field {{if .Err_OrgVisibility}}error{{end}}">
<label for="visibility">{{.i18n.Tr "org.settings.visibility"}}</label> <span class="inline required field"><label for="visibility">{{.i18n.Tr "org.settings.visibility"}}</label></span>
<div class="field"> <div class="ui radio checkbox">
<div class="ui radio checkbox"> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}/>
<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="0" {{if .DefaultOrgVisibilityMode.IsPublic}}checked{{end}}/> <label>{{.i18n.Tr "org.settings.visibility.public"}}</label>
<label>{{.i18n.Tr "org.settings.visibility.public"}}</label> </div>
</div> </div>
</div> <div class="inline field {{if .Err_OrgVisibility}}error{{end}}">
<div class="field"> <label>&nbsp;</label>
<div class="ui radio checkbox"> <div class="ui radio checkbox">
<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}/> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="1" {{if .DefaultOrgVisibilityMode.IsLimited}}checked{{end}}/>
<label>{{.i18n.Tr "org.settings.visibility.limited"}}</label> <label>{{.i18n.Tr "org.settings.visibility.limited"}}</label>
</div> </div>
</div> </div>
<div class="field"> <div class="inline field {{if .Err_OrgVisibility}}error{{end}}">
<div class="ui radio checkbox"> <label>&nbsp;</label>
<input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}/> <div class="ui radio checkbox">
<label>{{.i18n.Tr "org.settings.visibility.private"}}</label> <input class="hidden enable-system-radio" tabindex="0" name="visibility" type="radio" value="2" {{if .DefaultOrgVisibilityMode.IsPrivate}}checked{{end}}/>
</div> <label>{{.i18n.Tr "org.settings.visibility.private"}}</label>
</div> </div>
</div> </div>
<div class="inline field"> <div class="inline field">
<label></label> <label></label>
<button class="ui green button"> <button class="ui green button">

View File

@@ -1,9 +1,13 @@
<h4 class="ui top attached header"> <h4 class="ui top attached header">
<div class="ui stackable grid"> <div class="ui stackable grid">
<div class="six wide column"> <div class="ten wide column">
{{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}} {{if or .PageIsCommits (gt .CommitCount 0)}}
{{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}}
{{else}}
{{.i18n.Tr "repo.commits.no_commits" $.BaseBranch $.HeadBranch }} {{if .Branch}}({{.Branch}}){{end}}
{{end}}
</div> </div>
<div class="ten wide right aligned column"> <div class="six wide right aligned column">
{{if .PageIsCommits}} {{if .PageIsCommits}}
<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL | EscapePound}}/search"> <form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL | EscapePound}}/search">
<div class="ui tiny search input"> <div class="ui tiny search input">
@@ -23,7 +27,7 @@
</div> </div>
</h4> </h4>
{{if .Commits}} {{if and .Commits (gt .CommitCount 0)}}
<div class="ui attached table segment"> <div class="ui attached table segment">
<table class="ui very basic striped fixed table single line" id="commits-table"> <table class="ui very basic striped fixed table single line" id="commits-table">
<thead> <thead>

View File

@@ -54,6 +54,9 @@
<div class="ui segment"> <div class="ui segment">
{{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}} {{.i18n.Tr "repo.pulls.has_pull_request" $.RepoLink $.RepoRelPath .PullRequest.Index | Safe}}
</div> </div>
{{else if eq .CommitCount 0 }}
{{template "repo/commits_table" .}}
{{template "repo/diff/box" .}}
{{else}} {{else}}
{{template "repo/issue/new_form" .}} {{template "repo/issue/new_form" .}}
{{template "repo/commits_table" .}} {{template "repo/commits_table" .}}

View File

@@ -77,7 +77,7 @@
{{if .Attachments}} {{if .Attachments}}
{{range $attachment := .Attachments}} {{range $attachment := .Attachments}}
<li> <li>
<a target="_blank" rel="noopener noreferrer" href="{{$.RepoLink}}/releases/download/{{$release.TagName}}/{{$attachment.Name}}"> <a target="_blank" rel="noopener noreferrer" href="{{$.RepoLink}}/releases/download/{{$release.TagName | PathEscape}}/{{$attachment.Name | PathEscape}}">
<strong><span class="ui image octicon octicon-package" title='{{$attachment.Name}}'></span> {{$attachment.Name}}</strong> <strong><span class="ui image octicon octicon-package" title='{{$attachment.Name}}'></span> {{$attachment.Name}}</strong>
<span class="ui text grey right">{{$attachment.Size | FileSize}}</span> <span class="ui text grey right">{{$attachment.Size | FileSize}}</span>
</a> </a>

View File

@@ -58,7 +58,7 @@
<label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label> <label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label>
<input id="interval" name="interval" value="{{.MirrorInterval}}"> <input id="interval" name="interval" value="{{.MirrorInterval}}">
</div> </div>
<div class="field"> <div class="field {{if .Err_MirrorAddress}}error{{end}}">
<label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label>
<input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required> <input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required>
<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>

13
vendor/code.gitea.io/git/hook.go generated vendored
View File

@@ -82,11 +82,20 @@ func (h *Hook) Name() string {
func (h *Hook) Update() error { func (h *Hook) Update() error {
if len(strings.TrimSpace(h.Content)) == 0 { if len(strings.TrimSpace(h.Content)) == 0 {
if isExist(h.path) { if isExist(h.path) {
return os.Remove(h.path) err := os.Remove(h.path)
if err != nil {
return err
}
} }
h.IsActive = false
return nil return nil
} }
return ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm) err := ioutil.WriteFile(h.path, []byte(strings.Replace(h.Content, "\r", "", -1)), os.ModePerm)
if err != nil {
return err
}
h.IsActive = true
return nil
} }
// ListHooks returns a list of Git hooks of given repository. // ListHooks returns a list of Git hooks of given repository.

View File

@@ -48,17 +48,22 @@ func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch stri
prInfo := new(PullRequestInfo) prInfo := new(PullRequestInfo)
prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch) prInfo.MergeBase, err = repo.GetMergeBase(remoteBranch, headBranch)
if err != nil { if err == nil {
return nil, fmt.Errorf("GetMergeBase: %v", err) // We have a common base
} logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil {
logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path) return nil, err
if err != nil { }
return nil, err prInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
} if err != nil {
prInfo.Commits, err = repo.parsePrettyFormatLogToList(logs) return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
if err != nil { }
return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err) } else {
prInfo.Commits = list.New()
prInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch)
if err != nil {
prInfo.MergeBase = remoteBranch
}
} }
// Count number of changed files. // Count number of changed files.