Compare commits

...

53 Commits

Author SHA1 Message Date
techknowlogick
eb0ee6bf58
Add 1.6.4 changelog (#5739) 2019-01-15 20:43:07 -05:00
Julian Tölle
937857e3af fix: use correct value for "MSpan Structures Obtained" #4742 (#5706) (#5715)
Signed-off-by: Julian Tölle <julian.toelle97@gmail.com>
2019-01-13 16:32:05 +02:00
zeripath
f5b43a615c When redirecting clean the path to avoid redirecting to //www.othersite.com (#5669) (#5703)
Fix #5627

Signed-off-by: Andrew Thornton <art27@cantab.net>
2019-01-12 14:57:50 -05:00
Lunny Xiao
1c4293b37f fix public will not be reused as public key after deleting as deploy key (#5671) (#5685) 2019-01-10 09:23:04 -05:00
Jonas Franz
30560b0f9b
Add changelog for 1.6.3 (#5637)
* Add changelog for 1.6.3 and 1.7.0-rc2

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

* Remove 1.7.0 entries

Signed-off-by: Jonas Franz <info@jonasfranz.software>
2019-01-04 19:14:28 +01:00
zeripath
6076674d3a SECURITY: protect DeleteFilePost et al with cleanUploadFileName (#5631) (#5634)
This commit wraps more of the TreePaths with cleanUploadFileName

Signed-off-by: Andrew Thornton <art27@cantab.net>
2019-01-04 17:29:36 +01:00
Harshit Bansal
28cc3bd662 Fix wrong text getting saved on editing second comment on an issue. (#5608) (#5615)
* comments: Fix an incorrent DOM element selection.

This commit fixes a bug that was causing text from previously edited
comment to get saved when two comments were edited one after other.
Text area with id of `#content` isn't unique on the page but it was
being treated as unique by the event handling code.

Fixes: #5581.

* templates: Remove `id` from textarea in commit edit form.

An element is assigned an `id` only if it is unique for the whole page
but in this case there can be multiple textarea so it should have one.
2018-12-31 11:52:06 -05:00
techknowlogick
2631f7f64d
Changelog for 1.6.2 (#5567) 2018-12-21 10:08:46 -05:00
techknowlogick
af4626a270
Immediate fix to htmlEncode user added text (#5575)
There are likely problems remaining with the way that initCommentForm
is creating its elements. I suspect that a malformed avatar url could
be used maliciously.
2018-12-21 09:05:47 -05:00
techknowlogick
21c70e1ed2 backport 5571 (#5573) 2018-12-21 16:22:56 +08:00
Lunny Xiao
b45d58805a fix indexer reindex bug when gitea restart (#5563) (#5564)
* fix issue indexer bug reindex when restart gitea

* also fix code indexer reindex when gitea restart
2018-12-19 09:51:53 -05:00
Greg Karékinian
200b974e19 Backport #5537 Remove a double slash in the HTTPS redirect with Let's Encrypt (#5539)
Before:

$ curl 0.0.0.0:3001
<a href="https://gitea.example.com:3000//">Found</a>.

After:

$ curl 0.0.0.0:3001
<a href="https://gitea.example.com:3000/">Found</a>.

Fixes #5536
2018-12-13 10:42:38 -05:00
Lunny Xiao
800271ee1f fix bug when a read perm user to edit his issue (#5516) (#5534) 2018-12-12 12:37:22 -05:00
Lunny Xiao
e6362f3d23 fix detect force push failure on deletion of protected branches (#5522) (#5531) 2018-12-12 09:49:47 -05:00
Greg Karékinian
716c2918be Backported #5525 Fix the Let's Encrypt handler (#5527)
* Fix the Let's Encrypt handler by listening on a valid address

Also handle errors in the HTTP server go routine, return a fatal error
when something goes wrong.

Thanks to @gbl08ma for finding the actual bug

Here is an example of the error handling:

    2018/12/11 14:23:07 [....io/gitea/cmd/web.go:87 func1()] [E] Failed to
    start the Let's Encrypt handler on port 30: listen tcp 0.0.0.0:30: bind:
    permission denied

Closes #5280

* Fix a typo
2018-12-11 13:34:35 -05:00
Lunny Xiao
60d7b614fe
fix forgot deletion of notification when delete repository (#5506) (#5514) 2018-12-11 19:09:53 +08:00
Lunny Xiao
9cf9a54dca
fix undeleted content when deleting user (#5509) 2018-12-11 10:33:20 +08:00
Lunny Xiao
2b4f87da46 Fix empty wiki (#5504) (#5508)
* fix wiki page when wiki path is exist but empty

* improve the error check
2018-12-10 22:37:56 +02:00
techknowlogick
ad9f9cdc30 Add 1.6.1 changelog (#5500) 2018-12-09 21:06:16 +08:00
Lunny Xiao
8237fd4a2d fix topic name length on database (#5493) (#5495) 2018-12-09 02:57:49 +02:00
romankl
8e4a0a978a ensure that the closed_at is set for closed (#5450)
right now the `closed_at` field for json responses is not filled during
the `APIIssue` creation for api responses.

For a closed issue you get a result like:
```json
"state":"open","comments":0,"created_at":"2018-11-29T16:39:24+01:00",
"updated_at":"2018-11-30T10:49:19+01:00","closed_at":null,
"due_date":null,"pull_request":null}
```
which has no information about the closing date. (which exists in the
db and ui)
with this PR the result changes to this:

```json
:null,"assignee":null,"assignees":null,
"state":"closed",
"comments":0,"created_at":"2018-11-29T16:43:05+01:00",
"updated_at":"2018-12-02T19:17:05+01:00",
"closed_at":"2018-12-02T19:17:05+01:00",
"due_date":null,"pull_request":null}
```

fixes: https://github.com/go-gitea/gitea/issues/5446
Signed-off-by: Roman <romaaan.git@gmail.com>
2018-12-02 17:50:47 -05:00
Lanre Adelowo
c1275e2ba6 Admin should be able to delete repos even if he is not a member of the organization (#5443) (#5447) 2018-12-02 20:07:20 +02:00
romankl
7bc1faabdb word-break the WebHook url to prevent a ui-break (#5445)
right now, the url is displayed with an anchor tag with no classes. If
the url is really really long, the url will break out of the containing
div and (depending on the url length) the browser shows the horizontal
scrollbar.
This pr makes use of the already existing css class `dont-break-out`
which gives all the anchor the necessary properties to prevent the
break.
Another solution could be to introduce some classes like
`text text-break-word`, but that would duplicate the `dont-break-out`
class just for text elements that use the `text` class.

fixes: https://github.com/go-gitea/gitea/issues/5416
Signed-off-by: Roman <romaaan.git@gmail.com>
2018-12-02 10:47:45 -05:00
Lunny Xiao
e406dc058d Fix repository deletion when there is large number of issues in it (#5426) (#5434) 2018-11-30 09:38:35 -05:00
Lauris BH
328e38ebc7 Fix dependent issue searching when gitea is run in subpath (#5392) (#5400) 2018-11-25 19:27:17 -05:00
Daniel Balko
773addf727 Backported #5383 on v1.6: API: '/orgs/:org/repos': return private repos with read access (#5310) (#3829) (#5393)
Signed-off-by: Daniel Balko <inxonic+github@gmail.com>
2018-11-24 13:14:27 +02:00
Lunny Xiao
0da8bc9ec0 add changelog of v1.6.0 (#5379)
* add changelog of v1.6.0

* improve changelog
2018-11-22 22:53:58 -05:00
Lunny Xiao
5d69703d3c
dont' send assign webhooks when creating issue (#5365) (#5369) 2018-11-21 23:12:17 +08:00
Florian Eitel
ffc0c7f611 Migration fixes 5318 1.6 backport (#5355)
* Remove field from migration to support upgrades from older version

That will ensure the field does not get queried in the Select if it does
not exist yet:

```
[I] [SQL] SELECT "id", "repo_id", "index", "poster_id", "name", "content", "milestone_id", "priority", "assignee_id", "is_closed", "is_pull", "num_comments", "ref", "deadline_unix", "created_unix", "updated_unix
[...itea/routers/init.go:60 GlobalInit()] [E] Failed to initialize ORM engine: migrate: do migrate: pq: column "ref" does not exist
```

see #5318

* Skip remove stale watcher migration if not required

Otherwise the migration will fail if executed from a older database
version without multiple IssueWatch feature.

```
2018/11/11 23:51:14 [I] [SQL] SELECT DISTINCT "issue_watch"."user_id", "issue"."repo_id" FROM "issue_watch" INNER JOIN issue ON issue_watch.issue_id = issue.id WHERE (issue_watch.is_watching = $1) LIMIT 50 []int
[...itea/routers/init.go:60 GlobalInit()] [E] Failed to initialize ORM engine: migrate: do migrate: pq: relation "issue_watch" does not exist
```

see #5318
2018-11-18 22:34:14 +02:00
Lunny Xiao
8670decafb Fix create team, update team missing units (#5188) (#5313) 2018-11-11 12:43:56 -05:00
Lauris BH
297e619074
Fix file edit change preview functionality (#5300) (#5311) 2018-11-10 16:12:58 +02:00
Lunny Xiao
e9b984e162
fix bug when users have serval teams with different units on different repositories (#5307) (#5308) 2018-11-09 17:46:38 +08:00
Lauris BH
5995b65175 Fix U2F if gitea is configured in subpath (#5302) (#5306) 2018-11-09 13:45:50 +08:00
Lauris BH
996ce8cc03 Fix markdown image with link (#4675) (#5299)
* Fix markdown image with link

* Add gitea copyright notice

* add a test for markdown image with link

* remove svg related variables
2018-11-08 17:47:24 -05:00
Lauris BH
fe7cef0e1f Add changelog for 1.5.3 release (#5227) (#5298) 2018-11-08 17:20:10 -05:00
Lauris BH
464dcd1b66
Remove maxlines option for file logger (#5282) (#5287) 2018-11-07 09:14:44 +02:00
Lauris BH
68938d5dc4
Backport fix broken translation (#5284) 2018-11-07 06:50:52 +02:00
techknowlogick
9c11fafdb0
1.6.0-RC2 Changelog (#5275) 2018-11-04 17:07:08 -05:00
zeripath
c0bbbdd30b Backport #5250 on v1.6: Fix Issue 5249 and protect /api/v1/admin routes with CSRF token (#5272)
* Add CSRF checking to reqToken and place CSRF in the post for deadline creation

Fixes #5226, #5249

* /api/v1/admin/users routes should have reqToken middleware
2018-11-04 10:42:15 -05:00
kolaente
f95c966770 Backported wrong api request url for instances running in subfolders (#5247) (#5261) 2018-11-03 17:43:11 -04:00
Peter Hoffmann
14a074f979 fix: Accept web-command cli flags if web-command is commited (#5245)
* Added flags of default cmd CmdWeb to app-wide flags
* If command *is* specified app-wide flags are ignored

Backport of #5200
Signed-off-by: Berengar W. Lehr <Berengar.Lehr@kompetenztest.de>
2018-11-01 11:24:23 -04:00
Lunny Xiao
3786369356 This commit will reduce join star, repo_topic, topic tables on repo search, so that fix extra columns problem on mssql (#5136) (#5229)
* This commit will reduce join star, repo_topic, topic tables on repo search, so that fix extra columns problem on mssql

* fix tests
2018-10-31 20:21:31 -04:00
Lunny Xiao
79464216d9
fix data race on migrate repository (#5224) (#5230) 2018-10-31 20:23:13 +08:00
Peter Hoffmann
e28801ff1a fix: Add secret to all webhook's payload where it has been missing (#5208)
* Updated dependency manager via `dep ensure -update code.gitea.io/sdk`
* Gopkg.toml was not changed as sdk version is set to "master"
* affects webhooks for: Delete, Fork, IssueComment, Release
* also contains changes from go-gitea/go-sdk#125 and hence a swagger update

Signed-off-by: Berengar W. Lehr <Berengar.Lehr@kompetenztest.de>
Resolves: #4732, #5173
2018-10-30 17:14:12 +02:00
Lunny Xiao
478ba7f318
fix sqlite lock (#5210) (#5223) 2018-10-30 14:21:55 +08:00
Kim "BKC" Carlbäcker
582213a858 Update go-macaron/session to latest mast to fix RCE-bug (#5195) 2018-10-30 13:36:50 +08:00
Lunny Xiao
4d66de684f
Fix race on updatesize (#5190) (#5215)
* fix race on updatesize

* fix more repoPath
2018-10-30 09:20:18 +08:00
Rodrigo Villablanca Vásquez
d220a3d772 fix to 3819 - Backport (#5219) 2018-10-29 15:56:21 -04:00
Lunny Xiao
7022957b15 fix sqlite and mssql lock (#5214) (#5218) 2018-10-29 14:10:50 -04:00
Lunny Xiao
e7128e8c41 Fix sqlite lock (#5176) (#5179)
* fix sqlite lock

* fix sqlite lock on getUnitType
2018-10-25 17:30:25 +03:00
Jonas Franz
274ff0d011 Add comment replies (#5147)
*         Add comment replies

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

* Use review.ID instead

Signed-off-by: Jonas Franz <info@jonasfranz.software>
2018-10-23 10:38:06 -04:00
Filip Navara
7238bb329a Fix SQL quoting (#5137)
`show` is keyword in MySQL and has to be quoted to reference a column name. Use grave accents (ASCII code 96) for quoting to match rest of the source code. It's non-standard SQL, but it's supported by SQLite and MySQL.

Signed-off-by: Filip Navara <navara@emclient.com>
2018-10-22 14:46:47 -04:00
kolaente
49d666f99a Fix regex to support optional end line of old section in diff hunk (#5097)
+ Named groups in reges for easier group parsing
2018-10-18 09:57:35 +08:00
69 changed files with 794 additions and 380 deletions

View File

@ -4,12 +4,55 @@ 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.6.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.6.0-rc1) - 2018-10-17 ## [1.6.4](https://github.com/go-gitea/gitea/releases/tag/v1.6.4) - 2019-01-15
* BUGFIX
* Fix SSH key now can be reused as public key after deleting as deploy key (#5671) (#5685)
* When redirecting clean the path to avoid redirecting to external site (#5669) (#5703)
* Fix to use correct value for "MSpan Structures Obtained" (#5706) (#5715)
## [1.6.3](https://github.com/go-gitea/gitea/releases/tag/v1.6.3) - 2019-01-04
* SECURITY
* Prevent DeleteFilePost doing arbitrary deletion (#5631)
* BUGFIX
* Fix wrong text getting saved on editing second comment on an issue (#5608)
## [1.6.2](https://github.com/go-gitea/gitea/releases/tag/v1.6.2) - 2018-12-21
* SECURITY
* Sanitize uploaded file names (#5571) (#5573)
* HTMLEncode user added text (#5570) (#5575)
* BUGFIXES
* Fix indexer reindex bug when gitea restart (#5563) (#5564)
* Remove a double slash in the HTTPS redirect with Let's Encrypt (#5537) (#5539)
* Fix bug when a read perm user to edit his issue (#5516) (#5534)
* Detect force push failure on deletion of protected branches (#5522) (#5531)
* Let's Encrypt handler listens on correct port for certificate validation (#5525) (#5527)
* Fix forgot deletion of notification when delete repository (#5506) (#5514)
* Fix undeleted content when deleting user (#5429) (#5509)
* Fix empty wiki (#5504) (#5508)
## [1.6.1](https://github.com/go-gitea/gitea/releases/tag/v1.6.1) - 2018-12-08
* BUGFIXES
* Fix dependent issue searching when gitea is run in subpath (#5392) (#5400)
* API: '/orgs/:org/repos': return private repos with read access (#5393)
* Fix repository deletion when there is large number of issues in it (#5426) (#5434)
* Word-break the WebHook url to prevent a ui-break (#5445)
* Admin should be able to delete repos via the API even if they are not a member of the organization (#5443) (#5447)
* Ensure that the `closed_at` is set for closed (#5450)
* Fix topic name length on database (#5493) (#5495)
## [1.6.0](https://github.com/go-gitea/gitea/releases/tag/v1.6.0) - 2018-11-22
* BREAKING * BREAKING
* Respect email privacy option in user search via API (#4512) * Respect email privacy option in user search via API (#4512)
* Simply remove tidb and deps (#3993) * Simply remove tidb and deps (#3993)
* Swagger.v1.json template (#3572) * Swagger.v1.json template (#3572)
* SECURITY
* Add CSRF checking to reqToken and add reqToken to admin API routes (#5272) (#5250)
* Improve URL validation for external wiki and external issues (#4710)
* Make cookies HttpOnly and obey COOKIE_SECURE flag (#4706)
* Don't disclose emails of all users when sending out emails (#4664)
* Check that repositories can only be migrated to own user or organizations (#4366)
* FEATURE * FEATURE
* Add comment replies (#5147) (#5104)
* Pull request review/approval and comment on code (#3748) * Pull request review/approval and comment on code (#3748)
* Added dependencies for issues (#2196) (#2531) * Added dependencies for issues (#2196) (#2531)
* Add the ability to have built in themes in Gitea and provide dark theme arc-green (#4198) * Add the ability to have built in themes in Gitea and provide dark theme arc-green (#4198)
@ -21,63 +64,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add push webhook support for mirrored repositories (#4127) * Add push webhook support for mirrored repositories (#4127)
* Add csv file render support defaultly (#4105) * Add csv file render support defaultly (#4105)
* Add Recaptcha functionality to Gitea (#4044) * Add Recaptcha functionality to Gitea (#4044)
* BUGFIXES
* Fix release creation via API (#5076)
* Remove links from topics in edit mode (#5026)
* Fix missing AppSubUrl in few more templates (fixup) (#5021)
* Fix missing AppSubUrl in some templates (#5020)
* Hide outdated comments in file view (#5017)
* Upgrade gopkg.in/testfixtures.v2 (#4999)
* Disable debug routes unless PPROF is enabled in configuration (#4995)
* Fix user menu item styling (#4985)
* Fix layout of the topics editing form (#4971)
* Fix null pointer dereference in ParseCommitWithSignature (#4962)
* Fix url in discord webhook (#4953)
* Detect charset and convert non UTF-8 files for display (#4950)
* Make sure to catch the right error so it is displayed on the UI (#4945)
* Fix(topics): don't redirect to explore page. (#4938)
* Fix bug forget to remove Stopwatch when remove repository (#4928)
* Fix bug when repo remained bare if multiple branches pushed in single push (#4923)
* Fix: Let's Encrypt configuration settings (#4911)
* Fix: Crippled diff (#4726) (#4900)
* Fix trimming of markup section names (#4863)
* Issues api allow pulls and fix #4832 (#4852)
* Do not autocreate directory for new users/orgs (#4828) (#4849)
* Fix redirect with non-ascii branch names (#4764) (#4810)
* Fix missing release title in webhook (#4783) (#4796)
* User shouldn't be able to approve or reject his/her own PR (#4729)
* Make sure to reset commit count in the cache on mirror syncing (#4720)
* Fixed bug where team with admin privelege type doesn't get any unit (#4719)
* Fix incorrect caption of webhook setting (#4701) (#4717)
* Allow WIP marker to contains < or > (#4709)
* Hide org/create menu item in Dashboard if user has no rights (#4678) (#4680)
* Site admin could create repos even MAX_CREATION_LIMIT=0 (#4645)
* Fix custom templates being ignored (#4638)
* Fix starring icon after semantic ui update (#4628)
* Fix Split-View line adjustment (#4622)
* Fix integer constant overflows in tests (#4616)
* Push whitelist now doesn't apply to branch deletion (#4601) (#4607)
* Fix bugs when too many IN variables (#4594)
* Fix failure on creating pull request with assignees (#4419) (#4583)
* Fix panic issue on update avatar email (#4580) (#4581)
* Fix status code label for a successful webhook (#4540)
* An inactive user shouldn't be able to be added as a collaborator (#4535)
* Don't fail silently if trying to add a collaborator twice (#4533)
* Fix incorrect MergeWhitelistTeamIDs check in CanUserMerge function (#4519) (#4525)
* Fix out-of-transaction query in removeOrgUser (#4521) (#4522)
* Fix migration from older releases (#4495)
* Accept 'Data:' in commit graph (#4487)
* Update xorm to latest version and fix correct `user` table referencing in sql (#4473)
* Relative URLs for LibreJS page (#4460)
* Redirect to correct page after using scratch token (#4458)
* Fix column droping for MSSQL that need new transaction for that (#4440)
* Replace src with raw to fix image paths (#4377)
* Add default merge options when creating new repository (#4369)
* Fix docker build (#4358)
* Fixes repo membership check in API (#4341)
* Dep upgrade mysql lib (#4161)
* Fix some issues with special chars in branch names (#3767)
* Responsive design fixes (#4508)
* ENHANCEMENT * ENHANCEMENT
* Fix milestones sorted wrongly (#4987) * Fix milestones sorted wrongly (#4987)
* Allow api to create tags for releases if they don't exist (#4890) * Allow api to create tags for releases if they don't exist (#4890)
@ -130,15 +116,87 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Added front-end topics validation (#4316) * Added front-end topics validation (#4316)
* Don't display buttons if there are no system notifications (#4280) * Don't display buttons if there are no system notifications (#4280)
* Issue due date api (#3890) * Issue due date api (#3890)
* SECURITY * BUGFIXES
* Improve URL validation for external wiki and external issues (#4710) * dont' send assign webhooks when creating issue (#5365)
* Make cookies HttpOnly and obey COOKIE_SECURE flag (#4706) * Fix create team, update team missing units (#5188)
* Don't disclose emails of all users when sending out emails (#4664) * Fix file edit change preview functionality (#5300)
* Check that repositories can only be migrated to own user or organizations (#4366) * *ix bug when users have serval teams with different units on different repositories (#5307)
* Fix U2F if gitea is configured in subpath (#5302)
* Fix markdown image with link (#4675)
* Remove maxlines option for file logger (#5282)
* Fix wrong api request url for instances running in subfolders (#5261) (#5247)
* Accept web-command cli flags if web-command is commited (#5245) (#5200)
* Reduce join star, repo_topic, topic tables on repo search, to resolve extra columns problem on MSSQL (#5136) (#5229)
* Fix data race on migrate repository (#5224) (#5230)
* Add secret to all webhook's payload where it has been missing (#5208) (#5199)
* Fix sqlite and MSSQL lock (#5210) (#5223) (#5214) (#5218) (#5176) (#5179)
* Fix race on updatesize (#5190) (#5215)
* Fix filtering issues by tags on main screen issues (#5219) (#3824)
* Fix SQL quoting (#5137) (#5117)
* Fix regex to support optional end line of old section in diff hunk (#5097) (#5096)
* Fix release creation via API (#5076)
* Remove links from topics in edit mode (#5026)
* Fix missing AppSubUrl in few more templates (fixup) (#5021)
* Fix missing AppSubUrl in some templates (#5020)
* Hide outdated comments in file view (#5017)
* Upgrade gopkg.in/testfixtures.v2 (#4999)
* Disable debug routes unless PPROF is enabled in configuration (#4995)
* Fix user menu item styling (#4985)
* Fix layout of the topics editing form (#4971)
* Fix null pointer dereference in ParseCommitWithSignature (#4962)
* Fix url in discord webhook (#4953)
* Detect charset and convert non UTF-8 files for display (#4950)
* Make sure to catch the right error so it is displayed on the UI (#4945)
* Fix(topics): don't redirect to explore page. (#4938)
* Fix bug forget to remove Stopwatch when remove repository (#4928)
* Fix bug when repo remained bare if multiple branches pushed in single push (#4923)
* Fix: Crippled diff (#4726) (#4900)
* Fix trimming of markup section names (#4863)
* Issues api allow pulls and fix #4832 (#4852)
* Do not autocreate directory for new users/orgs (#4828) (#4849)
* Fix redirect with non-ascii branch names (#4764) (#4810)
* Fix missing release title in webhook (#4783) (#4796)
* User shouldn't be able to approve or reject his/her own PR (#4729)
* Make sure to reset commit count in the cache on mirror syncing (#4720)
* Fixed bug where team with admin privelege type doesn't get any unit (#4719)
* Fix incorrect caption of webhook setting (#4701) (#4717)
* Allow WIP marker to contains < or > (#4709)
* Hide org/create menu item in Dashboard if user has no rights (#4678) (#4680)
* Site admin could create repos even MAX_CREATION_LIMIT=0 (#4645)
* Fix custom templates being ignored (#4638)
* Fix starring icon after semantic ui update (#4628)
* Fix Split-View line adjustment (#4622)
* Fix integer constant overflows in tests (#4616)
* Push whitelist now doesn't apply to branch deletion (#4601) (#4607)
* Fix bugs when too many IN variables (#4594)
* Fix failure on creating pull request with assignees (#4419) (#4583)
* Fix panic issue on update avatar email (#4580) (#4581)
* Fix status code label for a successful webhook (#4540)
* An inactive user shouldn't be able to be added as a collaborator (#4535)
* Don't fail silently if trying to add a collaborator twice (#4533)
* Fix incorrect MergeWhitelistTeamIDs check in CanUserMerge function (#4519) (#4525)
* Fix out-of-transaction query in removeOrgUser (#4521) (#4522)
* Fix migration from older releases (#4495)
* Accept 'Data:' in commit graph (#4487)
* Update xorm to latest version and fix correct `user` table referencing in sql (#4473)
* Relative URLs for LibreJS page (#4460)
* Redirect to correct page after using scratch token (#4458)
* Fix column droping for MSSQL that need new transaction for that (#4440)
* Replace src with raw to fix image paths (#4377)
* Add default merge options when creating new repository (#4369)
* Fix docker build (#4358)
* Fixes repo membership check in API (#4341)
* Dep upgrade mysql lib (#4161)
* Fix some issues with special chars in branch names (#3767)
* Responsive design fixes (#4508)
* TRANSLATION * TRANSLATION
* Fix punctuation in English translation (#4958) * Fix punctuation in English translation (#4958)
* Fix translation (#4355) * Fix translation (#4355)
## [1.5.3](https://github.com/go-gitea/gitea/releases/tag/v1.5.3) - 2018-10-31
* SECURITY
* Fix remote command execution vulnerability in upstream library (#5177) (#5196)
## [1.5.2](https://github.com/go-gitea/gitea/releases/tag/v1.5.2) - 2018-10-09 ## [1.5.2](https://github.com/go-gitea/gitea/releases/tag/v1.5.2) - 2018-10-09
* SECURITY * SECURITY
* Enforce token on api routes (#4840) (#4905) * Enforce token on api routes (#4840) (#4905)

9
Gopkg.lock generated
View File

@ -11,11 +11,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:5e7f14543006a44047fb1d0df16da08b2ebc2428f1fd53bd8af26a2b34928b11" digest = "1:b194da40b41ae99546dfeec5a85f1fec2a6c51350d438e511ef90f4293c6dcd7"
name = "code.gitea.io/sdk" name = "code.gitea.io/sdk"
packages = ["gitea"] packages = ["gitea"]
pruneopts = "NUT" pruneopts = "NUT"
revision = "021567c9c12fe289b8980c34e81e6684434dd082" revision = "4f96d9ac89886e78c50de8c835ebe87461578a5e"
[[projects]] [[projects]]
digest = "1:3fcef06a1a6561955c94af6c7757a6fa37605eb653f0d06ab960e5bb80092195" digest = "1:3fcef06a1a6561955c94af6c7757a6fa37605eb653f0d06ab960e5bb80092195"
@ -342,14 +342,15 @@
revision = "d8a0b8677191f4380287cfebd08e462217bac7ad" revision = "d8a0b8677191f4380287cfebd08e462217bac7ad"
[[projects]] [[projects]]
digest = "1:b327ca585509a889130a8f51f43704a8fe03cb5cd281dbf1bc6405f5a7ea4702" branch = "master"
digest = "1:8fea5718d84af17762195beb6fe92a0d6c1048452a1dbc464d227f12e0cff0cc"
name = "github.com/go-macaron/session" name = "github.com/go-macaron/session"
packages = [ packages = [
".", ".",
"redis", "redis",
] ]
pruneopts = "NUT" pruneopts = "NUT"
revision = "66031fcb37a0fff002a1f028eb0b3a815c78306b" revision = "330e4e4d8beb7b00111ac34539561f46f94c4458"
[[projects]] [[projects]]
digest = "1:758d2371fcdee6d02565901b348729053c636055e67ef6e17aa466c7ff6cc57c" digest = "1:758d2371fcdee6d02565901b348729053c636055e67ef6e17aa466c7ff6cc57c"

View File

@ -112,10 +112,15 @@ func runHookPreReceive(c *cli.Context) error {
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix) branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
protectBranch, err := private.GetProtectedBranchBy(repoID, branchName) protectBranch, err := private.GetProtectedBranchBy(repoID, branchName)
if err != nil { if err != nil {
log.GitLogger.Fatal(2, "retrieve protected branches information failed") fail("Internal error", fmt.Sprintf("retrieve protected branches information failed: %v", err))
} }
if protectBranch != nil && protectBranch.IsProtected() { if protectBranch != nil && protectBranch.IsProtected() {
// check and deletion
if newCommitID == git.EmptySHA {
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
}
// detect force push // detect force push
if git.EmptySHA != oldCommitID { if git.EmptySHA != oldCommitID {
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath) output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
@ -126,17 +131,12 @@ func runHookPreReceive(c *cli.Context) error {
} }
} }
// check and deletion userID, _ := strconv.ParseInt(userIDStr, 10, 64)
if newCommitID == git.EmptySHA { canPush, err := private.CanUserPush(protectBranch.ID, userID)
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "") if err != nil {
} else { fail("Internal error", "Fail to detect user can push: %v", err)
userID, _ := strconv.ParseInt(userIDStr, 10, 64) } else if !canPush {
canPush, err := private.CanUserPush(protectBranch.ID, userID) fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
if err != nil {
fail("Internal error", "Fail to detect user can push: %v", err)
} else if !canPush {
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
}
} }
} }
} }

View File

@ -80,7 +80,13 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
Cache: autocert.DirCache(directory), Cache: autocert.DirCache(directory),
Email: email, Email: email,
} }
go http.ListenAndServe(listenAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validatio happens here) go func() {
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
var err = http.ListenAndServe(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
if err != nil {
log.Fatal(4, "Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
}
}()
server := &http.Server{ server := &http.Server{
Addr: listenAddr, Addr: listenAddr,
Handler: m, Handler: m,
@ -96,7 +102,10 @@ func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Use HTTPS", http.StatusBadRequest) http.Error(w, "Use HTTPS", http.StatusBadRequest)
return return
} }
target := setting.AppURL + r.URL.RequestURI() // Remove the trailing slash at the end of setting.AppURL, the request
// URI always contains a leading slash, which would result in a double
// slash
target := strings.TrimRight(setting.AppURL, "/") + r.URL.RequestURI()
http.Redirect(w, r, target, http.StatusFound) http.Redirect(w, r, target, http.StatusFound)
} }

View File

@ -39,8 +39,8 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) {
OwnerID: keyOwner.ID, OwnerID: keyOwner.ID,
}) })
req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token="+token, req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s",
keyOwner.Name, newPublicKey.ID) keyOwner.Name, newPublicKey.ID, token)
session.MakeRequest(t, req, http.StatusNoContent) session.MakeRequest(t, req, http.StatusNoContent)
models.AssertNotExistsBean(t, &models.PublicKey{ID: newPublicKey.ID}) models.AssertNotExistsBean(t, &models.PublicKey{ID: newPublicKey.ID})
} }
@ -51,7 +51,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) {
session := loginUser(t, "user1") session := loginUser(t, "user1")
token := getTokenForLoggedInUser(t, session) token := getTokenForLoggedInUser(t, session)
req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token="+token, models.NonexistentID) req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", models.NonexistentID, token)
session.MakeRequest(t, req, http.StatusNotFound) session.MakeRequest(t, req, http.StatusNotFound)
} }
@ -73,8 +73,8 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) {
session = loginUser(t, normalUsername) session = loginUser(t, normalUsername)
token = getTokenForLoggedInUser(t, session) token = getTokenForLoggedInUser(t, session)
req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token="+token, req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d?token=%s",
adminUsername, newPublicKey.ID) adminUsername, newPublicKey.ID, token)
session.MakeRequest(t, req, http.StatusForbidden) session.MakeRequest(t, req, http.StatusForbidden)
} }

View File

@ -212,21 +212,46 @@ func TestAPIViewRepo(t *testing.T) {
func TestAPIOrgRepos(t *testing.T) { func TestAPIOrgRepos(t *testing.T) {
prepareTestEnv(t) prepareTestEnv(t)
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
// User3 is an Org. Check their repos. // User3 is an Org. Check their repos.
sourceOrg := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) sourceOrg := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
// Login as User2.
session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session)
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name)
resp := session.MakeRequest(t, req, http.StatusOK)
var apiRepos []*api.Repository expectedResults := map[*models.User]struct {
DecodeJSON(t, resp, &apiRepos) count int
expectedLen := models.GetCount(t, models.Repository{OwnerID: sourceOrg.ID}, includesPrivate bool
models.Cond("is_private = ?", false)) }{
assert.Len(t, apiRepos, expectedLen) nil: {count: 1},
for _, repo := range apiRepos { user: {count: 2, includesPrivate: true},
assert.False(t, repo.Private) user2: {count: 3, includesPrivate: true},
user3: {count: 1},
}
for userToLogin, expected := range expectedResults {
var session *TestSession
var testName string
var token string
if userToLogin != nil && userToLogin.ID > 0 {
testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
session = loginUser(t, userToLogin.Name)
token = getTokenForLoggedInUser(t, session)
} else {
testName = "AnonymousUser"
session = emptyTestSession(t)
}
t.Run(testName, func(t *testing.T) {
req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name)
resp := session.MakeRequest(t, req, http.StatusOK)
var apiRepos []*api.Repository
DecodeJSON(t, resp, &apiRepos)
assert.Len(t, apiRepos, expected.count)
for _, repo := range apiRepos {
if !expected.includesPrivate {
assert.False(t, repo.Private)
}
}
})
} }
} }

View File

@ -143,7 +143,8 @@ func TestGit(t *testing.T) {
session := loginUser(t, "user1") session := loginUser(t, "user1")
keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User) keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", keyOwner.Name) token := getTokenForLoggedInUser(t, session)
urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token)
dataPubKey, err := ioutil.ReadFile(keyFile + ".pub") dataPubKey, err := ioutil.ReadFile(keyFile + ".pub")
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -48,7 +48,7 @@ arguments - which can alternatively be run by running the subcommand web.`
cmd.CmdAdmin, cmd.CmdAdmin,
cmd.CmdGenerate, cmd.CmdGenerate,
} }
app.Flags = append(app.Flags, []cli.Flag{}...) app.Flags = append(app.Flags, cmd.CmdWeb.Flags...)
app.Action = cmd.CmdWeb.Action app.Action = cmd.CmdWeb.Action
err := app.Run(os.Args) err := app.Run(os.Args)
if err != nil { if err != nil {

View File

@ -273,7 +273,7 @@ func (diff *Diff) NumFiles() int {
} }
// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9] // Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9]
var hunkRegex = regexp.MustCompile(`^@@ -([0-9]+),([0-9]+) \+([0-9]+)(,([0-9]+))? @@`) var hunkRegex = regexp.MustCompile(`^@@ -(?P<beginOld>[0-9]+)(,(?P<endOld>[0-9]+))? \+(?P<beginNew>[0-9]+)(,(?P<endNew>[0-9]+))? @@`)
func isHeader(lof string) bool { func isHeader(lof string) bool {
return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++") return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++")
@ -311,21 +311,28 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
if len(hunk) > headerLines { if len(hunk) > headerLines {
break break
} }
groups := hunkRegex.FindStringSubmatch(lof) // A map with named groups of our regex to recognize them later more easily
submatches := hunkRegex.FindStringSubmatch(lof)
groups := make(map[string]string)
for i, name := range hunkRegex.SubexpNames() {
if i != 0 && name != "" {
groups[name] = submatches[i]
}
}
if old { if old {
begin = com.StrTo(groups[1]).MustInt64() begin = com.StrTo(groups["beginOld"]).MustInt64()
end = com.StrTo(groups[2]).MustInt64() end = com.StrTo(groups["endOld"]).MustInt64()
// init otherLine with begin of opposite side // init otherLine with begin of opposite side
otherLine = com.StrTo(groups[3]).MustInt64() otherLine = com.StrTo(groups["beginNew"]).MustInt64()
} else { } else {
begin = com.StrTo(groups[3]).MustInt64() begin = com.StrTo(groups["beginNew"]).MustInt64()
if groups[5] != "" { if groups["endNew"] != "" {
end = com.StrTo(groups[5]).MustInt64() end = com.StrTo(groups["endNew"]).MustInt64()
} else { } else {
end = 0 end = 0
} }
// init otherLine with begin of opposite side // init otherLine with begin of opposite side
otherLine = com.StrTo(groups[1]).MustInt64() otherLine = com.StrTo(groups["beginOld"]).MustInt64()
} }
end += begin // end is for real only the number of lines in hunk end += begin // end is for real only the number of lines in hunk
// lof is between begin and end // lof is between begin and end

View File

@ -325,6 +325,10 @@ func (issue *Issue) APIFormat() *api.Issue {
Updated: issue.UpdatedUnix.AsTime(), Updated: issue.UpdatedUnix.AsTime(),
} }
if issue.ClosedUnix != 0 {
apiIssue.Closed = issue.ClosedUnix.AsTimePtr()
}
if issue.Milestone != nil { if issue.Milestone != nil {
apiIssue.Milestone = issue.Milestone.APIFormat() apiIssue.Milestone = issue.Milestone.APIFormat()
} }

View File

@ -159,12 +159,13 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
return fmt.Errorf("createAssigneeComment: %v", err) return fmt.Errorf("createAssigneeComment: %v", err)
} }
// if issue/pull is in the middle of creation - don't call webhook
if isCreate {
return nil
}
mode, _ := accessLevel(sess, doer.ID, issue.Repo) mode, _ := accessLevel(sess, doer.ID, issue.Repo)
if issue.IsPull { if issue.IsPull {
// if pull request is in the middle of creation - don't call webhook
if isCreate {
return nil
}
if err = issue.loadPullRequest(sess); err != nil { if err = issue.loadPullRequest(sess); err != nil {
return fmt.Errorf("loadPullRequest: %v", err) return fmt.Errorf("loadPullRequest: %v", err)
} }

View File

@ -26,7 +26,6 @@ func addMultipleAssignees(x *xorm.Engine) error {
IsClosed bool `xorm:"INDEX"` IsClosed bool `xorm:"INDEX"`
IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not. IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not.
NumComments int NumComments int
Ref string
DeadlineUnix util.TimeStamp `xorm:"INDEX"` DeadlineUnix util.TimeStamp `xorm:"INDEX"`
CreatedUnix util.TimeStamp `xorm:"INDEX created"` CreatedUnix util.TimeStamp `xorm:"INDEX created"`

View File

@ -5,6 +5,8 @@
package migrations package migrations
import ( import (
"fmt"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
@ -70,6 +72,13 @@ func removeStaleWatches(x *xorm.Engine) error {
return err return err
} }
var issueWatch IssueWatch
if exist, err := sess.IsTableExist(&issueWatch); err != nil {
return fmt.Errorf("IsExist IssueWatch: %v", err)
} else if !exist {
return nil
}
repoCache := make(map[int64]*Repository) repoCache := make(map[int64]*Repository)
err := x.BufferSize(setting.IterateBufferSize).Iterate(new(Watch), err := x.BufferSize(setting.IterateBufferSize).Iterate(new(Watch),
func(idx int, bean interface{}) error { func(idx int, bean interface{}) error {

View File

@ -25,7 +25,7 @@ func reformatAndRemoveIncorrectTopics(x *xorm.Engine) (err error) {
type Topic struct { type Topic struct {
ID int64 ID int64
Name string `xorm:"UNIQUE"` Name string `xorm:"UNIQUE VARCHAR(25)"`
RepoCount int RepoCount int
CreatedUnix int64 `xorm:"INDEX created"` CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"` UpdatedUnix int64 `xorm:"INDEX updated"`

View File

@ -123,10 +123,10 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
for _, watch := range watches { for _, watch := range watches {
issue.Repo.Units = nil issue.Repo.Units = nil
if issue.IsPull && !issue.Repo.CheckUnitUser(watch.UserID, false, UnitTypePullRequests) { if issue.IsPull && !issue.Repo.checkUnitUser(e, watch.UserID, false, UnitTypePullRequests) {
continue continue
} }
if !issue.IsPull && !issue.Repo.CheckUnitUser(watch.UserID, false, UnitTypeIssues) { if !issue.IsPull && !issue.Repo.checkUnitUser(e, watch.UserID, false, UnitTypeIssues) {
continue continue
} }

View File

@ -460,21 +460,21 @@ func removeOrgUser(sess *xorm.Session, orgID, userID int64) error {
return nil return nil
} }
org, err := GetUserByID(orgID) org, err := getUserByID(sess, orgID)
if err != nil { if err != nil {
return fmt.Errorf("GetUserByID [%d]: %v", orgID, err) return fmt.Errorf("GetUserByID [%d]: %v", orgID, err)
} }
// Check if the user to delete is the last member in owner team. // Check if the user to delete is the last member in owner team.
if isOwner, err := IsOrganizationOwner(orgID, userID); err != nil { if isOwner, err := isOrganizationOwner(sess, orgID, userID); err != nil {
return err return err
} else if isOwner { } else if isOwner {
t, err := org.GetOwnerTeam() t, err := org.getOwnerTeam(sess)
if err != nil { if err != nil {
return err return err
} }
if t.NumMembers == 1 { if t.NumMembers == 1 {
if err := t.GetMembers(); err != nil { if err := t.getMembers(sess); err != nil {
return err return err
} }
if t.Members[0].ID == userID { if t.Members[0].ID == userID {
@ -490,7 +490,7 @@ func removeOrgUser(sess *xorm.Session, orgID, userID int64) error {
} }
// Delete all repository accesses and unwatch them. // Delete all repository accesses and unwatch them.
env, err := org.AccessibleReposEnv(userID) env, err := org.accessibleReposEnv(sess, userID)
if err != nil { if err != nil {
return fmt.Errorf("AccessibleReposEnv: %v", err) return fmt.Errorf("AccessibleReposEnv: %v", err)
} }
@ -618,16 +618,26 @@ type accessibleReposEnv struct {
org *User org *User
userID int64 userID int64
teamIDs []int64 teamIDs []int64
e Engine
} }
// AccessibleReposEnv an AccessibleReposEnvironment for the repositories in `org` // AccessibleReposEnv an AccessibleReposEnvironment for the repositories in `org`
// that are accessible to the specified user. // that are accessible to the specified user.
func (org *User) AccessibleReposEnv(userID int64) (AccessibleReposEnvironment, error) { func (org *User) AccessibleReposEnv(userID int64) (AccessibleReposEnvironment, error) {
teamIDs, err := org.GetUserTeamIDs(userID) return org.accessibleReposEnv(x, userID)
}
func (org *User) accessibleReposEnv(e Engine, userID int64) (AccessibleReposEnvironment, error) {
teamIDs, err := org.getUserTeamIDs(e, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &accessibleReposEnv{org: org, userID: userID, teamIDs: teamIDs}, nil return &accessibleReposEnv{
org: org,
userID: userID,
teamIDs: teamIDs,
e: e,
}, nil
} }
func (env *accessibleReposEnv) cond() builder.Cond { func (env *accessibleReposEnv) cond() builder.Cond {
@ -642,7 +652,7 @@ func (env *accessibleReposEnv) cond() builder.Cond {
} }
func (env *accessibleReposEnv) CountRepos() (int64, error) { func (env *accessibleReposEnv) CountRepos() (int64, error) {
repoCount, err := x. repoCount, err := env.e.
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()). Where(env.cond()).
Distinct("`repository`.id"). Distinct("`repository`.id").
@ -659,7 +669,7 @@ func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
} }
repoIDs := make([]int64, 0, pageSize) repoIDs := make([]int64, 0, pageSize)
return repoIDs, x. return repoIDs, env.e.
Table("repository"). Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()). Where(env.cond()).
@ -681,14 +691,14 @@ func (env *accessibleReposEnv) Repos(page, pageSize int) ([]*Repository, error)
return repos, nil return repos, nil
} }
return repos, x. return repos, env.e.
In("`repository`.id", repoIDs). In("`repository`.id", repoIDs).
Find(&repos) Find(&repos)
} }
func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) { func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
repoIDs := make([]int64, 0, 10) repoIDs := make([]int64, 0, 10)
return repoIDs, x. return repoIDs, env.e.
Table("repository"). Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true). Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
Where(env.cond()). Where(env.cond()).
@ -709,7 +719,7 @@ func (env *accessibleReposEnv) MirrorRepos() ([]*Repository, error) {
return repos, nil return repos, nil
} }
return repos, x. return repos, env.e.
In("`repository`.id", repoIDs). In("`repository`.id", repoIDs).
Find(&repos) Find(&repos)
} }

View File

@ -40,6 +40,14 @@ func (t *Team) getUnits(e Engine) (err error) {
return err return err
} }
// GetUnitNames returns the team units names
func (t *Team) GetUnitNames() (res []string) {
for _, u := range t.Units {
res = append(res, Units[u.Type].NameKey)
}
return
}
// HasWriteAccess returns true if team has at least write level access mode. // HasWriteAccess returns true if team has at least write level access mode.
func (t *Team) HasWriteAccess() bool { func (t *Team) HasWriteAccess() bool {
return t.Authorize >= AccessModeWrite return t.Authorize >= AccessModeWrite
@ -215,7 +223,11 @@ func (t *Team) RemoveRepository(repoID int64) error {
// UnitEnabled returns if the team has the given unit type enabled // UnitEnabled returns if the team has the given unit type enabled
func (t *Team) UnitEnabled(tp UnitType) bool { func (t *Team) UnitEnabled(tp UnitType) bool {
if err := t.getUnits(x); err != nil { return t.unitEnabled(x, tp)
}
func (t *Team) unitEnabled(e Engine, tp UnitType) bool {
if err := t.getUnits(e); err != nil {
log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error()) log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error())
} }
@ -363,6 +375,24 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
return fmt.Errorf("update: %v", err) return fmt.Errorf("update: %v", err)
} }
// update units for team
if len(t.Units) > 0 {
for _, unit := range t.Units {
unit.TeamID = t.ID
}
// Delete team-unit.
if _, err := sess.
Where("team_id=?", t.ID).
Delete(new(TeamUnit)); err != nil {
return err
}
if _, err = sess.Insert(&t.Units); err != nil {
sess.Rollback()
return err
}
}
// Update access for team members if needed. // Update access for team members if needed.
if authChanged { if authChanged {
if err = t.getRepositories(sess); err != nil { if err = t.getRepositories(sess); err != nil {
@ -521,6 +551,16 @@ func getUserTeams(e Engine, orgID, userID int64) (teams []*Team, err error) {
Find(&teams) Find(&teams)
} }
func getUserRepoTeams(e Engine, orgID, userID, repoID int64) (teams []*Team, err error) {
return teams, e.
Join("INNER", "team_user", "team_user.team_id = team.id").
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team.org_id = ?", orgID).
And("team_user.uid=?", userID).
And("team_repo.repo_id=?", repoID).
Find(&teams)
}
// GetUserTeams returns all teams that user belongs to in given organization. // GetUserTeams returns all teams that user belongs to in given organization.
func GetUserTeams(orgID, userID int64) ([]*Team, error) { func GetUserTeams(orgID, userID int64) ([]*Team, error) {
return getUserTeams(x, orgID, userID) return getUserTeams(x, orgID, userID)

View File

@ -32,6 +32,7 @@ import (
"github.com/Unknwon/cae/zip" "github.com/Unknwon/cae/zip"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/go-xorm/builder"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/mcuadros/go-version" "github.com/mcuadros/go-version"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
@ -321,7 +322,11 @@ func (repo *Repository) getUnits(e Engine) (err error) {
// CheckUnitUser check whether user could visit the unit of this repository // CheckUnitUser check whether user could visit the unit of this repository
func (repo *Repository) CheckUnitUser(userID int64, isAdmin bool, unitType UnitType) bool { func (repo *Repository) CheckUnitUser(userID int64, isAdmin bool, unitType UnitType) bool {
if err := repo.getUnitsByUserID(x, userID, isAdmin); err != nil { return repo.checkUnitUser(x, userID, isAdmin, unitType)
}
func (repo *Repository) checkUnitUser(e Engine, userID int64, isAdmin bool, unitType UnitType) bool {
if err := repo.getUnitsByUserID(e, userID, isAdmin); err != nil {
return false return false
} }
@ -360,7 +365,7 @@ func (repo *Repository) getUnitsByUserID(e Engine, userID int64, isAdmin bool) (
return nil return nil
} }
teams, err := getUserTeams(e, repo.OwnerID, userID) teams, err := getUserRepoTeams(e, repo.OwnerID, userID, repo.ID)
if err != nil { if err != nil {
return err return err
} }
@ -369,7 +374,7 @@ func (repo *Repository) getUnitsByUserID(e Engine, userID int64, isAdmin bool) (
var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units)) var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units))
for _, u := range repo.Units { for _, u := range repo.Units {
for _, team := range teams { for _, team := range teams {
if team.UnitEnabled(u.Type) { if team.unitEnabled(e, u.Type) {
newRepoUnits = append(newRepoUnits, u) newRepoUnits = append(newRepoUnits, u)
break break
} }
@ -677,7 +682,7 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
} }
func (repo *Repository) updateSize(e Engine) error { func (repo *Repository) updateSize(e Engine) error {
repoInfoSize, err := git.GetRepoSize(repo.RepoPath()) repoInfoSize, err := git.GetRepoSize(repo.repoPath(e))
if err != nil { if err != nil {
return fmt.Errorf("UpdateSize: %v", err) return fmt.Errorf("UpdateSize: %v", err)
} }
@ -1031,7 +1036,6 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
if err = SyncReleasesWithTags(repo, gitRepo); err != nil { if err = SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Error(4, "Failed to synchronize tags to releases for repository: %v", err) log.Error(4, "Failed to synchronize tags to releases for repository: %v", err)
} }
UpdateRepoIndexer(repo)
} }
if err = repo.UpdateSize(); err != nil { if err = repo.UpdateSize(); err != nil {
@ -1049,10 +1053,16 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
} }
repo.IsMirror = true repo.IsMirror = true
return repo, UpdateRepository(repo, false) err = UpdateRepository(repo, false)
} else {
repo, err = CleanUpMigrateInfo(repo)
} }
return CleanUpMigrateInfo(repo) if err != nil && !repo.IsBare {
UpdateRepoIndexer(repo)
}
return repo, err
} }
// cleanUpMigrateGitConfig removes mirror info which prevents "push --all". // cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
@ -1705,7 +1715,7 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
} }
// Create/Remove git-daemon-export-ok for git-daemon... // Create/Remove git-daemon-export-ok for git-daemon...
daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`) daemonExportFile := path.Join(repo.repoPath(e), `git-daemon-export-ok`)
if repo.IsPrivate && com.IsExist(daemonExportFile) { if repo.IsPrivate && com.IsExist(daemonExportFile) {
if err = os.Remove(daemonExportFile); err != nil { if err = os.Remove(daemonExportFile); err != nil {
log.Error(4, "Failed to remove %s: %v", daemonExportFile, err) log.Error(4, "Failed to remove %s: %v", daemonExportFile, err)
@ -1828,55 +1838,56 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
&RepoRedirect{RedirectRepoID: repoID}, &RepoRedirect{RedirectRepoID: repoID},
&Webhook{RepoID: repoID}, &Webhook{RepoID: repoID},
&HookTask{RepoID: repoID}, &HookTask{RepoID: repoID},
&Notification{RepoID: repoID},
); err != nil { ); err != nil {
return fmt.Errorf("deleteBeans: %v", err) return fmt.Errorf("deleteBeans: %v", err)
} }
// Delete comments and attachments. deleteCond := builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repoID})
issueIDs := make([]int64, 0, 25) // Delete comments and attachments
attachmentPaths := make([]string, 0, len(issueIDs)) if _, err = sess.In("issue_id", deleteCond).
if err = sess. Delete(&Comment{}); err != nil {
Table("issue").
Cols("id").
Where("repo_id=?", repoID).
Find(&issueIDs); err != nil {
return err return err
} }
if len(issueIDs) > 0 { if _, err = sess.In("issue_id", deleteCond).
if _, err = sess.In("issue_id", issueIDs).Delete(&Comment{}); err != nil { Delete(&IssueUser{}); err != nil {
return err return err
} }
if _, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil {
return err
}
if _, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{}); err != nil {
return err
}
attachments := make([]*Attachment, 0, 5) if _, err = sess.In("issue_id", deleteCond).
if err = sess. Delete(&Reaction{}); err != nil {
In("issue_id", issueIDs). return err
Find(&attachments); err != nil { }
return err
}
for j := range attachments {
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
}
if _, err = sess.In("issue_id", issueIDs).Delete(&Attachment{}); err != nil { if _, err = sess.In("issue_id", deleteCond).
return err Delete(&IssueWatch{}); err != nil {
} return err
}
if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil { if _, err = sess.In("issue_id", deleteCond).
return err Delete(&Stopwatch{}); err != nil {
} return err
}
attachmentPaths := make([]string, 0, 20)
attachments := make([]*Attachment, 0, len(attachmentPaths))
if err = sess.Join("INNER", "issue", "issue.id = attachment.issue_id").
Where("issue.repo_id = ?", repoID).
Find(&attachments); err != nil {
return err
}
for j := range attachments {
attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
}
if _, err = sess.In("issue_id", deleteCond).
Delete(&Attachment{}); err != nil {
return err
}
if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil {
return err
} }
if _, err = sess.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil { if _, err = sess.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil {
@ -2447,7 +2458,7 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R
repoPath := RepoPath(u.Name, repo.Name) repoPath := RepoPath(u.Name, repo.Name)
_, stderr, err := process.GetManager().ExecTimeout(10*time.Minute, _, stderr, err := process.GetManager().ExecTimeout(10*time.Minute,
fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name), fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name),
"git", "clone", "--bare", oldRepo.RepoPath(), repoPath) "git", "clone", "--bare", oldRepo.repoPath(sess), repoPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("git clone: %v", stderr) return nil, fmt.Errorf("git clone: %v", stderr)
} }

View File

@ -173,11 +173,9 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
cond = cond.And(builder.Eq{"is_private": false}) cond = cond.And(builder.Eq{"is_private": false})
} }
var starred bool
if opts.OwnerID > 0 { if opts.OwnerID > 0 {
if opts.Starred { if opts.Starred {
starred = true cond = cond.And(builder.In("id", builder.Select("repo_id").From("star").Where(builder.Eq{"uid": opts.OwnerID})))
cond = builder.Eq{"star.uid": opts.OwnerID}
} else { } else {
var accessCond = builder.NewCond() var accessCond = builder.NewCond()
if opts.Collaborate != util.OptionalBoolTrue { if opts.Collaborate != util.OptionalBoolTrue {
@ -204,12 +202,23 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
} }
if opts.Keyword != "" { if opts.Keyword != "" {
var keywordCond = builder.NewCond() // separate keyword
if opts.TopicOnly { var subQueryCond = builder.NewCond()
keywordCond = keywordCond.Or(builder.Like{"topic.name", strings.ToLower(opts.Keyword)}) for _, v := range strings.Split(opts.Keyword, ",") {
} else { subQueryCond = subQueryCond.Or(builder.Like{"topic.name", strings.ToLower(v)})
keywordCond = keywordCond.Or(builder.Like{"lower_name", strings.ToLower(opts.Keyword)}) }
keywordCond = keywordCond.Or(builder.Like{"topic.name", strings.ToLower(opts.Keyword)}) subQuery := builder.Select("repo_topic.repo_id").From("repo_topic").
Join("INNER", "topic", "topic.id = repo_topic.topic_id").
Where(subQueryCond).
GroupBy("repo_topic.repo_id")
var keywordCond = builder.In("id", subQuery)
if !opts.TopicOnly {
var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
}
keywordCond = keywordCond.Or(likes)
} }
cond = cond.And(keywordCond) cond = cond.And(keywordCond)
} }
@ -229,15 +238,6 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
sess := x.NewSession() sess := x.NewSession()
defer sess.Close() defer sess.Close()
if starred {
sess.Join("INNER", "star", "star.repo_id = repository.id")
}
if opts.Keyword != "" {
sess.Join("LEFT", "repo_topic", "repo_topic.repo_id = repository.id")
sess.Join("LEFT", "topic", "repo_topic.topic_id = topic.id")
}
count, err := sess. count, err := sess.
Where(cond). Where(cond).
Count(new(Repository)) Count(new(Repository))
@ -246,27 +246,10 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
return nil, 0, fmt.Errorf("Count: %v", err) return nil, 0, fmt.Errorf("Count: %v", err)
} }
// Set again after reset by Count()
if starred {
sess.Join("INNER", "star", "star.repo_id = repository.id")
}
if opts.Keyword != "" {
sess.Join("LEFT", "repo_topic", "repo_topic.repo_id = repository.id")
sess.Join("LEFT", "topic", "repo_topic.topic_id = topic.id")
}
if opts.Keyword != "" {
sess.Select("repository.*")
sess.GroupBy("repository.id")
sess.OrderBy("repository." + opts.OrderBy.String())
} else {
sess.OrderBy(opts.OrderBy.String())
}
repos := make(RepositoryList, 0, opts.PageSize) repos := make(RepositoryList, 0, opts.PageSize)
if err = sess. if err = sess.
Where(cond). Where(cond).
OrderBy(opts.OrderBy.String()).
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&repos); err != nil { Find(&repos); err != nil {
return nil, 0, fmt.Errorf("Repo: %v", err) return nil, 0, fmt.Errorf("Repo: %v", err)

View File

@ -237,6 +237,9 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
{name: "AllPublic/OnlySearchPublicRepositoriesFromTopic", {name: "AllPublic/OnlySearchPublicRepositoriesFromTopic",
opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true}, opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
count: 1}, count: 1},
{name: "AllPublic/OnlySearchMultipleKeywordPublicRepositoriesFromTopic",
opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
count: 2},
} }
for _, testCase := range testCases { for _, testCase := range testCases {

View File

@ -535,6 +535,7 @@ func DeletePublicKey(doer *User, id int64) (err error) {
if err = sess.Commit(); err != nil { if err = sess.Commit(); err != nil {
return err return err
} }
sess.Close()
return RewriteAllPublicKeys() return RewriteAllPublicKeys()
} }
@ -543,6 +544,10 @@ func DeletePublicKey(doer *User, id int64) (err error) {
// Note: x.Iterate does not get latest data after insert/delete, so we have to call this function // Note: x.Iterate does not get latest data after insert/delete, so we have to call this function
// outside any session scope independently. // outside any session scope independently.
func RewriteAllPublicKeys() error { func RewriteAllPublicKeys() error {
return rewriteAllPublicKeys(x)
}
func rewriteAllPublicKeys(e Engine) error {
//Don't rewrite key if internal server //Don't rewrite key if internal server
if setting.SSH.StartBuiltinServer { if setting.SSH.StartBuiltinServer {
return nil return nil
@ -569,7 +574,7 @@ func RewriteAllPublicKeys() error {
} }
} }
err = x.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) { err = e.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString()) _, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
return err return err
}) })
@ -821,6 +826,11 @@ func DeleteDeployKey(doer *User, id int64) error {
if err = deletePublicKeys(sess, key.KeyID); err != nil { if err = deletePublicKeys(sess, key.KeyID); err != nil {
return err return err
} }
// after deleted the public keys, should rewrite the public keys file
if err = rewriteAllPublicKeys(sess); err != nil {
return err
}
} }
return sess.Commit() return sess.Commit()

View File

@ -197,14 +197,15 @@ func newCommitStatus(sess *xorm.Session, opts NewCommitStatusOptions) error {
return fmt.Errorf("newCommitStatus[nil, %s]: no repository specified", opts.SHA) return fmt.Errorf("newCommitStatus[nil, %s]: no repository specified", opts.SHA)
} }
opts.CommitStatus.RepoID = opts.Repo.ID opts.CommitStatus.RepoID = opts.Repo.ID
repoPath := opts.Repo.repoPath(sess)
if opts.Creator == nil { if opts.Creator == nil {
return fmt.Errorf("newCommitStatus[%s, %s]: no user specified", opts.Repo.RepoPath(), opts.SHA) return fmt.Errorf("newCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
} }
gitRepo, err := git.OpenRepository(opts.Repo.RepoPath()) gitRepo, err := git.OpenRepository(repoPath)
if err != nil { if err != nil {
return fmt.Errorf("OpenRepository[%s]: %v", opts.Repo.RepoPath(), err) return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err)
} }
if _, err := gitRepo.GetCommit(opts.SHA); err != nil { if _, err := gitRepo.GetCommit(opts.SHA); err != nil {
return fmt.Errorf("GetCommit[%s]: %v", opts.SHA, err) return fmt.Errorf("GetCommit[%s]: %v", opts.SHA, err)
@ -219,19 +220,19 @@ func newCommitStatus(sess *xorm.Session, opts NewCommitStatusOptions) error {
has, err := sess.Desc("index").Limit(1).Get(lastCommitStatus) has, err := sess.Desc("index").Limit(1).Get(lastCommitStatus)
if err != nil { if err != nil {
sess.Rollback() sess.Rollback()
return fmt.Errorf("newCommitStatus[%s, %s]: %v", opts.Repo.RepoPath(), opts.SHA, err) return fmt.Errorf("newCommitStatus[%s, %s]: %v", repoPath, opts.SHA, err)
} }
if has { if has {
log.Debug("newCommitStatus[%s, %s]: found", opts.Repo.RepoPath(), opts.SHA) log.Debug("newCommitStatus[%s, %s]: found", repoPath, opts.SHA)
nextIndex = lastCommitStatus.Index nextIndex = lastCommitStatus.Index
} }
opts.CommitStatus.Index = nextIndex + 1 opts.CommitStatus.Index = nextIndex + 1
log.Debug("newCommitStatus[%s, %s]: %d", opts.Repo.RepoPath(), opts.SHA, opts.CommitStatus.Index) log.Debug("newCommitStatus[%s, %s]: %d", repoPath, opts.SHA, opts.CommitStatus.Index)
// Insert new CommitStatus // Insert new CommitStatus
if _, err = sess.Insert(opts.CommitStatus); err != nil { if _, err = sess.Insert(opts.CommitStatus); err != nil {
sess.Rollback() sess.Rollback()
return fmt.Errorf("newCommitStatus[%s, %s]: %v", opts.Repo.RepoPath(), opts.SHA, err) return fmt.Errorf("newCommitStatus[%s, %s]: %v", repoPath, opts.SHA, err)
} }
return nil return nil

View File

@ -26,7 +26,7 @@ var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)
// Topic represents a topic of repositories // Topic represents a topic of repositories
type Topic struct { type Topic struct {
ID int64 ID int64
Name string `xorm:"UNIQUE"` Name string `xorm:"UNIQUE VARCHAR(25)"`
RepoCount int RepoCount int
CreatedUnix util.TimeStamp `xorm:"INDEX created"` CreatedUnix util.TimeStamp `xorm:"INDEX created"`
UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`

View File

@ -4,6 +4,10 @@
package models package models
import (
"strings"
)
// UnitType is Unit's Type // UnitType is Unit's Type
type UnitType int type UnitType int
@ -137,3 +141,16 @@ var (
UnitTypeExternalWiki: UnitExternalWiki, UnitTypeExternalWiki: UnitExternalWiki,
} }
) )
// FindUnitTypes give the unit key name and return unit
func FindUnitTypes(nameKeys ...string) (res []UnitType) {
for _, key := range nameKeys {
for t, u := range Units {
if strings.EqualFold(key, u.NameKey) {
res = append(res, t)
break
}
}
}
return
}

View File

@ -1038,25 +1038,26 @@ func deleteUser(e *xorm.Session, u *User) error {
&EmailAddress{UID: u.ID}, &EmailAddress{UID: u.ID},
&UserOpenID{UID: u.ID}, &UserOpenID{UID: u.ID},
&Reaction{UserID: u.ID}, &Reaction{UserID: u.ID},
&TeamUser{UID: u.ID},
&Collaboration{UserID: u.ID},
&Stopwatch{UserID: u.ID},
); err != nil { ); err != nil {
return fmt.Errorf("deleteBeans: %v", err) return fmt.Errorf("deleteBeans: %v", err)
} }
// ***** START: PublicKey ***** // ***** START: PublicKey *****
keys := make([]*PublicKey, 0, 10) if _, err = e.Delete(&PublicKey{OwnerID: u.ID}); err != nil {
if err = e.Find(&keys, &PublicKey{OwnerID: u.ID}); err != nil {
return fmt.Errorf("get all public keys: %v", err)
}
keyIDs := make([]int64, len(keys))
for i := range keys {
keyIDs[i] = keys[i].ID
}
if err = deletePublicKeys(e, keyIDs...); err != nil {
return fmt.Errorf("deletePublicKeys: %v", err) return fmt.Errorf("deletePublicKeys: %v", err)
} }
rewriteAllPublicKeys(e)
// ***** END: PublicKey ***** // ***** END: PublicKey *****
// ***** START: GPGPublicKey *****
if _, err = e.Delete(&GPGKey{OwnerID: u.ID}); err != nil {
return fmt.Errorf("deleteGPGKeys: %v", err)
}
// ***** END: GPGPublicKey *****
// Clear assignee. // Clear assignee.
if err = clearAssigneeByUserID(e, u.ID); err != nil { if err = clearAssigneeByUserID(e, u.ID); err != nil {
return fmt.Errorf("clear assignee: %v", err) return fmt.Errorf("clear assignee: %v", err)
@ -1110,6 +1111,7 @@ func DeleteUser(u *User) (err error) {
if err = sess.Commit(); err != nil { if err = sess.Commit(); err != nil {
return err return err
} }
sess.Close()
return RewriteAllPublicKeys() return RewriteAllPublicKeys()
} }

View File

@ -93,7 +93,7 @@ func DeleteUserOpenID(openid *UserOpenID) (err error) {
// ToggleUserOpenIDVisibility toggles visibility of an openid address of given user. // ToggleUserOpenIDVisibility toggles visibility of an openid address of given user.
func ToggleUserOpenIDVisibility(id int64) (err error) { func ToggleUserOpenIDVisibility(id int64) (err error) {
_, err = x.Exec("update user_open_id set show = not show where id = ?", id) _, err = x.Exec("update `user_open_id` set `show` = not `show` where `id` = ?", id)
return err return err
} }

View File

@ -377,6 +377,7 @@ type CodeCommentForm struct {
Line int64 Line int64
TreePath string `form:"path" binding:"Required"` TreePath string `form:"path" binding:"Required"`
IsReview bool `form:"is_review"` IsReview bool `form:"is_review"`
Reply int64 `form:"reply"`
} }
// Validate validates the fields // Validate validates the fields

View File

@ -8,6 +8,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/go-macaron/csrf"
"code.gitea.io/git" "code.gitea.io/git"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@ -97,6 +99,17 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
} }
} }
// RequireCSRF requires a validated a CSRF token
func (ctx *APIContext) RequireCSRF() {
headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName())
formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName())
if len(headerToken) > 0 || len(formValueToken) > 0 {
csrf.Validate(ctx.Context.Context, ctx.csrf)
} else {
ctx.Context.Error(401)
}
}
// 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) {

View File

@ -60,7 +60,7 @@ func InitIssueIndexer(populateIndexer func() error) {
return return
} }
if err = createIssueIndexer(); err != nil { if err = createIssueIndexer(setting.Indexer.IssuePath, issueIndexerLatestVersion); err != nil {
log.Fatal(4, "InitIssuesIndexer: create index, %v", err) log.Fatal(4, "InitIssuesIndexer: create index, %v", err)
} }
if err = populateIndexer(); err != nil { if err = populateIndexer(); err != nil {
@ -69,7 +69,7 @@ func InitIssueIndexer(populateIndexer func() error) {
} }
// createIssueIndexer create an issue indexer if one does not already exist // createIssueIndexer create an issue indexer if one does not already exist
func createIssueIndexer() error { func createIssueIndexer(path string, latestVersion int) error {
mapping := bleve.NewIndexMapping() mapping := bleve.NewIndexMapping()
docMapping := bleve.NewDocumentMapping() docMapping := bleve.NewDocumentMapping()
@ -100,8 +100,14 @@ func createIssueIndexer() error {
mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping()) mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping())
var err error var err error
issueIndexer, err = bleve.New(setting.Indexer.IssuePath, mapping) issueIndexer, err = bleve.New(path, mapping)
return err if err != nil {
return err
}
return rupture.WriteIndexMetadata(path, &rupture.IndexMetadata{
Version: latestVersion,
})
} }
// IssueIndexerBatch batch to add updates to // IssueIndexerBatch batch to add updates to

View File

@ -84,7 +84,7 @@ func InitRepoIndexer(populateIndexer func() error) {
return return
} }
if err = createRepoIndexer(); err != nil { if err = createRepoIndexer(setting.Indexer.RepoPath, repoIndexerLatestVersion); err != nil {
log.Fatal(4, "CreateRepoIndexer: %v", err) log.Fatal(4, "CreateRepoIndexer: %v", err)
} }
if err = populateIndexer(); err != nil { if err = populateIndexer(); err != nil {
@ -93,7 +93,7 @@ func InitRepoIndexer(populateIndexer func() error) {
} }
// createRepoIndexer create a repo indexer if one does not already exist // createRepoIndexer create a repo indexer if one does not already exist
func createRepoIndexer() error { func createRepoIndexer(path string, latestVersion int) error {
var err error var err error
docMapping := bleve.NewDocumentMapping() docMapping := bleve.NewDocumentMapping()
numericFieldMapping := bleve.NewNumericFieldMapping() numericFieldMapping := bleve.NewNumericFieldMapping()
@ -119,8 +119,13 @@ func createRepoIndexer() error {
mapping.AddDocumentMapping(repoIndexerDocType, docMapping) mapping.AddDocumentMapping(repoIndexerDocType, docMapping)
mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping()) mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping())
repoIndexer, err = bleve.New(setting.Indexer.RepoPath, mapping) repoIndexer, err = bleve.New(path, mapping)
return err if err != nil {
return err
}
return rupture.WriteIndexMetadata(path, &rupture.IndexMetadata{
Version: latestVersion,
})
} }
func filenameIndexerID(repoID int64, filename string) string { func filenameIndexerID(repoID int64, filename string) string {

View File

@ -8,7 +8,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -25,9 +24,6 @@ type FileLogWriter struct {
// The opened file // The opened file
Filename string `json:"filename"` Filename string `json:"filename"`
Maxlines int `json:"maxlines"`
maxlinesCurlines int
// Rotate at size // Rotate at size
Maxsize int `json:"maxsize"` Maxsize int `json:"maxsize"`
maxsizeCursize int maxsizeCursize int
@ -69,7 +65,6 @@ func (l *MuxWriter) SetFd(fd *os.File) {
func NewFileWriter() LoggerInterface { func NewFileWriter() LoggerInterface {
w := &FileLogWriter{ w := &FileLogWriter{
Filename: "", Filename: "",
Maxlines: 1000000,
Maxsize: 1 << 28, //256 MB Maxsize: 1 << 28, //256 MB
Daily: true, Daily: true,
Maxdays: 7, Maxdays: 7,
@ -87,7 +82,6 @@ func NewFileWriter() LoggerInterface {
// config like: // config like:
// { // {
// "filename":"log/gogs.log", // "filename":"log/gogs.log",
// "maxlines":10000,
// "maxsize":1<<30, // "maxsize":1<<30,
// "daily":true, // "daily":true,
// "maxdays":15, // "maxdays":15,
@ -116,15 +110,13 @@ func (w *FileLogWriter) StartLogger() error {
func (w *FileLogWriter) docheck(size int) { func (w *FileLogWriter) docheck(size int) {
w.startLock.Lock() w.startLock.Lock()
defer w.startLock.Unlock() defer w.startLock.Unlock()
if w.Rotate && ((w.Maxlines > 0 && w.maxlinesCurlines >= w.Maxlines) || if w.Rotate && ((w.Maxsize > 0 && w.maxsizeCursize >= w.Maxsize) ||
(w.Maxsize > 0 && w.maxsizeCursize >= w.Maxsize) ||
(w.Daily && time.Now().Day() != w.dailyOpenDate)) { (w.Daily && time.Now().Day() != w.dailyOpenDate)) {
if err := w.DoRotate(); err != nil { if err := w.DoRotate(); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
return return
} }
} }
w.maxlinesCurlines++
w.maxsizeCursize += size w.maxsizeCursize += size
} }
@ -152,15 +144,6 @@ func (w *FileLogWriter) initFd() error {
} }
w.maxsizeCursize = int(finfo.Size()) w.maxsizeCursize = int(finfo.Size())
w.dailyOpenDate = time.Now().Day() w.dailyOpenDate = time.Now().Day()
if finfo.Size() > 0 {
content, err := ioutil.ReadFile(w.Filename)
if err != nil {
return err
}
w.maxlinesCurlines = len(strings.Split(string(content), "\n"))
} else {
w.maxlinesCurlines = 0
}
return nil return nil
} }

View File

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2018 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.
@ -38,7 +39,16 @@ func (r *Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []
link = []byte(mLink) link = []byte(mLink)
} }
r.Renderer.Link(out, link, title, content) if len(content) > 10 && string(content[0:9]) == "<a href=\"" && bytes.Contains(content[9:], []byte("<img")) {
// Image with link case: markdown `[![]()]()`
// If the content is an image, then we change the original href around it
// which points to itself to a new address "link"
rightQuote := bytes.Index(content[9:], []byte("\""))
content = bytes.Replace(content, content[9:9+rightQuote], link, 1)
out.Write(content)
} else {
r.Renderer.Link(out, link, title, content)
}
} }
// List renders markdown bullet or digit lists to HTML // List renders markdown bullet or digit lists to HTML
@ -90,13 +100,6 @@ func (r *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
r.Renderer.ListItem(out, text, flags) r.Renderer.ListItem(out, text, flags)
} }
// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
var (
svgSuffix = []byte(".svg")
svgSuffixWithMark = []byte(".svg?")
)
// Image defines how images should be processed to produce corresponding HTML elements. // Image defines how images should be processed to produce corresponding HTML elements.
func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
prefix := r.URLPrefix prefix := r.URLPrefix
@ -104,22 +107,14 @@ func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byt
prefix = util.URLJoin(prefix, "wiki", "raw") prefix = util.URLJoin(prefix, "wiki", "raw")
} }
prefix = strings.Replace(prefix, "/src/", "/raw/", 1) prefix = strings.Replace(prefix, "/src/", "/raw/", 1)
if len(link) > 0 { if len(link) > 0 && !markup.IsLink(link) {
if markup.IsLink(link) { lnk := string(link)
// External link with .svg suffix usually means CI status. lnk = util.URLJoin(prefix, lnk)
// TODO: define a keyword to allow non-svg images render as external link. lnk = strings.Replace(lnk, " ", "+", -1)
if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) { link = []byte(lnk)
r.Renderer.Image(out, link, title, alt)
return
}
} else {
lnk := string(link)
lnk = util.URLJoin(prefix, lnk)
lnk = strings.Replace(lnk, " ", "+", -1)
link = []byte(lnk)
}
} }
// Put a link around it pointing to itself by default
out.WriteString(`<a href="`) out.WriteString(`<a href="`)
out.Write(link) out.Write(link)
out.WriteString(`">`) out.WriteString(`">`)

View File

@ -73,6 +73,7 @@ func TestRender_Images(t *testing.T) {
url := "../../.images/src/02/train.jpg" url := "../../.images/src/02/train.jpg"
title := "Train" title := "Train"
href := "https://gitea.io"
result := util.URLJoin(AppSubURL, url) result := util.URLJoin(AppSubURL, url)
test( test(
@ -82,6 +83,9 @@ func TestRender_Images(t *testing.T) {
test( test(
"[["+title+"|"+url+"]]", "[["+title+"|"+url+"]]",
`<p><a href="`+result+`" rel="nofollow"><img src="`+result+`" title="`+title+`" alt="`+title+`"/></a></p>`) `<p><a href="`+result+`" rel="nofollow"><img src="`+result+`" title="`+title+`" alt="`+title+`"/></a></p>`)
test(
"[!["+title+"]("+url+")]("+href+")",
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
} }
func testAnswers(baseURLContent, baseURLImages string) []string { func testAnswers(baseURLContent, baseURLImages string) []string {

View File

@ -117,7 +117,7 @@ func (opts *Options) handle(ctx *macaron.Context, log *log.Logger, opt *Options)
if fi.IsDir() { if fi.IsDir() {
// Redirect if missing trailing slash. // Redirect if missing trailing slash.
if !strings.HasSuffix(ctx.Req.URL.Path, "/") { if !strings.HasSuffix(ctx.Req.URL.Path, "/") {
http.Redirect(ctx.Resp, ctx.Req.Request, ctx.Req.URL.Path+"/", http.StatusFound) http.Redirect(ctx.Resp, ctx.Req.Request, path.Clean(ctx.Req.URL.Path+"/"), http.StatusFound)
return true return true
} }

View File

@ -1325,10 +1325,9 @@ func newLogService() {
} }
LogConfigs[i] = fmt.Sprintf( LogConfigs[i] = fmt.Sprintf(
`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level, `{"level":%s,"filename":"%s","rotate":%v,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
logPath, logPath,
sec.Key("LOG_ROTATE").MustBool(true), sec.Key("LOG_ROTATE").MustBool(true),
sec.Key("MAX_LINES").MustInt(1000000),
1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)), 1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
sec.Key("DAILY_ROTATE").MustBool(true), sec.Key("DAILY_ROTATE").MustBool(true),
sec.Key("MAX_DAYS").MustInt(7)) sec.Key("MAX_DAYS").MustInt(7))
@ -1391,10 +1390,9 @@ func NewXORMLogService(disableConsole bool) {
logPath = path.Join(filepath.Dir(logPath), "xorm.log") logPath = path.Join(filepath.Dir(logPath), "xorm.log")
logConfigs = fmt.Sprintf( logConfigs = fmt.Sprintf(
`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level, `{"level":%s,"filename":"%s","rotate":%v,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
logPath, logPath,
sec.Key("LOG_ROTATE").MustBool(true), sec.Key("LOG_ROTATE").MustBool(true),
sec.Key("MAX_LINES").MustInt(1000000),
1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)), 1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
sec.Key("DAILY_ROTATE").MustBool(true), sec.Key("DAILY_ROTATE").MustBool(true),
sec.Key("MAX_DAYS").MustInt(7)) sec.Key("MAX_DAYS").MustInt(7))

View File

@ -457,7 +457,7 @@ issues.next=Página Siguiente
issues.open_title=Abierta issues.open_title=Abierta
issues.closed_title=Cerrada issues.closed_title=Cerrada
issues.num_comments=%d comentarios issues.num_comments=%d comentarios
issues.commented_at=`comentado <a href="#%s"> %s`</a> issues.commented_at=`comentado <a href="#%s">%s</a>`
issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario? issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario?
issues.no_content=Aún no existe contenido. issues.no_content=Aún no existe contenido.
issues.close_issue=Cerrar issues.close_issue=Cerrar

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
function htmlEncode(text) {
return jQuery('<div />').text(text).html()
}
var csrf; var csrf;
var suburl; var suburl;
@ -312,12 +316,12 @@ function initCommentForm() {
switch (input_id) { switch (input_id) {
case '#milestone_id': case '#milestone_id':
$list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' + $list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' +
$(this).text() + '</a>'); htmlEncode($(this).text()) + '</a>');
break; break;
case '#assignee_id': case '#assignee_id':
$list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' + $list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' +
'<img class="ui avatar image" src=' + $(this).data('avatar') + '>' + '<img class="ui avatar image" src=' + $(this).data('avatar') + '>' +
$(this).text() + '</a>'); htmlEncode($(this).text()) + '</a>');
} }
$('.ui' + select_id + '.list .no-select').addClass('hide'); $('.ui' + select_id + '.list .no-select').addClass('hide');
$(input_id).val($(this).data('id')); $(input_id).val($(this).data('id'));
@ -604,7 +608,7 @@ function initRepository() {
// Setup new form // Setup new form
if ($editContentZone.html().length == 0) { if ($editContentZone.html().length == 0) {
$editContentZone.html($('#edit-content-form').html()); $editContentZone.html($('#edit-content-form').html());
$textarea = $('#content'); $textarea = $editContentZone.find('textarea');
issuesTribute.attach($textarea.get()); issuesTribute.attach($textarea.get());
emojiTribute.attach($textarea.get()); emojiTribute.attach($textarea.get());
@ -1456,7 +1460,7 @@ function searchUsers() {
$.each(response.data, function (i, item) { $.each(response.data, function (i, item) {
var title = item.login; var title = item.login;
if (item.full_name && item.full_name.length > 0) { if (item.full_name && item.full_name.length > 0) {
title += ' (' + item.full_name + ')'; title += ' (' + htmlEncode(item.full_name) + ')';
} }
items.push({ items.push({
title: title, title: title,
@ -1530,7 +1534,7 @@ function initU2FAuth() {
} }
u2fApi.ensureSupport() u2fApi.ensureSupport()
.then(function () { .then(function () {
$.getJSON('/user/u2f/challenge').success(function(req) { $.getJSON(suburl + '/user/u2f/challenge').success(function(req) {
u2fApi.sign(req.appId, req.challenge, req.registeredKeys, 30) u2fApi.sign(req.appId, req.challenge, req.registeredKeys, 30)
.then(u2fSigned) .then(u2fSigned)
.catch(function (err) { .catch(function (err) {
@ -1543,16 +1547,16 @@ function initU2FAuth() {
}); });
}).catch(function () { }).catch(function () {
// Fallback in case browser do not support U2F // Fallback in case browser do not support U2F
window.location.href = "/user/two_factor" window.location.href = suburl + "/user/two_factor"
}) })
} }
function u2fSigned(resp) { function u2fSigned(resp) {
$.ajax({ $.ajax({
url:'/user/u2f/sign', url: suburl + '/user/u2f/sign',
type:"POST", type: "POST",
headers: {"X-Csrf-Token": csrf}, headers: {"X-Csrf-Token": csrf},
data: JSON.stringify(resp), data: JSON.stringify(resp),
contentType:"application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
}).done(function(res){ }).done(function(res){
window.location.replace(res); window.location.replace(res);
}).fail(function (xhr, textStatus) { }).fail(function (xhr, textStatus) {
@ -1565,11 +1569,11 @@ function u2fRegistered(resp) {
return; return;
} }
$.ajax({ $.ajax({
url:'/user/settings/security/u2f/register', url: suburl + '/user/settings/security/u2f/register',
type:"POST", type: "POST",
headers: {"X-Csrf-Token": csrf}, headers: {"X-Csrf-Token": csrf},
data: JSON.stringify(resp), data: JSON.stringify(resp),
contentType:"application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
success: function(){ success: function(){
window.location.reload(); window.location.reload();
}, },
@ -1623,7 +1627,7 @@ function initU2FRegister() {
} }
function u2fRegisterRequest() { function u2fRegisterRequest() {
$.post("/user/settings/security/u2f/request_register", { $.post(suburl + "/user/settings/security/u2f/request_register", {
"_csrf": csrf, "_csrf": csrf,
"name": $('#nickname').val() "name": $('#nickname').val()
}).success(function(req) { }).success(function(req) {
@ -2510,7 +2514,7 @@ function initTopicbar() {
if (res.topics) { if (res.topics) {
formattedResponse.success = true; formattedResponse.success = true;
for (var i=0;i < res.topics.length;i++) { for (var i=0;i < res.topics.length;i++) {
formattedResponse.results.push({"description": res.topics[i].Name, "data-value":res.topics[i].Name}) formattedResponse.results.push({"description": res.topics[i].Name, "data-value": res.topics[i].Name})
} }
} }
@ -2590,6 +2594,10 @@ function updateDeadline(deadlineString) {
data: JSON.stringify({ data: JSON.stringify({
'due_date': realDeadline, 'due_date': realDeadline,
}), }),
headers: {
'X-Csrf-Token': csrf,
'X-Remote': true,
},
contentType: 'application/json', contentType: 'application/json',
type: 'POST', type: 'POST',
success: function () { success: function () {
@ -2621,13 +2629,13 @@ function initIssueList() {
$('.new-dependency-drop-list') $('.new-dependency-drop-list')
.dropdown({ .dropdown({
apiSettings: { apiSettings: {
url: '/api/v1/repos' + repolink + '/issues?q={query}', url: suburl + '/api/v1/repos/' + repolink + '/issues?q={query}',
onResponse: function(response) { onResponse: function(response) {
var filteredResponse = {'success': true, 'results': []}; var filteredResponse = {'success': true, 'results': []};
// Parse the response from the api to work with our dropdown // Parse the response from the api to work with our dropdown
$.each(response, function(index, issue) { $.each(response, function(index, issue) {
filteredResponse.results.push({ filteredResponse.results.push({
'name' : '#' + issue.number + '&nbsp;' + issue.title, 'name' : '#' + issue.number + '&nbsp;' + htmlEncode(issue.title),
'value' : issue.id 'value' : issue.id
}); });
}); });

View File

@ -174,11 +174,15 @@ func repoAssignment() macaron.Handler {
// Contexter middleware already checks token for user sign in process. // Contexter middleware already checks token for user sign in process.
func reqToken() macaron.Handler { func reqToken() macaron.Handler {
return func(ctx *context.Context) { return func(ctx *context.APIContext) {
if true != ctx.Data["IsApiToken"] { if true == ctx.Data["IsApiToken"] {
ctx.Error(401)
return return
} }
if ctx.IsSigned {
ctx.RequireCSRF()
return
}
ctx.Context.Error(401)
} }
} }
@ -627,7 +631,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
}) })
}) })
}, reqAdmin()) }, reqToken(), reqAdmin())
m.Group("/topics", func() { m.Group("/topics", func() {
m.Get("/search", repo.TopicSearch) m.Get("/search", repo.TopicSearch)

View File

@ -196,5 +196,6 @@ func ToTeam(team *models.Team) *api.Team {
Name: team.Name, Name: team.Name,
Description: team.Description, Description: team.Description,
Permission: team.Authorize.String(), Permission: team.Authorize.String(),
Units: team.GetUnitNames(),
} }
} }

View File

@ -89,6 +89,20 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
Description: form.Description, Description: form.Description,
Authorize: models.ParseAccessMode(form.Permission), Authorize: models.ParseAccessMode(form.Permission),
} }
unitTypes := models.FindUnitTypes(form.Units...)
if team.Authorize < models.AccessModeOwner {
var units = make([]*models.TeamUnit, 0, len(form.Units))
for _, tp := range unitTypes {
units = append(units, &models.TeamUnit{
OrgID: ctx.Org.Organization.ID,
Type: tp,
})
}
team.Units = units
}
if err := models.NewTeam(team); err != nil { if err := models.NewTeam(team); err != nil {
if models.IsErrTeamAlreadyExist(err) { if models.IsErrTeamAlreadyExist(err) {
ctx.Error(422, "", err) ctx.Error(422, "", err)
@ -127,6 +141,19 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
team.Name = form.Name team.Name = form.Name
team.Description = form.Description team.Description = form.Description
team.Authorize = models.ParseAccessMode(form.Permission) team.Authorize = models.ParseAccessMode(form.Permission)
unitTypes := models.FindUnitTypes(form.Units...)
if team.Authorize < models.AccessModeOwner {
var units = make([]*models.TeamUnit, 0, len(form.Units))
for _, tp := range unitTypes {
units = append(units, &models.TeamUnit{
OrgID: ctx.Org.Organization.ID,
Type: tp,
})
}
team.Units = units
}
if err := models.UpdateTeam(team, true); err != nil { if err := models.UpdateTeam(team, true); err != nil {
ctx.Error(500, "EditTeam", err) ctx.Error(500, "EditTeam", err)
return return

View File

@ -508,7 +508,7 @@ func Delete(ctx *context.APIContext) {
owner := ctx.Repo.Owner owner := ctx.Repo.Owner
repo := ctx.Repo.Repository repo := ctx.Repo.Repository
if owner.IsOrganization() { if owner.IsOrganization() && !ctx.User.IsAdmin {
isOwner, err := owner.IsOwnedBy(ctx.User.ID) isOwner, err := owner.IsOwnedBy(ctx.User.ID)
if err != nil { if err != nil {
ctx.Error(500, "IsOwnedBy", err) ctx.Error(500, "IsOwnedBy", err)

View File

@ -11,14 +11,13 @@ import (
) )
// listUserRepos - List the repositories owned by the given user. // listUserRepos - List the repositories owned by the given user.
func listUserRepos(ctx *context.APIContext, u *models.User) { func listUserRepos(ctx *context.APIContext, u *models.User, private bool) {
showPrivateRepos := ctx.IsSigned && (ctx.User.ID == u.ID || ctx.User.IsAdmin) repos, err := models.GetUserRepositories(u.ID, private, 1, u.NumRepos, "")
repos, err := models.GetUserRepositories(u.ID, showPrivateRepos, 1, u.NumRepos, "")
if err != nil { if err != nil {
ctx.Error(500, "GetUserRepositories", err) ctx.Error(500, "GetUserRepositories", err)
return return
} }
apiRepos := make([]*api.Repository, len(repos)) apiRepos := make([]*api.Repository, 0, len(repos))
var ctxUserID int64 var ctxUserID int64
if ctx.User != nil { if ctx.User != nil {
ctxUserID = ctx.User.ID ctxUserID = ctx.User.ID
@ -29,7 +28,9 @@ func listUserRepos(ctx *context.APIContext, u *models.User) {
ctx.Error(500, "AccessLevel", err) ctx.Error(500, "AccessLevel", err)
return return
} }
apiRepos[i] = repos[i].APIFormat(access) if ctx.IsSigned && ctx.User.IsAdmin || access >= models.AccessModeRead {
apiRepos = append(apiRepos, repos[i].APIFormat(access))
}
} }
ctx.JSON(200, &apiRepos) ctx.JSON(200, &apiRepos)
} }
@ -54,7 +55,8 @@ func ListUserRepos(ctx *context.APIContext) {
if ctx.Written() { if ctx.Written() {
return return
} }
listUserRepos(ctx, user) private := ctx.IsSigned && (ctx.User.ID == user.ID || ctx.User.IsAdmin)
listUserRepos(ctx, user, private)
} }
// ListMyRepos - list the repositories you own or have access to. // ListMyRepos - list the repositories you own or have access to.
@ -106,5 +108,5 @@ func ListOrgRepos(ctx *context.APIContext) {
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
listUserRepos(ctx, ctx.Org.Organization) listUserRepos(ctx, ctx.Org.Organization, ctx.IsSigned)
} }

View File

@ -163,7 +163,11 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
branchName = form.NewBranchName branchName = form.NewBranchName
} }
form.TreePath = strings.Trim(path.Clean("/"+form.TreePath), " /") form.TreePath = cleanUploadFileName(form.TreePath)
if len(form.TreePath) == 0 {
ctx.Error(500, "Upload file name is invalid")
return
}
treeNames, treePaths := getParentTreeFields(form.TreePath) treeNames, treePaths := getParentTreeFields(form.TreePath)
ctx.Data["TreePath"] = form.TreePath ctx.Data["TreePath"] = form.TreePath
@ -373,6 +377,13 @@ func DeleteFile(ctx *context.Context) {
func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
ctx.Data["PageIsDelete"] = true ctx.Data["PageIsDelete"] = true
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
ctx.Repo.TreePath = cleanUploadFileName(ctx.Repo.TreePath)
if len(ctx.Repo.TreePath) == 0 {
ctx.Error(500, "Delete file name is invalid")
return
}
ctx.Data["TreePath"] = ctx.Repo.TreePath ctx.Data["TreePath"] = ctx.Repo.TreePath
canCommit := renderCommitRights(ctx) canCommit := renderCommitRights(ctx)
@ -477,7 +488,12 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
branchName = form.NewBranchName branchName = form.NewBranchName
} }
form.TreePath = strings.Trim(path.Clean("/"+form.TreePath), " /") form.TreePath = cleanUploadFileName(form.TreePath)
if len(form.TreePath) == 0 {
ctx.Error(500, "Upload file name is invalid")
return
}
treeNames, treePaths := getParentTreeFields(form.TreePath) treeNames, treePaths := getParentTreeFields(form.TreePath)
if len(treeNames) == 0 { if len(treeNames) == 0 {
// We must at least have one element for user to input. // We must at least have one element for user to input.
@ -559,6 +575,17 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath) ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath)
} }
func cleanUploadFileName(name string) string {
name = strings.TrimLeft(name, "./\\")
name = strings.Replace(name, "../", "", -1)
name = strings.Replace(name, "..\\", "", -1)
name = strings.TrimPrefix(path.Clean(name), ".git/")
if name == ".git" {
return ""
}
return name
}
// UploadFileToServer upload file to server file dir not git // UploadFileToServer upload file to server file dir not git
func UploadFileToServer(ctx *context.Context) { func UploadFileToServer(ctx *context.Context) {
file, header, err := ctx.Req.FormFile("file") file, header, err := ctx.Req.FormFile("file")
@ -591,7 +618,13 @@ func UploadFileToServer(ctx *context.Context) {
} }
} }
upload, err := models.NewUpload(header.Filename, buf, file) name := cleanUploadFileName(header.Filename)
if len(name) == 0 {
ctx.Error(500, "Upload file name is invalid")
return
}
upload, err := models.NewUpload(name, buf, file)
if err != nil { if err != nil {
ctx.Error(500, fmt.Sprintf("NewUpload: %v", err)) ctx.Error(500, fmt.Sprintf("NewUpload: %v", err))
return return

View File

@ -0,0 +1,30 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"testing"
"code.gitea.io/gitea/models"
"github.com/stretchr/testify/assert"
)
func TestCleanUploadName(t *testing.T) {
models.PrepareTestEnv(t)
var kases = map[string]string{
".git/refs/master": "git/refs/master",
"/root/abc": "root/abc",
"./../../abc": "abc",
"a/../.git": "a/.git",
"a/../../../abc": "a/abc",
"../../../acd": "acd",
"../../.git/abc": "git/abc",
"..\\..\\.git/abc": "git/abc",
}
for k, v := range kases {
assert.EqualValues(t, v, cleanUploadFileName(k))
}
}

View File

@ -63,6 +63,9 @@ func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) {
} }
} }
} }
if review.ID == 0 {
review.ID = form.Reply
}
//FIXME check if line, commit and treepath exist //FIXME check if line, commit and treepath exist
comment, err := models.CreateCodeComment( comment, err := models.CreateCodeComment(
ctx.User, ctx.User,
@ -78,7 +81,7 @@ func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) {
return return
} }
// Send no notification if comment is pending // Send no notification if comment is pending
if !form.IsReview { if !form.IsReview || form.Reply != 0 {
notification.Service.NotifyIssue(issue, ctx.User.ID) notification.Service.NotifyIssue(issue, ctx.User.ID)
} }

View File

@ -73,7 +73,6 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err
commit, err := wikiRepo.GetBranchCommit("master") commit, err := wikiRepo.GetBranchCommit("master")
if err != nil { if err != nil {
ctx.ServerError("GetBranchCommit", err)
return wikiRepo, nil, err return wikiRepo, nil, err
} }
return wikiRepo, commit, nil return wikiRepo, commit, nil
@ -111,6 +110,9 @@ func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName strin
func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *git.TreeEntry) { func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *git.TreeEntry) {
wikiRepo, commit, err := findWikiRepoCommit(ctx) wikiRepo, commit, err := findWikiRepoCommit(ctx)
if err != nil { if err != nil {
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
return nil, nil return nil, nil
} }

View File

@ -253,6 +253,8 @@ func Issues(ctx *context.Context) {
opts.Page = page opts.Page = page
opts.PageSize = setting.UI.IssuePagingNum opts.PageSize = setting.UI.IssuePagingNum
opts.Labels = ctx.Query("labels")
issues, err := models.Issues(opts) issues, err := models.Issues(opts)
if err != nil { if err != nil {
ctx.ServerError("Issues", err) ctx.ServerError("Issues", err)

View File

@ -100,7 +100,7 @@
<dt>{{.i18n.Tr "admin.dashboard.mspan_structures_usage"}}</dt> <dt>{{.i18n.Tr "admin.dashboard.mspan_structures_usage"}}</dt>
<dd>{{.SysStatus.MSpanInuse}}</dd> <dd>{{.SysStatus.MSpanInuse}}</dd>
<dt>{{.i18n.Tr "admin.dashboard.mspan_structures_obtained"}}</dt> <dt>{{.i18n.Tr "admin.dashboard.mspan_structures_obtained"}}</dt>
<dd>{{.SysStatus.HeapSys}}</dd> <dd>{{.SysStatus.MSpanSys}}</dd>
<dt>{{.i18n.Tr "admin.dashboard.mcache_structures_usage"}}</dt> <dt>{{.i18n.Tr "admin.dashboard.mcache_structures_usage"}}</dt>
<dd>{{.SysStatus.MCacheInuse}}</dd> <dd>{{.SysStatus.MCacheInuse}}</dd>
<dt>{{.i18n.Tr "admin.dashboard.mcache_structures_obtained"}}</dt> <dt>{{.i18n.Tr "admin.dashboard.mcache_structures_obtained"}}</dt>

View File

@ -51,8 +51,6 @@
{{end}} {{end}}
{{if .RequireTribute}} {{if .RequireTribute}}
<script src="{{AppSubUrl}}/vendor/plugins/tribute/tribute.min.js"></script> <script src="{{AppSubUrl}}/vendor/plugins/tribute/tribute.min.js"></script>
{{if .Assignees}}
<script> <script>
var issuesTribute = new Tribute({ var issuesTribute = new Tribute({
values: [ values: [
@ -73,7 +71,6 @@
}) })
issuesTribute.attach(document.getElementById('content')) issuesTribute.attach(document.getElementById('content'))
</script> </script>
{{end}}
<script> <script>
var emojiTribute = new Tribute({ var emojiTribute = new Tribute({
collection: [{ collection: [{

View File

@ -151,7 +151,7 @@
{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}} {{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}}
</ui> </ui>
</div> </div>
{{template "repo/diff/comment_form_datahandler" dict "hidden" true "root" $ "comment" (index $line.Comments 0)}} {{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}}
</div> </div>
{{end}} {{end}}
</td> </td>
@ -164,7 +164,7 @@
{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}} {{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}}
</ui> </ui>
</div> </div>
{{template "repo/diff/comment_form_datahandler" dict "hidden" true "root" $ "comment" (index $line.Comments 0)}} {{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}}
</div> </div>
{{end}} {{end}}
</td> </td>
@ -204,7 +204,7 @@
<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> <a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
</div> </div>
<div class="ui bottom attached active write tab segment"> <div class="ui bottom attached active write tab segment">
<textarea tabindex="1" id="content" name="content"></textarea> <textarea tabindex="1" name="content"></textarea>
</div> </div>
<div class="ui bottom attached tab preview segment markdown"> <div class="ui bottom attached tab preview segment markdown">
{{$.i18n.Tr "loading"}} {{$.i18n.Tr "loading"}}

View File

@ -25,19 +25,19 @@
<div class="footer"> <div class="footer">
<span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span> <span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span>
<div class="ui right floated"> <div class="ui right floated">
{{if not $.reply}} {{if $.reply}}
<button name="reply" value="{{$.reply}}" class="ui submit green tiny button btn-reply">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
{{else}}
{{if $.root.CurrentReview}} {{if $.root.CurrentReview}}
<button name="is_review" value="true" type="submit" <button name="is_review" value="true" type="submit"
class="ui submit green tiny button btn-add-comment">{{$.root.i18n.Tr "repo.diff.comment.add_review_comment"}}</button> class="ui submit green tiny button btn-add-comment">{{$.root.i18n.Tr "repo.diff.comment.add_review_comment"}}</button>
{{else}} {{else}}
<button name="is_review" value="true" type="submit" <button name="is_review" value="true" type="submit"
class="ui submit green tiny button btn-start-review">{{$.root.i18n.Tr "repo.diff.comment.start_review"}}</button> class="ui submit green tiny button btn-start-review">{{$.root.i18n.Tr "repo.diff.comment.start_review"}}</button>
<button type="submit"
class="ui submit tiny basic button btn-add-single">{{$.root.i18n.Tr "repo.diff.comment.add_single_comment"}}</button>
{{end}} {{end}}
{{end}} {{end}}
{{if not $.root.CurrentReview}}
<button type="submit"
class="ui submit tiny basic button btn-add-single">{{$.root.i18n.Tr "repo.diff.comment.add_single_comment"}}</button>
{{end}}
{{if or (not $.HasComments) $.hidden}} {{if or (not $.HasComments) $.hidden}}
<button type="button" class="ui submit tiny basic button btn-cancel" onclick="cancelCodeComment(this);">{{$.root.i18n.Tr "cancel"}}</button> <button type="button" class="ui submit tiny basic button btn-cancel" onclick="cancelCodeComment(this);">{{$.root.i18n.Tr "cancel"}}</button>
{{end}} {{end}}

View File

@ -32,7 +32,7 @@
{{ template "repo/diff/comments" dict "root" $.root "comments" $line.Comments}} {{ template "repo/diff/comments" dict "root" $.root "comments" $line.Comments}}
</ui> </ui>
</div> </div>
{{template "repo/diff/comment_form_datahandler" dict "hidden" true "root" $.root "comment" (index $line.Comments 0)}} {{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index $line.Comments 0).ReviewID "root" $.root "comment" (index $line.Comments 0)}}
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -3,9 +3,7 @@
<div class="file-body file-code code-view code-diff"> <div class="file-body file-code code-view code-diff">
<table> <table>
<tbody> <tbody>
{{with .File}} {{template "repo/diff/section_unified" dict "file" .File "root" $}}
{{template "repo/diff/section_unified" .}}
{{end}}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -30,7 +30,7 @@
<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff"> <div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
<a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a> <a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a>
{{if not .IsNewFile}} {{if not .IsNewFile}}
<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a> <a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "preview"}}</a>
<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | EscapePound}}/{{.TreePath | EscapePound}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a> <a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | EscapePound}}/{{.TreePath | EscapePound}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a>
{{end}} {{end}}
</div> </div>

View File

@ -115,7 +115,7 @@
<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> <a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
</div> </div>
<div class="ui bottom attached active write tab segment"> <div class="ui bottom attached active write tab segment">
<textarea tabindex="1" id="content" name="content"></textarea> <textarea tabindex="1" name="content"></textarea>
</div> </div>
<div class="ui bottom attached tab preview segment markdown"> <div class="ui bottom attached tab preview segment markdown">
{{$.i18n.Tr "loading"}} {{$.i18n.Tr "loading"}}

View File

@ -342,7 +342,7 @@
</div> </div>
{{end}} {{end}}
</div> </div>
{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" true "root" $ "comment" (index $comms 0)}} {{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index $comms 0).ReviewID "root" $ "comment" (index $comms 0)}}
</div> </div>
</div> </div>
{{end}} {{end}}

View File

@ -338,7 +338,7 @@
</div> </div>
</div> </div>
{{if .CanCreateIssueDependencies}} {{if .CanCreateIssueDependencies}}
<input type="hidden" id="repolink" value="{{$.RepoLink}}"> <input type="hidden" id="repolink" value="{{$.RepoRelPath}}">
<!-- I know, there is probably a better way to do this --> <!-- I know, there is probably a better way to do this -->
<input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/> <input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/>

View File

@ -38,7 +38,7 @@
{{else}} {{else}}
<span class="text grey"><i class="octicon octicon-primitive-dot"></i></span> <span class="text grey"><i class="octicon octicon-primitive-dot"></i></span>
{{end}} {{end}}
<a href="{{$.BaseLink}}/settings/hooks/{{.ID}}">{{.URL}}</a> <a class="dont-break-out" href="{{$.BaseLink}}/settings/hooks/{{.ID}}">{{.URL}}</a>
<div class="ui right"> <div class="ui right">
<span class="text blue"><a href="{{$.BaseLink}}/settings/hooks/{{.ID}}"><i class="fa fa-pencil"></i></a></span> <span class="text blue"><a href="{{$.BaseLink}}/settings/hooks/{{.ID}}"><i class="fa fa-pencil"></i></a></span>
<span class="text red"><a class="delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}"><i class="fa fa-times"></i></a></span> <span class="text red"><a class="delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}"><i class="fa fa-times"></i></a></span>

View File

@ -6128,6 +6128,22 @@
"admin" "admin"
], ],
"x-go-name": "Permission" "x-go-name": "Permission"
},
"units": {
"type": "array",
"enum": [
"repo.code",
"repo.issues",
"repo.ext_issues",
"repo.wiki",
"repo.pulls",
"repo.releases",
"repo.ext_wiki"
],
"items": {
"type": "string"
},
"x-go-name": "Units"
} }
}, },
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
@ -6198,6 +6214,10 @@
"format": "date-time", "format": "date-time",
"x-go-name": "Created" "x-go-name": "Created"
}, },
"fingerprint": {
"type": "string",
"x-go-name": "Fingerprint"
},
"id": { "id": {
"type": "integer", "type": "integer",
"format": "int64", "format": "int64",
@ -6207,10 +6227,18 @@
"type": "string", "type": "string",
"x-go-name": "Key" "x-go-name": "Key"
}, },
"key_id": {
"type": "integer",
"format": "int64",
"x-go-name": "KeyID"
},
"read_only": { "read_only": {
"type": "boolean", "type": "boolean",
"x-go-name": "ReadOnly" "x-go-name": "ReadOnly"
}, },
"repository": {
"$ref": "#/definitions/Repository"
},
"title": { "title": {
"type": "string", "type": "string",
"x-go-name": "Title" "x-go-name": "Title"
@ -6491,6 +6519,22 @@
"admin" "admin"
], ],
"x-go-name": "Permission" "x-go-name": "Permission"
},
"units": {
"type": "array",
"enum": [
"repo.code",
"repo.issues",
"repo.ext_issues",
"repo.wiki",
"repo.pulls",
"repo.releases",
"repo.ext_wiki"
],
"items": {
"type": "string"
},
"x-go-name": "Units"
} }
}, },
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
@ -7092,6 +7136,14 @@
"type": "string", "type": "string",
"x-go-name": "Key" "x-go-name": "Key"
}, },
"key_type": {
"type": "string",
"x-go-name": "KeyType"
},
"read_only": {
"type": "boolean",
"x-go-name": "ReadOnly"
},
"title": { "title": {
"type": "string", "type": "string",
"x-go-name": "Title" "x-go-name": "Title"
@ -7099,6 +7151,9 @@
"url": { "url": {
"type": "string", "type": "string",
"x-go-name": "URL" "x-go-name": "URL"
},
"user": {
"$ref": "#/definitions/User"
} }
}, },
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
@ -7313,6 +7368,10 @@
"description": "Repository represents a repository", "description": "Repository represents a repository",
"type": "object", "type": "object",
"properties": { "properties": {
"archived": {
"type": "boolean",
"x-go-name": "Archived"
},
"clone_url": { "clone_url": {
"type": "string", "type": "string",
"x-go-name": "CloneURL" "x-go-name": "CloneURL"
@ -7523,6 +7582,22 @@
"owner" "owner"
], ],
"x-go-name": "Permission" "x-go-name": "Permission"
},
"units": {
"type": "array",
"enum": [
"repo.code",
"repo.issues",
"repo.ext_issues",
"repo.wiki",
"repo.pulls",
"repo.releases",
"repo.ext_wiki"
],
"items": {
"type": "string"
},
"x-go-name": "Units"
} }
}, },
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"

View File

@ -71,7 +71,7 @@
especially on mobile views. */}} especially on mobile views. */}}
<span style="line-height: 2.5"> <span style="line-height: 2.5">
{{range .}} {{range .}}
<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description}}">{{.Name}}</a> <a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&repo={{$.RepoID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}" title="{{.Description}}">{{.Name}}</a>
{{end}} {{end}}
</span> </span>
{{end}} {{end}}

View File

@ -42,17 +42,17 @@ type EditUserOption struct {
FullName string `json:"full_name" binding:"MaxSize(100)"` FullName string `json:"full_name" binding:"MaxSize(100)"`
// required: true // required: true
// swagger:strfmt email // swagger:strfmt email
Email string `json:"email" binding:"Required;Email;MaxSize(254)"` Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
Password string `json:"password" binding:"MaxSize(255)"` Password string `json:"password" binding:"MaxSize(255)"`
Website string `json:"website" binding:"MaxSize(50)"` Website string `json:"website" binding:"MaxSize(50)"`
Location string `json:"location" binding:"MaxSize(50)"` Location string `json:"location" binding:"MaxSize(50)"`
Active *bool `json:"active"` Active *bool `json:"active"`
Admin *bool `json:"admin"` Admin *bool `json:"admin"`
AllowGitHook *bool `json:"allow_git_hook"` AllowGitHook *bool `json:"allow_git_hook"`
AllowImportLocal *bool `json:"allow_import_local"` AllowImportLocal *bool `json:"allow_import_local"`
MaxRepoCreation *int `json:"max_repo_creation"` MaxRepoCreation *int `json:"max_repo_creation"`
ProhibitLogin *bool `json:"prohibit_login"` ProhibitLogin *bool `json:"prohibit_login"`
AllowCreateOrganization *bool `json:"allow_create_organization"` AllowCreateOrganization *bool `json:"allow_create_organization"`
} }
// AdminEditUser modify user informations // AdminEditUser modify user informations

View File

@ -199,7 +199,7 @@ type CreatePayload struct {
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret FIXME // SetSecret modifies the secret of the CreatePayload
func (p *CreatePayload) SetSecret(secret string) { func (p *CreatePayload) SetSecret(secret string) {
p.Secret = secret p.Secret = secret
} }
@ -246,6 +246,7 @@ const (
// DeletePayload represents delete payload // DeletePayload represents delete payload
type DeletePayload struct { type DeletePayload struct {
Secret string `json:"secret"`
Ref string `json:"ref"` Ref string `json:"ref"`
RefType string `json:"ref_type"` RefType string `json:"ref_type"`
PusherType PusherType `json:"pusher_type"` PusherType PusherType `json:"pusher_type"`
@ -253,8 +254,9 @@ type DeletePayload struct {
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret implements Payload // SetSecret modifies the secret of the DeletePayload
func (p *DeletePayload) SetSecret(secret string) { func (p *DeletePayload) SetSecret(secret string) {
p.Secret = secret
} }
// JSONPayload implements Payload // JSONPayload implements Payload
@ -271,13 +273,15 @@ func (p *DeletePayload) JSONPayload() ([]byte, error) {
// ForkPayload represents fork payload // ForkPayload represents fork payload
type ForkPayload struct { type ForkPayload struct {
Secret string `json:"secret"`
Forkee *Repository `json:"forkee"` Forkee *Repository `json:"forkee"`
Repo *Repository `json:"repository"` Repo *Repository `json:"repository"`
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret implements Payload // SetSecret modifies the secret of the ForkPayload
func (p *ForkPayload) SetSecret(secret string) { func (p *ForkPayload) SetSecret(secret string) {
p.Secret = secret
} }
// JSONPayload implements Payload // JSONPayload implements Payload
@ -297,6 +301,7 @@ const (
// IssueCommentPayload represents a payload information of issue comment event. // IssueCommentPayload represents a payload information of issue comment event.
type IssueCommentPayload struct { type IssueCommentPayload struct {
Secret string `json:"secret"`
Action HookIssueCommentAction `json:"action"` Action HookIssueCommentAction `json:"action"`
Issue *Issue `json:"issue"` Issue *Issue `json:"issue"`
Comment *Comment `json:"comment"` Comment *Comment `json:"comment"`
@ -305,8 +310,9 @@ type IssueCommentPayload struct {
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret implements Payload // SetSecret modifies the secret of the IssueCommentPayload
func (p *IssueCommentPayload) SetSecret(secret string) { func (p *IssueCommentPayload) SetSecret(secret string) {
p.Secret = secret
} }
// JSONPayload implements Payload // JSONPayload implements Payload
@ -333,14 +339,16 @@ const (
// ReleasePayload represents a payload information of release event. // ReleasePayload represents a payload information of release event.
type ReleasePayload struct { type ReleasePayload struct {
Secret string `json:"secret"`
Action HookReleaseAction `json:"action"` Action HookReleaseAction `json:"action"`
Release *Release `json:"release"` Release *Release `json:"release"`
Repository *Repository `json:"repository"` Repository *Repository `json:"repository"`
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret implements Payload // SetSecret modifies the secret of the ReleasePayload
func (p *ReleasePayload) SetSecret(secret string) { func (p *ReleasePayload) SetSecret(secret string) {
p.Secret = secret
} }
// JSONPayload implements Payload // JSONPayload implements Payload
@ -368,7 +376,7 @@ type PushPayload struct {
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret FIXME // SetSecret modifies the secret of the PushPayload
func (p *PushPayload) SetSecret(secret string) { func (p *PushPayload) SetSecret(secret string) {
p.Secret = secret p.Secret = secret
} }
@ -520,7 +528,7 @@ type RepositoryPayload struct {
Sender *User `json:"sender"` Sender *User `json:"sender"`
} }
// SetSecret set the payload's secret // SetSecret modifies the secret of the RepositoryPayload
func (p *RepositoryPayload) SetSecret(secret string) { func (p *RepositoryPayload) SetSecret(secret string) {
p.Secret = secret p.Secret = secret
} }

View File

@ -152,3 +152,9 @@ type IssueDeadline struct {
// swagger:strfmt date-time // swagger:strfmt date-time
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
} }
// EditPriorityOption options for updating priority
type EditPriorityOption struct {
// required:true
Priority int `json:"priority"`
}

View File

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2018 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.
@ -11,6 +12,8 @@ type Team struct {
Description string `json:"description"` Description string `json:"description"`
// enum: none,read,write,admin,owner // enum: none,read,write,admin,owner
Permission string `json:"permission"` Permission string `json:"permission"`
// enum: repo.code,repo.issues,repo.ext_issues,repo.wiki,repo.pulls,repo.releases,repo.ext_wiki
Units []string `json:"units"`
} }
// CreateTeamOption options for creating a team // CreateTeamOption options for creating a team
@ -20,6 +23,8 @@ type CreateTeamOption struct {
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(255)"`
// enum: read,write,admin // enum: read,write,admin
Permission string `json:"permission"` Permission string `json:"permission"`
// enum: repo.code,repo.issues,repo.ext_issues,repo.wiki,repo.pulls,repo.releases,repo.ext_wiki
Units []string `json:"units"`
} }
// EditTeamOption options for editing a team // EditTeamOption options for editing a team
@ -29,4 +34,6 @@ type EditTeamOption struct {
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(255)"`
// enum: read,write,admin // enum: read,write,admin
Permission string `json:"permission"` Permission string `json:"permission"`
// enum: repo.code,repo.issues,repo.ext_issues,repo.wiki,repo.pulls,repo.releases,repo.ext_wiki
Units []string `json:"units"`
} }

View File

@ -40,6 +40,7 @@ type Repository struct {
Watchers int `json:"watchers_count"` Watchers int `json:"watchers_count"`
OpenIssues int `json:"open_issues_count"` OpenIssues int `json:"open_issues_count"`
DefaultBranch string `json:"default_branch"` DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"`
// swagger:strfmt date-time // swagger:strfmt date-time
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
// swagger:strfmt date-time // swagger:strfmt date-time

View File

@ -13,13 +13,16 @@ import (
// DeployKey a deploy key // DeployKey a deploy key
type DeployKey struct { type DeployKey struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Key string `json:"key"` KeyID int64 `json:"key_id"`
URL string `json:"url"` Key string `json:"key"`
Title string `json:"title"` URL string `json:"url"`
Title string `json:"title"`
Fingerprint string `json:"fingerprint"`
// swagger:strfmt date-time // swagger:strfmt date-time
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
ReadOnly bool `json:"read_only"` ReadOnly bool `json:"read_only"`
Repository *Repository `json:"repository,omitempty"`
} }
// ListDeployKeys list all the deploy keys of one repository // ListDeployKeys list all the deploy keys of one repository

View File

@ -19,7 +19,10 @@ type PublicKey struct {
Title string `json:"title,omitempty"` Title string `json:"title,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"` Fingerprint string `json:"fingerprint,omitempty"`
// swagger:strfmt date-time // swagger:strfmt date-time
Created time.Time `json:"created_at,omitempty"` Created time.Time `json:"created_at,omitempty"`
Owner *User `json:"user,omitempty"`
ReadOnly bool `json:"read_only,omitempty"`
KeyType string `json:"key_type,omitempty"`
} }
// ListPublicKeys list all the public keys of the user // ListPublicKeys list all the public keys of the user

View File

@ -86,7 +86,7 @@ func (s *FileStore) Release() error {
return err return err
} }
return ioutil.WriteFile(s.p.filepath(s.sid), data, os.ModePerm) return ioutil.WriteFile(s.p.filepath(s.sid), data, 0600)
} }
// Flush deletes all session data. // Flush deletes all session data.
@ -121,7 +121,7 @@ func (p *FileProvider) filepath(sid string) string {
// Read returns raw session store by session ID. // Read returns raw session store by session ID.
func (p *FileProvider) Read(sid string) (_ RawStore, err error) { func (p *FileProvider) Read(sid string) (_ RawStore, err error) {
filename := p.filepath(sid) filename := p.filepath(sid)
if err = os.MkdirAll(path.Dir(filename), os.ModePerm); err != nil { if err = os.MkdirAll(path.Dir(filename), 0700); err != nil {
return nil, err return nil, err
} }
p.lock.RLock() p.lock.RLock()
@ -129,7 +129,7 @@ func (p *FileProvider) Read(sid string) (_ RawStore, err error) {
var f *os.File var f *os.File
if com.IsFile(filename) { if com.IsFile(filename) {
f, err = os.OpenFile(filename, os.O_RDWR, os.ModePerm) f, err = os.OpenFile(filename, os.O_RDONLY, 0600)
} else { } else {
f, err = os.Create(filename) f, err = os.Create(filename)
} }
@ -187,15 +187,15 @@ func (p *FileProvider) regenerate(oldsid, sid string) (err error) {
if err != nil { if err != nil {
return err return err
} }
if err = os.MkdirAll(path.Dir(oldname), os.ModePerm); err != nil { if err = os.MkdirAll(path.Dir(oldname), 0700); err != nil {
return err return err
} }
if err = ioutil.WriteFile(oldname, data, os.ModePerm); err != nil { if err = ioutil.WriteFile(oldname, data, 0600); err != nil {
return err return err
} }
} }
if err = os.MkdirAll(path.Dir(filename), os.ModePerm); err != nil { if err = os.MkdirAll(path.Dir(filename), 0700); err != nil {
return err return err
} }
if err = os.Rename(oldname, filename); err != nil { if err = os.Rename(oldname, filename); err != nil {

View File

@ -18,15 +18,17 @@ package session
import ( import (
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )
const _VERSION = "0.3.0" const _VERSION = "0.4.0"
func Version() string { func Version() string {
return _VERSION return _VERSION
@ -245,8 +247,8 @@ func NewManager(name string, opt Options) (*Manager, error) {
return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig) return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig)
} }
// sessionId generates a new session ID with rand string, unix nano time, remote addr by hash function. // sessionID generates a new session ID with rand string, unix nano time, remote addr by hash function.
func (m *Manager) sessionId() string { func (m *Manager) sessionID() string {
return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2)) return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2))
} }
@ -255,10 +257,10 @@ func (m *Manager) sessionId() string {
func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) { func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) {
sid := ctx.GetCookie(m.opt.CookieName) sid := ctx.GetCookie(m.opt.CookieName)
if len(sid) > 0 && m.provider.Exist(sid) { if len(sid) > 0 && m.provider.Exist(sid) {
return m.provider.Read(sid) return m.Read(sid)
} }
sid = m.sessionId() sid = m.sessionID()
sess, err := m.provider.Read(sid) sess, err := m.provider.Read(sid)
if err != nil { if err != nil {
return nil, err return nil, err
@ -282,6 +284,12 @@ func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) {
// Read returns raw session store by session ID. // Read returns raw session store by session ID.
func (m *Manager) Read(sid string) (RawStore, error) { func (m *Manager) Read(sid string) (RawStore, error) {
// No slashes or dots "./" should ever occur in the sid and to prevent session file forgery bug.
// See https://github.com/gogs/gogs/issues/5469
if strings.ContainsAny(sid, "./") {
return nil, errors.New("invalid 'sid': " + sid)
}
return m.provider.Read(sid) return m.provider.Read(sid)
} }
@ -308,7 +316,7 @@ func (m *Manager) Destory(ctx *macaron.Context) error {
// RegenerateId regenerates a session store from old session ID to new one. // RegenerateId regenerates a session store from old session ID to new one.
func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) { func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) {
sid := m.sessionId() sid := m.sessionID()
oldsid := ctx.GetCookie(m.opt.CookieName) oldsid := ctx.GetCookie(m.opt.CookieName)
sess, err = m.provider.Regenerate(oldsid, sid) sess, err = m.provider.Regenerate(oldsid, sid)
if err != nil { if err != nil {