mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-10 15:32:55 +09:00
Compare commits
13 Commits
v1.8.0-rc3
...
v1.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
799f5e05c9 | ||
|
|
497f37bffd | ||
|
|
d7aa553f1b | ||
|
|
ba12463175 | ||
|
|
0acaa6bd00 | ||
|
|
66a3353c31 | ||
|
|
5236d8a936 | ||
|
|
5876e37ed4 | ||
|
|
3c21a0ee80 | ||
|
|
f43783f003 | ||
|
|
b9c5a3acc3 | ||
|
|
c363ef5da0 | ||
|
|
e8ca2da08f |
52
CHANGELOG.md
52
CHANGELOG.md
@@ -4,33 +4,11 @@ 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
|
## [1.8.0](https://github.com/go-gitea/gitea/releases/tag/v1.8.0) - 2019-04-20
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6594)
|
* Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6594)
|
||||||
* BUGFIXES
|
* Resolve 2FA bypass on API (#6676) (#6674)
|
||||||
* Allow resend of confirmation email when logged in (#6482) (#6486)
|
* Prevent the creation of empty sessions for non-logged in users (#6690) (#6677)
|
||||||
* 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
|
|
||||||
* BUGFIXES
|
|
||||||
* Disable benchmarking during tag events on DroneIO (#6365) (#6366)
|
|
||||||
* Make sure units of a team are returned (#6379) (#6381)
|
|
||||||
* Don't Unescape redirect_to cookie value (#6399) (#6401)
|
|
||||||
* Fix dump table name error and add some test for dump database (#6394) (#6402)
|
|
||||||
* Fix migration v82 to ignore unsynced tags between database and git data; Add missing is_archived column on repository table (#6387) (#6403)
|
|
||||||
* Display correct error for invalid mirror interval (#6414) (#6429)
|
|
||||||
* Clean up ref name rules (#6437) (#6439)
|
|
||||||
* Fix Hook & HookList in Swagger (#6432) (#6440)
|
|
||||||
* Change order that PostProcess Processors are run (#6445) (#6447)
|
|
||||||
|
|
||||||
## [1.8.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.8.0-rc1) - 2019-03-18
|
|
||||||
* BREAKING
|
* BREAKING
|
||||||
* Add "ghost" and "notifications" to list of reserved user names. (#6208)
|
* Add "ghost" and "notifications" to list of reserved user names. (#6208)
|
||||||
* Change sqlite DB path default to data directory (#6198)
|
* Change sqlite DB path default to data directory (#6198)
|
||||||
@@ -110,7 +88,31 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Allow markdown table to scroll (#4401)
|
* Allow markdown table to scroll (#4401)
|
||||||
* Automatically clear stopwatch on merging a PR (#4327)
|
* Automatically clear stopwatch on merging a PR (#4327)
|
||||||
* Add the Owner Name to differentiate when merging (#3807)
|
* Add the Owner Name to differentiate when merging (#3807)
|
||||||
|
* Add title attributes to all items in the repo list viewer (#6258) (#6650)
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
|
* Fix dropdown icon padding (#6651) (#6654)
|
||||||
|
* Fix wrong GPG expire date (#6643) (#6644)
|
||||||
|
* Fix forking an empty repository (#6637) (#6653)
|
||||||
|
* Remove call to EscapePound .Link as it is already escaped (#6656) (#6666)
|
||||||
|
* Properly escape on the redirect from the web editor (#6657) (#6667)
|
||||||
|
* 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)
|
||||||
|
* Disable benchmarking during tag events on DroneIO (#6365) (#6366)
|
||||||
|
* Make sure units of a team are returned (#6379) (#6381)
|
||||||
|
* Don't Unescape redirect_to cookie value (#6399) (#6401)
|
||||||
|
* Fix dump table name error and add some test for dump database (#6394) (#6402)
|
||||||
|
* Fix migration v82 to ignore unsynced tags between database and git data; Add missing is_archived column on repository table (#6387) (#6403)
|
||||||
|
* Display correct error for invalid mirror interval (#6414) (#6429)
|
||||||
|
* Clean up ref name rules (#6437) (#6439)
|
||||||
|
* Fix Hook & HookList in Swagger (#6432) (#6440)
|
||||||
|
* Change order that PostProcess Processors are run (#6445) (#6447)
|
||||||
* Clean up various use of escape/unescape functions for URL generation (#6334)
|
* Clean up various use of escape/unescape functions for URL generation (#6334)
|
||||||
* Return 409 when creating repo if it already exists. (#6330)
|
* Return 409 when creating repo if it already exists. (#6330)
|
||||||
* Add same changes from issues page to milestone->issues page (#6328)
|
* Add same changes from issues page to milestone->issues page (#6328)
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -3,6 +3,7 @@ IMPORT := code.gitea.io/gitea
|
|||||||
|
|
||||||
GO ?= go
|
GO ?= go
|
||||||
SED_INPLACE := sed -i
|
SED_INPLACE := sed -i
|
||||||
|
SHASUM ?= shasum -a 256
|
||||||
|
|
||||||
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
||||||
|
|
||||||
@@ -148,7 +149,7 @@ misspell-check:
|
|||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||||
fi
|
fi
|
||||||
misspell -error -i unknwon $(GOFILES)
|
misspell -error -i unknwon,destory $(GOFILES)
|
||||||
|
|
||||||
.PHONY: misspell
|
.PHONY: misspell
|
||||||
misspell:
|
misspell:
|
||||||
@@ -327,7 +328,7 @@ release-windows:
|
|||||||
fi
|
fi
|
||||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
mv /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-linux
|
.PHONY: release-linux
|
||||||
@@ -337,7 +338,7 @@ release-linux:
|
|||||||
fi
|
fi
|
||||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out gitea-$(VERSION) .
|
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
mv /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-darwin
|
.PHONY: release-darwin
|
||||||
@@ -347,23 +348,23 @@ release-darwin:
|
|||||||
fi
|
fi
|
||||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
mv /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-copy
|
.PHONY: release-copy
|
||||||
release-copy:
|
release-copy:
|
||||||
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
|
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
|
||||||
|
|
||||||
.PHONY: release-check
|
.PHONY: release-check
|
||||||
release-check:
|
release-check:
|
||||||
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
|
||||||
|
|
||||||
.PHONY: release-compress
|
.PHONY: release-compress
|
||||||
release-compress:
|
release-compress:
|
||||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||||
fi
|
fi
|
||||||
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),gxz -k -9 $(notdir $(file));)
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||||
|
|
||||||
.PHONY: javascripts
|
.PHONY: javascripts
|
||||||
javascripts: public/js/index.js
|
javascripts: public/js/index.js
|
||||||
|
|||||||
@@ -106,3 +106,26 @@ func TestAPISudoUserForbidden(t *testing.T) {
|
|||||||
req := NewRequest(t, "GET", urlStr)
|
req := NewRequest(t, "GET", urlStr)
|
||||||
session.MakeRequest(t, req, http.StatusForbidden)
|
session.MakeRequest(t, req, http.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIListUsers(t *testing.T) {
|
||||||
|
prepareTestEnv(t)
|
||||||
|
adminUsername := "user1"
|
||||||
|
session := loginUser(t, adminUsername)
|
||||||
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
|
||||||
|
urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token)
|
||||||
|
req := NewRequest(t, "GET", urlStr)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
var users []api.User
|
||||||
|
DecodeJSON(t, resp, &users)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.UserName == adminUsername {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found)
|
||||||
|
numberOfUsers := models.GetCount(t, &models.User{}, "type = 0")
|
||||||
|
assert.Equal(t, numberOfUsers, len(users))
|
||||||
|
}
|
||||||
|
|||||||
115
integrations/create_no_session_test.go
Normal file
115
integrations/create_no_session_test.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
|
"github.com/go-macaron/session"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getSessionID(t *testing.T, resp *httptest.ResponseRecorder) string {
|
||||||
|
cookies := resp.Result().Cookies()
|
||||||
|
found := false
|
||||||
|
sessionID := ""
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
if cookie.Name == setting.SessionConfig.CookieName {
|
||||||
|
sessionID = cookie.Value
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found)
|
||||||
|
assert.NotEmpty(t, sessionID)
|
||||||
|
return sessionID
|
||||||
|
}
|
||||||
|
|
||||||
|
func sessionFile(tmpDir, sessionID string) string {
|
||||||
|
return filepath.Join(tmpDir, sessionID[0:1], sessionID[1:2], sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sessionFileExist(t *testing.T, tmpDir, sessionID string) bool {
|
||||||
|
sessionFile := sessionFile(tmpDir, sessionID)
|
||||||
|
_, err := os.Lstat(sessionFile)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionFileCreation(t *testing.T) {
|
||||||
|
prepareTestEnv(t)
|
||||||
|
|
||||||
|
oldSessionConfig := setting.SessionConfig.ProviderConfig
|
||||||
|
defer func() {
|
||||||
|
setting.SessionConfig.ProviderConfig = oldSessionConfig
|
||||||
|
mac = routes.NewMacaron()
|
||||||
|
routes.RegisterRoutes(mac)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var config session.Options
|
||||||
|
err := json.Unmarshal([]byte(oldSessionConfig), &config)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
config.Provider = "file"
|
||||||
|
|
||||||
|
// Now create a temporaryDirectory
|
||||||
|
tmpDir, err := ioutil.TempDir("", "sessions")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
if _, err := os.Stat(tmpDir); !os.IsNotExist(err) {
|
||||||
|
_ = os.RemoveAll(tmpDir)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
config.ProviderConfig = tmpDir
|
||||||
|
|
||||||
|
newConfigBytes, err := json.Marshal(config)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
setting.SessionConfig.ProviderConfig = string(newConfigBytes)
|
||||||
|
|
||||||
|
mac = routes.NewMacaron()
|
||||||
|
routes.RegisterRoutes(mac)
|
||||||
|
|
||||||
|
t.Run("NoSessionOnViewIssue", func(t *testing.T) {
|
||||||
|
req := NewRequest(t, "GET", "/user2/repo1/issues/1")
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
sessionID := getSessionID(t, resp)
|
||||||
|
|
||||||
|
// We're not logged in so there should be no session
|
||||||
|
assert.False(t, sessionFileExist(t, tmpDir, sessionID))
|
||||||
|
})
|
||||||
|
t.Run("CreateSessionOnLogin", func(t *testing.T) {
|
||||||
|
req := NewRequest(t, "GET", "/user/login")
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
sessionID := getSessionID(t, resp)
|
||||||
|
|
||||||
|
// We're not logged in so there should be no session
|
||||||
|
assert.False(t, sessionFileExist(t, tmpDir, sessionID))
|
||||||
|
|
||||||
|
doc := NewHTMLParser(t, resp.Body)
|
||||||
|
req = NewRequestWithValues(t, "POST", "/user/login", map[string]string{
|
||||||
|
"_csrf": doc.GetCSRF(),
|
||||||
|
"user_name": "user2",
|
||||||
|
"password": userPassword,
|
||||||
|
})
|
||||||
|
resp = MakeRequest(t, req, http.StatusFound)
|
||||||
|
sessionID = getSessionID(t, resp)
|
||||||
|
|
||||||
|
assert.FileExists(t, sessionFile(tmpDir, sessionID))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -75,7 +75,30 @@ func TestAccessTokenExchange(t *testing.T) {
|
|||||||
|
|
||||||
func TestAccessTokenExchangeWithoutPKCE(t *testing.T) {
|
func TestAccessTokenExchangeWithoutPKCE(t *testing.T) {
|
||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
|
req := NewRequestWithJSON(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))
|
||||||
|
assert.True(t, len(parsed.AccessToken) > 10)
|
||||||
|
assert.True(t, len(parsed.RefreshToken) > 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccessTokenExchangeJSON(t *testing.T) {
|
||||||
|
prepareTestEnv(t)
|
||||||
|
req := NewRequestWithJSON(t, "POST", "/login/oauth/access_token", map[string]string{
|
||||||
"grant_type": "authorization_code",
|
"grant_type": "authorization_code",
|
||||||
"client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
|
"client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
|
||||||
"client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
|
"client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
|
||||||
|
|||||||
@@ -164,10 +164,9 @@ func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, e
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//parseGPGKey parse a PrimaryKey entity (primary key + subs keys + self-signature)
|
//getExpiryTime extract the expire time of primary key based on sig
|
||||||
func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) {
|
func getExpiryTime(e *openpgp.Entity) time.Time {
|
||||||
pubkey := e.PrimaryKey
|
expiry := time.Time{}
|
||||||
|
|
||||||
//Extract self-sign for expire date based on : https://github.com/golang/crypto/blob/master/openpgp/keys.go#L165
|
//Extract self-sign for expire date based on : https://github.com/golang/crypto/blob/master/openpgp/keys.go#L165
|
||||||
var selfSig *packet.Signature
|
var selfSig *packet.Signature
|
||||||
for _, ident := range e.Identities {
|
for _, ident := range e.Identities {
|
||||||
@@ -178,10 +177,16 @@ func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expiry := time.Time{}
|
|
||||||
if selfSig.KeyLifetimeSecs != nil {
|
if selfSig.KeyLifetimeSecs != nil {
|
||||||
expiry = selfSig.CreationTime.Add(time.Duration(*selfSig.KeyLifetimeSecs) * time.Second)
|
expiry = e.PrimaryKey.CreationTime.Add(time.Duration(*selfSig.KeyLifetimeSecs) * time.Second)
|
||||||
}
|
}
|
||||||
|
return expiry
|
||||||
|
}
|
||||||
|
|
||||||
|
//parseGPGKey parse a PrimaryKey entity (primary key + subs keys + self-signature)
|
||||||
|
func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) {
|
||||||
|
pubkey := e.PrimaryKey
|
||||||
|
expiry := getExpiryTime(e)
|
||||||
|
|
||||||
//Parse Subkeys
|
//Parse Subkeys
|
||||||
subkeys := make([]*GPGKey, len(e.Subkeys))
|
subkeys := make([]*GPGKey, len(e.Subkeys))
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
@@ -225,3 +226,153 @@ Q0KHb+QcycSgbDx0ZAvdIacuKvBBcbxrsmFUI4LR+oIup0G9gUc0roPvr014jYQL
|
|||||||
assert.Equal(t, "user1@example.com", key.Emails[0].Email)
|
assert.Equal(t, "user1@example.com", key.Emails[0].Email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckGParseGPGExpire(t *testing.T) {
|
||||||
|
testIssue6599 := `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
epiDVQ==
|
||||||
|
=VSKJ
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
`
|
||||||
|
ekey, err := checkArmoredGPGKeyString(testIssue6599)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
expire := getExpiryTime(ekey)
|
||||||
|
assert.Equal(t, time.Unix(1586105389, 0), expire)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2432,6 +2432,7 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R
|
|||||||
Description: desc,
|
Description: desc,
|
||||||
DefaultBranch: oldRepo.DefaultBranch,
|
DefaultBranch: oldRepo.DefaultBranch,
|
||||||
IsPrivate: oldRepo.IsPrivate,
|
IsPrivate: oldRepo.IsPrivate,
|
||||||
|
IsEmpty: oldRepo.IsEmpty,
|
||||||
IsFork: true,
|
IsFork: true,
|
||||||
ForkID: oldRepo.ID,
|
ForkID: oldRepo.ID,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,9 +214,10 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
|
|||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ctx.Data["IsApiToken"] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["IsApiToken"] = true
|
|
||||||
return u, true
|
return u, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,15 +168,15 @@ func (f *GrantApplicationForm) Validate(ctx *macaron.Context, errs binding.Error
|
|||||||
|
|
||||||
// AccessTokenForm for issuing access tokens from authorization codes or refresh tokens
|
// AccessTokenForm for issuing access tokens from authorization codes or refresh tokens
|
||||||
type AccessTokenForm struct {
|
type AccessTokenForm struct {
|
||||||
GrantType string
|
GrantType string `json:"grant_type"`
|
||||||
ClientID string
|
ClientID string `json:"client_id"`
|
||||||
ClientSecret string
|
ClientSecret string `json:"client_secret"`
|
||||||
RedirectURI string
|
RedirectURI string `json:"redirect_uri"`
|
||||||
Code string
|
Code string `json:"code"`
|
||||||
RefreshToken string
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
|
||||||
// PKCE support
|
// PKCE support
|
||||||
CodeVerifier string
|
CodeVerifier string `json:"code_verifier"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate valideates the fields
|
// Validate valideates the fields
|
||||||
|
|||||||
@@ -110,6 +110,28 @@ func (ctx *APIContext) RequireCSRF() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckForOTP validateds OTP
|
||||||
|
func (ctx *APIContext) CheckForOTP() {
|
||||||
|
otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
|
||||||
|
twofa, err := models.GetTwoFactorByUID(ctx.Context.User.ID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrTwoFactorNotEnrolled(err) {
|
||||||
|
return // No 2FA enrollment for this user
|
||||||
|
}
|
||||||
|
ctx.Context.Error(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok, err := twofa.ValidateTOTP(otpHeader)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Context.Error(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
ctx.Context.Error(401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// APIContexter returns apicontext as macaron middleware
|
// APIContexter returns apicontext as macaron middleware
|
||||||
func APIContexter() macaron.Handler {
|
func APIContexter() macaron.Handler {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
"code.gitea.io/gitea/modules/auth"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -88,6 +90,28 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
|||||||
ctx.HTML(200, "user/auth/activate")
|
ctx.HTML(200, "user/auth/activate")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if ctx.IsSigned && auth.IsAPIPath(ctx.Req.URL.Path) && ctx.IsBasicAuth {
|
||||||
|
twofa, err := models.GetTwoFactorByUID(ctx.User.ID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrTwoFactorNotEnrolled(err) {
|
||||||
|
return // No 2FA enrollment for this user
|
||||||
|
}
|
||||||
|
ctx.Error(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
|
||||||
|
ok, err := twofa.ValidateTOTP(otpHeader)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "Only signed in user is allowed to call APIs.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
||||||
|
|||||||
@@ -396,6 +396,13 @@ func RepoAssignment() macaron.Handler {
|
|||||||
ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
|
ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if repo.IsFork {
|
||||||
|
RetrieveBaseRepo(ctx, repo)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// repo is empty and display enable
|
// repo is empty and display enable
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
||||||
@@ -423,13 +430,6 @@ func RepoAssignment() macaron.Handler {
|
|||||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||||
|
|
||||||
if repo.IsFork {
|
|
||||||
RetrieveBaseRepo(ctx, repo)
|
|
||||||
if ctx.Written() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// People who have push access or have forked repository can propose a new pull request.
|
// People who have push access or have forked repository can propose a new pull request.
|
||||||
if ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
|
if ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
|
||||||
// Pull request is allowed if this is a fork repository
|
// Pull request is allowed if this is a fork repository
|
||||||
|
|||||||
216
modules/session/memory.go
Normal file
216
modules/session/memory.go
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// Copyright 2013 Beego Authors
|
||||||
|
// Copyright 2014 The Macaron Authors
|
||||||
|
// Copyright 2019 The Gitea Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-macaron/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MemStore represents a in-memory session store implementation.
|
||||||
|
type MemStore struct {
|
||||||
|
sid string
|
||||||
|
lock sync.RWMutex
|
||||||
|
data map[interface{}]interface{}
|
||||||
|
lastAccess time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemStore creates and returns a memory session store.
|
||||||
|
func NewMemStore(sid string) *MemStore {
|
||||||
|
return &MemStore{
|
||||||
|
sid: sid,
|
||||||
|
data: make(map[interface{}]interface{}),
|
||||||
|
lastAccess: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets value to given key in session.
|
||||||
|
func (s *MemStore) Set(key, val interface{}) error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
s.data[key] = val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets value by given key in session.
|
||||||
|
func (s *MemStore) Get(key interface{}) interface{} {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
|
||||||
|
return s.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a key from session.
|
||||||
|
func (s *MemStore) Delete(key interface{}) error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
delete(s.data, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns current session ID.
|
||||||
|
func (s *MemStore) ID() string {
|
||||||
|
return s.sid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release releases resource and save data to provider.
|
||||||
|
func (*MemStore) Release() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush deletes all session data.
|
||||||
|
func (s *MemStore) Flush() error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
s.data = make(map[interface{}]interface{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemProvider represents a in-memory session provider implementation.
|
||||||
|
type MemProvider struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
maxLifetime int64
|
||||||
|
data map[string]*list.Element
|
||||||
|
// A priority list whose lastAccess newer gets higher priority.
|
||||||
|
list *list.List
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes memory session provider.
|
||||||
|
func (p *MemProvider) Init(maxLifetime int64, _ string) error {
|
||||||
|
p.lock.Lock()
|
||||||
|
p.maxLifetime = maxLifetime
|
||||||
|
p.lock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// update expands time of session store by given ID.
|
||||||
|
func (p *MemProvider) update(sid string) error {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
if e, ok := p.data[sid]; ok {
|
||||||
|
e.Value.(*MemStore).lastAccess = time.Now()
|
||||||
|
p.list.MoveToFront(e)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read returns raw session store by session ID.
|
||||||
|
func (p *MemProvider) Read(sid string) (_ session.RawStore, err error) {
|
||||||
|
p.lock.RLock()
|
||||||
|
e, ok := p.data[sid]
|
||||||
|
p.lock.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
if err = p.update(sid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e.Value.(*MemStore), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new session.
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
s := NewMemStore(sid)
|
||||||
|
p.data[sid] = p.list.PushBack(s)
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exist returns true if session with given ID exists.
|
||||||
|
func (p *MemProvider) Exist(sid string) bool {
|
||||||
|
p.lock.RLock()
|
||||||
|
defer p.lock.RUnlock()
|
||||||
|
|
||||||
|
_, ok := p.data[sid]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destory deletes a session by session ID.
|
||||||
|
func (p *MemProvider) Destory(sid string) error {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
e, ok := p.data[sid]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.list.Remove(e)
|
||||||
|
delete(p.data, sid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regenerate regenerates a session store from old session ID to new one.
|
||||||
|
func (p *MemProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
|
||||||
|
if p.Exist(sid) {
|
||||||
|
return nil, fmt.Errorf("new sid '%s' already exists", sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := p.Read(oldsid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = p.Destory(oldsid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.(*MemStore).sid = sid
|
||||||
|
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
p.data[sid] = p.list.PushBack(s)
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count counts and returns number of sessions.
|
||||||
|
func (p *MemProvider) Count() int {
|
||||||
|
return p.list.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC calls GC to clean expired sessions.
|
||||||
|
func (p *MemProvider) GC() {
|
||||||
|
p.lock.RLock()
|
||||||
|
for {
|
||||||
|
// No session in the list.
|
||||||
|
e := p.list.Back()
|
||||||
|
if e == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() {
|
||||||
|
p.lock.RUnlock()
|
||||||
|
p.lock.Lock()
|
||||||
|
p.list.Remove(e)
|
||||||
|
delete(p.data, e.Value.(*MemStore).sid)
|
||||||
|
p.lock.Unlock()
|
||||||
|
p.lock.RLock()
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.lock.RUnlock()
|
||||||
|
}
|
||||||
194
modules/session/virtual.go
Normal file
194
modules/session/virtual.go
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/go-macaron/session"
|
||||||
|
couchbase "github.com/go-macaron/session/couchbase"
|
||||||
|
memcache "github.com/go-macaron/session/memcache"
|
||||||
|
mysql "github.com/go-macaron/session/mysql"
|
||||||
|
nodb "github.com/go-macaron/session/nodb"
|
||||||
|
postgres "github.com/go-macaron/session/postgres"
|
||||||
|
redis "github.com/go-macaron/session/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VirtualSessionProvider represents a shadowed session provider implementation.
|
||||||
|
type VirtualSessionProvider struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
maxlifetime int64
|
||||||
|
rootPath string
|
||||||
|
provider session.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes the cookie session provider with given root path.
|
||||||
|
func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
|
||||||
|
var opts session.Options
|
||||||
|
if err := json.Unmarshal([]byte(config), &opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Note that these options are unprepared so we can't just use NewManager here.
|
||||||
|
// Nor can we access the provider map in session.
|
||||||
|
// So we will just have to do this by hand.
|
||||||
|
// This is only slightly more wrong than modules/setting/session.go:23
|
||||||
|
switch opts.Provider {
|
||||||
|
case "memory":
|
||||||
|
o.provider = &MemProvider{list: list.New(), data: make(map[string]*list.Element)}
|
||||||
|
case "file":
|
||||||
|
o.provider = &session.FileProvider{}
|
||||||
|
case "redis":
|
||||||
|
o.provider = &redis.RedisProvider{}
|
||||||
|
case "mysql":
|
||||||
|
o.provider = &mysql.MysqlProvider{}
|
||||||
|
case "postgres":
|
||||||
|
o.provider = &postgres.PostgresProvider{}
|
||||||
|
case "couchbase":
|
||||||
|
o.provider = &couchbase.CouchbaseProvider{}
|
||||||
|
case "memcache":
|
||||||
|
o.provider = &memcache.MemcacheProvider{}
|
||||||
|
case "nodb":
|
||||||
|
o.provider = &nodb.NodbProvider{}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
|
||||||
|
}
|
||||||
|
return o.provider.Init(gclifetime, opts.ProviderConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read returns raw session store by session ID.
|
||||||
|
func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
|
||||||
|
o.lock.RLock()
|
||||||
|
defer o.lock.RUnlock()
|
||||||
|
if o.provider.Exist(sid) {
|
||||||
|
return o.provider.Read(sid)
|
||||||
|
}
|
||||||
|
kv := make(map[interface{}]interface{})
|
||||||
|
kv["_old_uid"] = "0"
|
||||||
|
return NewVirtualStore(o, sid, kv), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exist returns true if session with given ID exists.
|
||||||
|
func (o *VirtualSessionProvider) Exist(sid string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destory deletes a session by session ID.
|
||||||
|
func (o *VirtualSessionProvider) Destory(sid string) error {
|
||||||
|
o.lock.Lock()
|
||||||
|
defer o.lock.Unlock()
|
||||||
|
return o.provider.Destory(sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regenerate regenerates a session store from old session ID to new one.
|
||||||
|
func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
|
||||||
|
o.lock.Lock()
|
||||||
|
defer o.lock.Unlock()
|
||||||
|
return o.provider.Regenerate(oldsid, sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count counts and returns number of sessions.
|
||||||
|
func (o *VirtualSessionProvider) Count() int {
|
||||||
|
o.lock.RLock()
|
||||||
|
defer o.lock.RUnlock()
|
||||||
|
return o.provider.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC calls GC to clean expired sessions.
|
||||||
|
func (o *VirtualSessionProvider) GC() {
|
||||||
|
o.provider.GC()
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
session.Register("VirtualSession", &VirtualSessionProvider{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualStore represents a virtual session store implementation.
|
||||||
|
type VirtualStore struct {
|
||||||
|
p *VirtualSessionProvider
|
||||||
|
sid string
|
||||||
|
lock sync.RWMutex
|
||||||
|
data map[interface{}]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVirtualStore creates and returns a virtual session store.
|
||||||
|
func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore {
|
||||||
|
return &VirtualStore{
|
||||||
|
p: p,
|
||||||
|
sid: sid,
|
||||||
|
data: kv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets value to given key in session.
|
||||||
|
func (s *VirtualStore) Set(key, val interface{}) error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
s.data[key] = val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets value by given key in session.
|
||||||
|
func (s *VirtualStore) Get(key interface{}) interface{} {
|
||||||
|
s.lock.RLock()
|
||||||
|
defer s.lock.RUnlock()
|
||||||
|
|
||||||
|
return s.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete delete a key from session.
|
||||||
|
func (s *VirtualStore) Delete(key interface{}) error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
delete(s.data, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns current session ID.
|
||||||
|
func (s *VirtualStore) ID() string {
|
||||||
|
return s.sid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release releases resource and save data to provider.
|
||||||
|
func (s *VirtualStore) Release() error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
// Now need to lock the provider
|
||||||
|
s.p.lock.Lock()
|
||||||
|
defer s.p.lock.Unlock()
|
||||||
|
if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) {
|
||||||
|
// Now ensure that we don't exist!
|
||||||
|
realProvider := s.p.provider
|
||||||
|
|
||||||
|
if realProvider.Exist(s.sid) {
|
||||||
|
// This is an error!
|
||||||
|
return fmt.Errorf("new sid '%s' already exists", s.sid)
|
||||||
|
}
|
||||||
|
realStore, err := realProvider.Read(s.sid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for key, value := range s.data {
|
||||||
|
if err := realStore.Set(key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realStore.Release()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush deletes all session data.
|
||||||
|
func (s *VirtualStore) Flush() error {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
s.data = make(map[interface{}]interface{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -5,11 +5,15 @@
|
|||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
// This ensures that VirtualSessionProvider is available
|
||||||
|
_ "code.gitea.io/gitea/modules/session"
|
||||||
|
|
||||||
"github.com/go-macaron/session"
|
"github.com/go-macaron/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,5 +35,12 @@ func newSessionService() {
|
|||||||
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
|
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
|
||||||
SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
|
SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
|
||||||
|
|
||||||
|
shadowConfig, err := json.Marshal(SessionConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(4, "Can't shadow session config: %v", err)
|
||||||
|
}
|
||||||
|
SessionConfig.ProviderConfig = string(shadowConfig)
|
||||||
|
SessionConfig.Provider = "VirtualSession"
|
||||||
|
|
||||||
log.Info("Session Service Enabled")
|
log.Info("Session Service Enabled")
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -129,6 +129,7 @@
|
|||||||
.dropdown {
|
.dropdown {
|
||||||
.dropdown.icon {
|
.dropdown.icon {
|
||||||
margin-top: -7px!important;
|
margin-top: -7px!important;
|
||||||
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
.text {
|
.text {
|
||||||
margin-right: 0!important;
|
margin-right: 0!important;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||||
"code.gitea.io/gitea/routers/api/v1/user"
|
"code.gitea.io/gitea/routers/api/v1/user"
|
||||||
api "code.gitea.io/sdk/gitea"
|
api "code.gitea.io/sdk/gitea"
|
||||||
)
|
)
|
||||||
@@ -319,8 +320,14 @@ func GetAllUsers(ctx *context.APIContext) {
|
|||||||
PageSize: -1,
|
PageSize: -1,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "SearchUsers", err)
|
ctx.Error(500, "GetAllUsers", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.JSON(200, &users)
|
|
||||||
|
results := make([]*api.User, len(users))
|
||||||
|
for i := range users {
|
||||||
|
results[i] = convert.ToUser(users[i], ctx.IsSigned, ctx.User.IsAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, &results)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,10 @@ func reqToken() macaron.Handler {
|
|||||||
if true == ctx.Data["IsApiToken"] {
|
if true == ctx.Data["IsApiToken"] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if ctx.Context.IsBasicAuth {
|
||||||
|
ctx.CheckForOTP()
|
||||||
|
return
|
||||||
|
}
|
||||||
if ctx.IsSigned {
|
if ctx.IsSigned {
|
||||||
ctx.RequireCSRF()
|
ctx.RequireCSRF()
|
||||||
return
|
return
|
||||||
@@ -181,11 +185,12 @@ func reqToken() macaron.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reqBasicAuth() macaron.Handler {
|
func reqBasicAuth() macaron.Handler {
|
||||||
return func(ctx *context.Context) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsBasicAuth {
|
if !ctx.Context.IsBasicAuth {
|
||||||
ctx.Error(401)
|
ctx.Context.Error(401)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ctx.CheckForOTP()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"code.gitea.io/git"
|
"code.gitea.io/git"
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -217,3 +218,18 @@ func ToTeam(team *models.Team) *api.Team {
|
|||||||
Units: team.GetUnitNames(),
|
Units: team.GetUnitNames(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToUser convert models.User to api.User
|
||||||
|
func ToUser(user *models.User, signed, admin bool) *api.User {
|
||||||
|
result := &api.User{
|
||||||
|
ID: user.ID,
|
||||||
|
UserName: user.Name,
|
||||||
|
AvatarURL: user.AvatarLink(),
|
||||||
|
FullName: markup.Sanitize(user.FullName),
|
||||||
|
IsAdmin: user.IsAdmin,
|
||||||
|
}
|
||||||
|
if signed && (!user.KeepEmailPrivate || admin) {
|
||||||
|
result.Email = user.Email
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||||
api "code.gitea.io/sdk/gitea"
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
@@ -67,16 +67,7 @@ func Search(ctx *context.APIContext) {
|
|||||||
|
|
||||||
results := make([]*api.User, len(users))
|
results := make([]*api.User, len(users))
|
||||||
for i := range users {
|
for i := range users {
|
||||||
results[i] = &api.User{
|
results[i] = convert.ToUser(users[i], ctx.IsSigned, ctx.User.IsAdmin)
|
||||||
ID: users[i].ID,
|
|
||||||
UserName: users[i].Name,
|
|
||||||
AvatarURL: users[i].AvatarLink(),
|
|
||||||
FullName: markup.Sanitize(users[i].FullName),
|
|
||||||
IsAdmin: users[i].IsAdmin,
|
|
||||||
}
|
|
||||||
if ctx.IsSigned && (!users[i].KeepEmailPrivate || ctx.User.IsAdmin) {
|
|
||||||
results[i].Email = users[i].Email
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, map[string]interface{}{
|
ctx.JSON(200, map[string]interface{}{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/uploader"
|
"code.gitea.io/gitea/modules/uploader"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -66,9 +67,9 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
|||||||
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
||||||
if treePath != ctx.Repo.TreePath {
|
if treePath != ctx.Repo.TreePath {
|
||||||
if isNewFile {
|
if isNewFile {
|
||||||
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_new", ctx.Repo.BranchName, treePath))
|
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_new", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
|
||||||
} else {
|
} else {
|
||||||
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_edit", ctx.Repo.BranchName, treePath))
|
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_edit", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -324,7 +325,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(form.TreePath))
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditFilePost response for editing file
|
// EditFilePost response for editing file
|
||||||
@@ -376,7 +377,7 @@ func DeleteFile(ctx *context.Context) {
|
|||||||
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
||||||
|
|
||||||
if treePath != ctx.Repo.TreePath {
|
if treePath != ctx.Repo.TreePath {
|
||||||
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_delete", ctx.Repo.BranchName, treePath))
|
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_delete", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +461,7 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath))
|
ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath))
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName)
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderUploadSettings(ctx *context.Context) {
|
func renderUploadSettings(ctx *context.Context) {
|
||||||
@@ -477,7 +478,7 @@ func UploadFile(ctx *context.Context) {
|
|||||||
canCommit := renderCommitRights(ctx)
|
canCommit := renderCommitRights(ctx)
|
||||||
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
treePath := cleanUploadFileName(ctx.Repo.TreePath)
|
||||||
if treePath != ctx.Repo.TreePath {
|
if treePath != ctx.Repo.TreePath {
|
||||||
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_upload", ctx.Repo.BranchName, treePath))
|
ctx.Redirect(path.Join(ctx.Repo.RepoLink, "_upload", util.PathEscapeSegments(ctx.Repo.BranchName), util.PathEscapeSegments(treePath)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Repo.TreePath = treePath
|
ctx.Repo.TreePath = treePath
|
||||||
@@ -596,7 +597,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath)
|
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanUploadFileName(name string) string {
|
func cleanUploadFileName(name string) string {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<form class="ui comment form stackable grid" action="{{EscapePound .Link}}" method="post">
|
<form class="ui comment form stackable grid" action="{{.Link}}" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
{{if .Flash}}
|
{{if .Flash}}
|
||||||
<div class="sixteen wide column">
|
<div class="sixteen wide column">
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
{{$subJumpablePathName := $entry.GetSubJumpablePathName}}
|
{{$subJumpablePathName := $entry.GetSubJumpablePathName}}
|
||||||
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}}
|
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}}
|
||||||
<span class="octicon octicon-file-directory"></span>
|
<span class="octicon octicon-file-directory"></span>
|
||||||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $subJumpablePathName}}">
|
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $subJumpablePathName}}" title="{{$subJumpablePathName}}">
|
||||||
{{if eq (len $subJumpablePath) 2}}
|
{{if eq (len $subJumpablePath) 2}}
|
||||||
<span class="jumpable-path">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}}
|
<span class="jumpable-path">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}}
|
||||||
{{else}}
|
{{else}}
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="octicon octicon-{{EntryIcon $entry}}"></span>
|
<span class="octicon octicon-{{EntryIcon $entry}}"></span>
|
||||||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}">{{$entry.Name}}</a>
|
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}" title="{{$entry.Name}}">{{$entry.Name}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Reference in New Issue
Block a user