diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 0d67df41c2..a32871e0eb 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -135,7 +135,7 @@ type CreateRepoOption struct { // Whether the repository is private Private bool `json:"private"` // Label-Set to use - IssueLabels string `json:"issue_labels"` + IssueLabels string `json:"issue_labels" binding:"MaxSize(255)"` // Whether the repository should be auto-initialized? AutoInit bool `json:"auto_init"` // Whether the repository is template @@ -143,15 +143,15 @@ type CreateRepoOption struct { // Gitignores to use Gitignores string `json:"gitignores"` // License to use - License string `json:"license"` + License string `json:"license" binding:"MaxSize(100)"` // Readme of the repository to create - Readme string `json:"readme"` + Readme string `json:"readme" binding:"MaxSize(255)"` // DefaultBranch of the repository (used when initializes and in template) DefaultBranch string `json:"default_branch" binding:"GitRefName;MaxSize(100)"` // TrustModel of the repository // enum: default,collaborator,committer,collaboratorcommitter TrustModel string `json:"trust_model"` - // ObjectFormatName of the underlying git repository + // ObjectFormatName of the underlying git repository, empty string for default (sha1) // enum: sha1,sha256 ObjectFormatName string `json:"object_format_name" binding:"MaxSize(6)"` } diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 4f17590abd..ff43628fa5 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -10,7 +10,6 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/git" @@ -22,26 +21,19 @@ import ( release_service "code.gitea.io/gitea/services/release" ) -func hasRepoWriteScope(ctx *context.APIContext) bool { - scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) - if ctx.Data["IsApiToken"] != true || !ok { - return true - } - - requiredScopes := auth_model.GetRequiredScopes(auth_model.Write, auth_model.AccessTokenScopeCategoryRepository) - allow, err := scope.HasScope(requiredScopes...) - if err != nil { - ctx.APIError(http.StatusForbidden, "checking scope failed: "+err.Error()) - return false - } - return allow -} - -func canAccessDraftRelease(ctx *context.APIContext) bool { +func canAccessReleaseDraft(ctx *context.APIContext) bool { if !ctx.IsSigned || !ctx.Repo.CanWrite(unit.TypeReleases) { return false } - return hasRepoWriteScope(ctx) + if ctx.Data["IsApiToken"] != true { + // not API token request, the request is from a user session with write access + return true + } + // the request is from an access token with scope + scope := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) + requiredScopes := auth_model.GetRequiredScopes(auth_model.Write, auth_model.AccessTokenScopeCategoryRepository) + allow, _ := scope.HasScope(requiredScopes...) // err (invalid token) can be safely ignored + return allow } // GetRelease get a single release of a repository @@ -85,13 +77,9 @@ func GetRelease(ctx *context.APIContext) { return } - if release.IsDraft { // only the users with write access can see draft releases - if !canAccessDraftRelease(ctx) { - if !ctx.Written() { - ctx.APIErrorNotFound() - } - return - } + if release.IsDraft && !canAccessReleaseDraft(ctx) { // only the users with write access can see draft releases + ctx.APIErrorNotFound() + return } if err := release.LoadAttributes(ctx); err != nil { @@ -182,14 +170,12 @@ func ListReleases(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" listOptions := utils.GetListOptions(ctx) - - includeDrafts := (ctx.Repo.AccessMode >= perm.AccessModeWrite || ctx.Repo.UnitAccessMode(unit.TypeReleases) >= perm.AccessModeWrite) && hasRepoWriteScope(ctx) if ctx.Written() { return } opts := repo_model.FindReleasesOptions{ ListOptions: listOptions, - IncludeDrafts: includeDrafts, + IncludeDrafts: canAccessReleaseDraft(ctx), IncludeTags: false, IsDraft: ctx.FormOptionalBool("draft"), IsPreRelease: ctx.FormOptionalBool("pre-release"), diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 6b30070db8..19075961f3 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -34,13 +34,9 @@ func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool { ctx.APIErrorNotFound() return false } - if release.IsDraft { - if !canAccessDraftRelease(ctx) { - if !ctx.Written() { - ctx.APIErrorNotFound() - } - return false - } + if release.IsDraft && !canAccessReleaseDraft(ctx) { + ctx.APIErrorNotFound() + return false } return true } @@ -149,13 +145,9 @@ func ListReleaseAttachments(ctx *context.APIContext) { ctx.APIErrorNotFound() return } - if release.IsDraft { - if !canAccessDraftRelease(ctx) { - if !ctx.Written() { - ctx.APIErrorNotFound() - } - return - } + if release.IsDraft && !canAccessReleaseDraft(ctx) { + ctx.APIErrorNotFound() + return } if err := release.LoadAttributes(ctx); err != nil { ctx.APIErrorInternal(err) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 765a723968..8b69c6bcc6 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -27,9 +27,9 @@ type CreateRepoForm struct { DefaultBranch string `binding:"GitRefName;MaxSize(100)"` AutoInit bool Gitignores string - IssueLabels string - License string - Readme string + IssueLabels string `binding:"MaxSize(255)"` + License string `binding:"MaxSize(100)"` + Readme string `binding:"MaxSize(255)"` Template bool RepoTemplate int64 @@ -41,7 +41,7 @@ type CreateRepoForm struct { Labels bool ProtectedBranch bool - ForkSingleBranch string + ForkSingleBranch string `binding:"MaxSize(255)"` ObjectFormatName string } diff --git a/services/repository/create.go b/services/repository/create.go index cbdc9cca76..e027d3b979 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -230,6 +230,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, if opts.ObjectFormatName == "" { opts.ObjectFormatName = git.Sha1ObjectFormat.Name() } + if opts.ObjectFormatName != git.Sha1ObjectFormat.Name() && opts.ObjectFormatName != git.Sha256ObjectFormat.Name() { + return nil, fmt.Errorf("unsupported object format: %s", opts.ObjectFormatName) + } repo := &repo_model.Repository{ OwnerID: owner.ID, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 570747ca57..a1ecc7fb4f 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -23780,7 +23780,7 @@ "x-go-name": "Name" }, "object_format_name": { - "description": "ObjectFormatName of the underlying git repository", + "description": "ObjectFormatName of the underlying git repository, empty string for default (sha1)", "type": "string", "enum": [ "sha1", diff --git a/tests/integration/api_releases_attachment_test.go b/tests/integration/api_releases_attachment_test.go index e859b23c72..3f2592e331 100644 --- a/tests/integration/api_releases_attachment_test.go +++ b/tests/integration/api_releases_attachment_test.go @@ -15,16 +15,13 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) -func TestAPIEditReleaseAttachmentWithUnallowedFile(t *testing.T) { +func testAPIEditReleaseAttachmentWithUnallowedFile(t *testing.T) { // Limit the allowed release types (since by default there is no restriction) defer test.MockVariableValue(&setting.Repository.Release.AllowedTypes, ".exe")() - defer tests.PrepareTestEnv(t)() - attachment := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: 9}) release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{ID: attachment.ReleaseID}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: attachment.RepoID}) @@ -42,9 +39,7 @@ func TestAPIEditReleaseAttachmentWithUnallowedFile(t *testing.T) { session.MakeRequest(t, req, http.StatusUnprocessableEntity) } -func TestAPIDraftReleaseAttachmentAccess(t *testing.T) { - defer tests.PrepareTestEnv(t)() - +func testAPIDraftReleaseAttachmentAccess(t *testing.T) { attachment := unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{ID: 13}) release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{ID: attachment.ReleaseID}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: attachment.RepoID}) diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index c7200129dd..c7f1343dde 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -29,9 +29,19 @@ import ( "github.com/stretchr/testify/assert" ) -func TestAPIListReleasesWithWriteToken(t *testing.T) { +func TestAPIReleaseRead(t *testing.T) { defer tests.PrepareTestEnv(t)() + t.Run("DraftReleaseAttachmentAccess", testAPIDraftReleaseAttachmentAccess) + t.Run("ListReleasesWithWriteToken", testAPIListReleasesWithWriteToken) + t.Run("ListReleasesWithReadToken", testAPIListReleasesWithReadToken) + t.Run("GetDraftRelease", testAPIGetDraftRelease) + t.Run("GetLatestRelease", testAPIGetLatestRelease) + t.Run("GetReleaseByTag", testAPIGetReleaseByTag) + t.Run("GetDraftReleaseByTag", testAPIGetDraftReleaseByTag) + t.Run("EditReleaseAttachmentWithUnallowedFile", testAPIEditReleaseAttachmentWithUnallowedFile) // failed attempt, so it is also a read test +} +func testAPIListReleasesWithWriteToken(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeWriteRepository) @@ -81,9 +91,7 @@ func TestAPIListReleasesWithWriteToken(t *testing.T) { testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft") } -func TestAPIListReleasesWithReadToken(t *testing.T) { - defer tests.PrepareTestEnv(t)() - +func testAPIListReleasesWithReadToken(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeReadRepository) @@ -129,9 +137,7 @@ func TestAPIListReleasesWithReadToken(t *testing.T) { testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft") } -func TestAPIGetDraftRelease(t *testing.T) { - defer tests.PrepareTestEnv(t)() - +func testAPIGetDraftRelease(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{ID: 4}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) @@ -300,9 +306,7 @@ func TestAPICreateReleaseGivenInvalidTarget(t *testing.T) { MakeRequest(t, req, http.StatusNotFound) } -func TestAPIGetLatestRelease(t *testing.T) { - defer tests.PrepareTestEnv(t)() - +func testAPIGetLatestRelease(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) @@ -315,9 +319,7 @@ func TestAPIGetLatestRelease(t *testing.T) { assert.Equal(t, "testing-release", release.Title) } -func TestAPIGetReleaseByTag(t *testing.T) { - defer tests.PrepareTestEnv(t)() - +func testAPIGetReleaseByTag(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) @@ -341,9 +343,7 @@ func TestAPIGetReleaseByTag(t *testing.T) { assert.NotEmpty(t, err.Message) } -func TestAPIGetDraftReleaseByTag(t *testing.T) { - defer tests.PrepareTestEnv(t)() - +func testAPIGetDraftReleaseByTag(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) diff --git a/web_src/css/base.css b/web_src/css/base.css index 5a75aaaee6..3fa5c1246c 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -42,7 +42,7 @@ --gap-inline: 0.25rem; /* gap for inline texts and elements, for example: the spaces for sentence with labels, button text, etc */ --gap-block: 0.5rem; /* gap for element blocks, for example: spaces between buttons, menu image & title, header icon & title etc */ - --background-view-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAG0lEQVQYlWN4+vTpf3SMDTAMBYXYBLFpHgoKAeiOf0SGE9kbAAAAAElFTkSuQmCC") right bottom var(--color-primary-light-7); + --background-view-image: repeating-conic-gradient(var(--color-transparency-grid-dark) 0 25%, var(--color-transparency-grid-light) 0 50%) 0 0 / 18px 18px; } @media (min-width: 768px) and (max-width: 1200px) { diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css index f58c222c9a..ad5eec9e82 100644 --- a/web_src/css/themes/theme-gitea-dark.css +++ b/web_src/css/themes/theme-gitea-dark.css @@ -247,6 +247,8 @@ gitea-theme-meta-info { --color-highlight-bg: #352c1c; --color-overlay-backdrop: #080808c0; --color-danger: var(--color-red); + --color-transparency-grid-light: #2a2a2a; + --color-transparency-grid-dark: #1a1a1a; accent-color: var(--color-accent); color-scheme: dark; } diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css index 8766bf7abc..049b64f73f 100644 --- a/web_src/css/themes/theme-gitea-light.css +++ b/web_src/css/themes/theme-gitea-light.css @@ -247,6 +247,8 @@ gitea-theme-meta-info { --color-highlight-bg: #fffbdd; --color-overlay-backdrop: #080808c0; --color-danger: var(--color-red); + --color-transparency-grid-light: #fafafa; + --color-transparency-grid-dark: #e2e2e2; accent-color: var(--color-accent); color-scheme: light; } diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index efa3472e8c..4a8da7b11d 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -626,7 +626,7 @@ export default defineComponent({
- +