Skip to content

Commit d8bab42

Browse files
authored
Merge branch 'main' into update-actions-route
2 parents 60c8f42 + 2ce7162 commit d8bab42

16 files changed

Lines changed: 310 additions & 180 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ require (
174174
github.com/caddyserver/zerossl v0.1.4 // indirect
175175
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
176176
github.com/cespare/xxhash/v2 v2.3.0 // indirect
177-
github.com/cloudflare/circl v1.6.1 // indirect
177+
github.com/cloudflare/circl v1.6.3 // indirect
178178
github.com/couchbase/go-couchbase v0.1.1 // indirect
179179
github.com/couchbase/gomemcached v0.3.3 // indirect
180180
github.com/couchbase/goutils v0.1.2 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
227227
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
228228
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
229229
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
230-
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
231-
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
230+
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
231+
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
232232
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
233233
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
234234
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=

models/auth/oauth2.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"net/url"
1515
"slices"
1616
"strings"
17+
"time"
1718

1819
"code.gitea.io/gitea/models/db"
1920
"code.gitea.io/gitea/modules/container"
@@ -27,6 +28,11 @@ import (
2728
"xorm.io/xorm"
2829
)
2930

31+
// Authorization codes should expire within 10 minutes per https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2
32+
const oauth2AuthorizationCodeValidity = 10 * time.Minute
33+
34+
var ErrOAuth2AuthorizationCodeInvalidated = errors.New("oauth2 authorization code already invalidated")
35+
3036
// OAuth2Application represents an OAuth2 client (RFC 6749)
3137
type OAuth2Application struct {
3238
ID int64 `xorm:"pk autoincr"`
@@ -386,6 +392,14 @@ func (code *OAuth2AuthorizationCode) TableName() string {
386392
return "oauth2_authorization_code"
387393
}
388394

395+
// IsExpired reports whether the authorization code is expired.
396+
func (code *OAuth2AuthorizationCode) IsExpired() bool {
397+
if code.ValidUntil.IsZero() {
398+
return true
399+
}
400+
return code.ValidUntil <= timeutil.TimeStampNow()
401+
}
402+
389403
// GenerateRedirectURI generates a redirect URI for a successful authorization request. State will be used if not empty.
390404
func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL, error) {
391405
redirect, err := url.Parse(code.RedirectURI)
@@ -403,8 +417,14 @@ func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL
403417

404418
// Invalidate deletes the auth code from the database to invalidate this code
405419
func (code *OAuth2AuthorizationCode) Invalidate(ctx context.Context) error {
406-
_, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code)
407-
return err
420+
affected, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code)
421+
if err != nil {
422+
return err
423+
}
424+
if affected == 0 {
425+
return ErrOAuth2AuthorizationCodeInvalidated
426+
}
427+
return nil
408428
}
409429

410430
// ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE implementation.
@@ -472,13 +492,15 @@ func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redi
472492
// for code scanners to grab sensitive tokens.
473493
codeSecret := "gta_" + base32Lower.EncodeToString(rBytes)
474494

495+
validUntil := time.Now().Add(oauth2AuthorizationCodeValidity)
475496
code = &OAuth2AuthorizationCode{
476497
Grant: grant,
477498
GrantID: grant.ID,
478499
RedirectURI: redirectURI,
479500
Code: codeSecret,
480501
CodeChallenge: codeChallenge,
481502
CodeChallengeMethod: codeChallengeMethod,
503+
ValidUntil: timeutil.TimeStamp(validUntil.Unix()),
482504
}
483505
if err := db.Insert(ctx, code); err != nil {
484506
return nil, err

models/auth/oauth2_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,45 @@ package auth_test
55

66
import (
77
"testing"
8+
"time"
89

910
auth_model "code.gitea.io/gitea/models/auth"
1011
"code.gitea.io/gitea/models/unittest"
12+
"code.gitea.io/gitea/modules/timeutil"
1113

1214
"github.com/stretchr/testify/assert"
1315
)
1416

17+
func TestOAuth2AuthorizationCodeValidity(t *testing.T) {
18+
assert.NoError(t, unittest.PrepareTestDatabase())
19+
20+
t.Run("GenerateSetsValidUntil", func(t *testing.T) {
21+
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1})
22+
expectedValidUntil := timeutil.TimeStamp(time.Now().Unix() + 600)
23+
code, err := grant.GenerateNewAuthorizationCode(t.Context(), "http://127.0.0.1/", "", "")
24+
assert.NoError(t, err)
25+
assert.Equal(t, expectedValidUntil, code.ValidUntil)
26+
assert.False(t, code.IsExpired())
27+
assert.NoError(t, code.Invalidate(t.Context()))
28+
})
29+
30+
t.Run("Expired", func(t *testing.T) {
31+
defer timeutil.MockSet(time.Unix(2, 0).UTC())()
32+
33+
code := &auth_model.OAuth2AuthorizationCode{ValidUntil: timeutil.TimeStamp(1)}
34+
assert.True(t, code.IsExpired())
35+
})
36+
37+
t.Run("InvalidateTwice", func(t *testing.T) {
38+
code, err := auth_model.GetOAuth2AuthorizationByCode(t.Context(), "authcode")
39+
assert.NoError(t, err)
40+
if assert.NotNil(t, code) {
41+
assert.NoError(t, code.Invalidate(t.Context()))
42+
assert.ErrorIs(t, code.Invalidate(t.Context()), auth_model.ErrOAuth2AuthorizationCodeInvalidated)
43+
}
44+
})
45+
}
46+
1547
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
1648
assert.NoError(t, unittest.PrepareTestDatabase())
1749
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})

models/dbfs/dbfile.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (f *file) readAt(fileMeta *dbfsMeta, offset int64, p []byte) (n int, err er
7575
}
7676

7777
func (f *file) Read(p []byte) (n int, err error) {
78-
if f.metaID == 0 || !f.allowRead {
78+
if !f.allowRead {
7979
return 0, os.ErrInvalid
8080
}
8181

@@ -89,7 +89,7 @@ func (f *file) Read(p []byte) (n int, err error) {
8989
}
9090

9191
func (f *file) Write(p []byte) (n int, err error) {
92-
if f.metaID == 0 || !f.allowWrite {
92+
if !f.allowWrite {
9393
return 0, os.ErrInvalid
9494
}
9595

@@ -184,10 +184,6 @@ func (f *file) Close() error {
184184
}
185185

186186
func (f *file) Stat() (os.FileInfo, error) {
187-
if f.metaID == 0 {
188-
return nil, os.ErrInvalid
189-
}
190-
191187
fileMeta, err := findFileMetaByID(f.ctx, f.metaID)
192188
if err != nil {
193189
return nil, err
@@ -232,15 +228,17 @@ func (f *file) open(flag int) (err error) {
232228
if f.metaID != 0 {
233229
return os.ErrExist
234230
}
235-
} else {
236-
// create a new file if none exists.
237-
if f.metaID == 0 {
238-
if err = f.createEmpty(); err != nil {
239-
return err
240-
}
231+
}
232+
// create a new file if not exists.
233+
if f.metaID == 0 {
234+
if err = f.createEmpty(); err != nil {
235+
return err
241236
}
242237
}
243238
}
239+
if f.metaID == 0 {
240+
return os.ErrNotExist
241+
}
244242
if flag&os.O_TRUNC != 0 {
245243
if err = f.truncate(); err != nil {
246244
return err
@@ -252,7 +250,7 @@ func (f *file) open(flag int) (err error) {
252250
}
253251
}
254252
return nil
255-
}
253+
} // end if: allowWrite
256254

257255
// read only mode
258256
if f.metaID == 0 {
@@ -322,9 +320,6 @@ func (f *file) delete() error {
322320
}
323321

324322
func (f *file) size() (int64, error) {
325-
if f.metaID == 0 {
326-
return 0, os.ErrNotExist
327-
}
328323
fileMeta, err := findFileMetaByID(f.ctx, f.metaID)
329324
if err != nil {
330325
return 0, err
@@ -339,7 +334,7 @@ func findFileMetaByID(ctx context.Context, metaID int64) (*dbfsMeta, error) {
339334
} else if ok {
340335
return &fileMeta, nil
341336
}
342-
return nil, nil //nolint:nilnil // return nil to indicate that the object does not exist
337+
return nil, os.ErrNotExist
343338
}
344339

345340
func buildPath(path string) string {

models/dbfs/dbfs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ The DBFS solution:
4040
* In the future, when Gitea action needs to limit the log size (other CI/CD services also do so), it's easier to calculate the log file size.
4141
* Even sometimes the UI needs to render the tailing lines, the tailing lines can be found be counting the "\n" from the end of the file by seek.
4242
The seeking and finding is not the fastest way, but it's still acceptable and won't affect the performance too much.
43+
44+
Limitations of the DBFS solution:
45+
* Not fully POSIX-compliant, some behaviors may be different from the real filesystem, especially for concurrent read/write
4346
*/
4447

4548
type dbfsMeta struct {

models/dbfs/dbfs_test.go

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,14 @@ import (
99
"os"
1010
"testing"
1111

12+
"code.gitea.io/gitea/modules/test"
13+
1214
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
1316
)
1417

15-
func changeDefaultFileBlockSize(n int64) (restore func()) {
16-
old := defaultFileBlockSize
17-
defaultFileBlockSize = n
18-
return func() {
19-
defaultFileBlockSize = old
20-
}
21-
}
22-
2318
func TestDbfsBasic(t *testing.T) {
24-
defer changeDefaultFileBlockSize(4)()
19+
defer test.MockVariableValue(&defaultFileBlockSize, 4)()
2520

2621
// test basic write/read
2722
f, err := OpenFile(t.Context(), "test.txt", os.O_RDWR|os.O_CREATE)
@@ -122,10 +117,55 @@ func TestDbfsBasic(t *testing.T) {
122117
stat, err = f.Stat()
123118
assert.NoError(t, err)
124119
assert.EqualValues(t, 10, stat.Size())
120+
121+
t.Run("NonExisting", func(t *testing.T) {
122+
f, err := OpenFile(t.Context(), "non-existing.txt", os.O_RDONLY)
123+
assert.ErrorIs(t, err, os.ErrNotExist)
124+
assert.Nil(t, f)
125+
126+
f, err = OpenFile(t.Context(), "non-existing.txt", os.O_WRONLY)
127+
assert.ErrorIs(t, err, os.ErrNotExist)
128+
assert.Nil(t, f)
129+
130+
f, err = OpenFile(t.Context(), "non-existing.txt", os.O_WRONLY|os.O_APPEND|os.O_TRUNC)
131+
assert.ErrorIs(t, err, os.ErrNotExist)
132+
assert.Nil(t, f)
133+
})
134+
135+
t.Run("Existing", func(t *testing.T) {
136+
assertFileContent := func(f File, expected string) {
137+
_, err := f.Seek(0, io.SeekStart)
138+
require.NoError(t, err)
139+
buf, err := io.ReadAll(f)
140+
require.NoError(t, err)
141+
assert.Equal(t, expected, string(buf))
142+
}
143+
144+
f, err := OpenFile(t.Context(), "existing.txt", os.O_RDWR|os.O_CREATE)
145+
require.NoError(t, err)
146+
_, _ = f.Write([]byte("test"))
147+
assertFileContent(f, "test")
148+
assert.NoError(t, f.Close())
149+
150+
f, err = OpenFile(t.Context(), "existing.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND)
151+
require.NoError(t, err)
152+
_, _ = f.Write([]byte("\nnew"))
153+
assertFileContent(f, "test\nnew")
154+
assert.NoError(t, f.Close())
155+
156+
f, err = OpenFile(t.Context(), "existing.txt", os.O_RDWR|os.O_TRUNC)
157+
require.NoError(t, err)
158+
assertFileContent(f, "")
159+
assert.NoError(t, f.Close())
160+
161+
f, err = OpenFile(t.Context(), "existing.txt", os.O_RDWR|os.O_CREATE|os.O_EXCL)
162+
assert.ErrorIs(t, err, os.ErrExist)
163+
assert.Nil(t, f)
164+
})
125165
}
126166

127167
func TestDbfsReadWrite(t *testing.T) {
128-
defer changeDefaultFileBlockSize(4)()
168+
defer test.MockVariableValue(&defaultFileBlockSize, 4)()
129169

130170
f1, err := OpenFile(t.Context(), "test.log", os.O_RDWR|os.O_CREATE)
131171
assert.NoError(t, err)
@@ -157,30 +197,32 @@ func TestDbfsReadWrite(t *testing.T) {
157197
}
158198

159199
func TestDbfsSeekWrite(t *testing.T) {
160-
defer changeDefaultFileBlockSize(4)()
200+
defer test.MockVariableValue(&defaultFileBlockSize, 4)()
161201

162-
f, err := OpenFile(t.Context(), "test2.log", os.O_RDWR|os.O_CREATE)
163-
assert.NoError(t, err)
164-
defer f.Close()
202+
// write something
203+
fw, err := OpenFile(t.Context(), "test2.log", os.O_RDWR|os.O_CREATE)
204+
require.NoError(t, err)
205+
defer fw.Close()
165206

166-
n, err := f.Write([]byte("111"))
207+
n, err := fw.Write([]byte("111"))
167208
assert.NoError(t, err)
168209

169-
_, err = f.Seek(int64(n), io.SeekStart)
210+
_, err = fw.Seek(int64(n), io.SeekStart)
170211
assert.NoError(t, err)
171212

172-
_, err = f.Write([]byte("222"))
213+
_, err = fw.Write([]byte("222"))
173214
assert.NoError(t, err)
174215

175-
_, err = f.Seek(int64(n), io.SeekStart)
216+
_, err = fw.Seek(int64(n), io.SeekStart)
176217
assert.NoError(t, err)
177218

178-
_, err = f.Write([]byte("333"))
219+
_, err = fw.Write([]byte("333"))
179220
assert.NoError(t, err)
180221

222+
// then read it
181223
fr, err := OpenFile(t.Context(), "test2.log", os.O_RDONLY)
182-
assert.NoError(t, err)
183-
defer f.Close()
224+
require.NoError(t, err)
225+
defer fr.Close()
184226

185227
buf, err := io.ReadAll(fr)
186228
assert.NoError(t, err)

0 commit comments

Comments
 (0)