Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Cai <jcai@gitlab.com>2020-05-10 06:44:47 +0300
committerJohn Cai <jcai@gitlab.com>2020-05-15 18:49:59 +0300
commit9738045a3d0cc8f453fb68716602c01dbb2c41be (patch)
tree23b221b2ab3ddc089018c3e16eade9f6908efefe
parent99aafef6e94851fe6b18e44ebbfd642106d0d09d (diff)
Add gitlab api access
-rw-r--r--NOTICE28
-rw-r--r--go.mod3
-rw-r--r--go.sum41
-rw-r--r--internal/service/hooks/api/access.go208
-rw-r--r--internal/service/hooks/api/access_test.go288
-rw-r--r--internal/testhelper/testserver.go115
6 files changed, 652 insertions, 31 deletions
diff --git a/NOTICE b/NOTICE
index 70a54ae4f..ae9b55a9e 100644
--- a/NOTICE
+++ b/NOTICE
@@ -2264,6 +2264,34 @@ LICENSE.txt - gitlab.com/gitlab-org/gitaly/internal/praefect/grpc-proxy/proxy
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LICENSE - gitlab.com/gitlab-org/gitlab-shell/client
+Copyright (c) 2011-2018 GitLab B.V.
+
+With regard to the GitLab Software:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &#34;Software&#34;), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED &#34;AS IS&#34;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+For all third party components incorporated into the GitLab Software, those
+components are licensed under the original license provided by the owner of the
+applicable component.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LICENSE - gitlab.com/gitlab-org/labkit
The MIT License (MIT)
diff --git a/go.mod b/go.mod
index a9d8e7317..733f30e9c 100644
--- a/go.mod
+++ b/go.mod
@@ -12,12 +12,13 @@ require (
github.com/kelseyhightower/envconfig v1.3.0
github.com/lib/pq v1.2.0
github.com/olekukonko/tablewriter v0.0.2
+ github.com/otiai10/curr v1.0.0 // indirect
github.com/prometheus/client_golang v1.0.0
github.com/prometheus/procfs v0.0.3 // indirect
github.com/rubenv/sql-migrate v0.0.0-20191213152630-06338513c237
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.4.0
- github.com/tinylib/msgp v1.1.0 // indirect
+ gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20200506213341-716e30c55e89
gitlab.com/gitlab-org/labkit v0.0.0-20200507062444-0149780c759d
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1
diff --git a/go.sum b/go.sum
index ced17a5b1..cc2e6d09b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+bou.ke/monkey v1.0.1 h1:zEMLInw9xvNakzUUPjfS4Ds6jYPqCFx3m7bRmG5NH2U=
+bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@@ -74,6 +76,7 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/getsentry/raven-go v0.1.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
+github.com/getsentry/raven-go v0.1.2/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/getsentry/sentry-go v0.5.1 h1:MIPe7ScHADsrK2vznqmhksIUFxq7m0JfTh+ZIMkI+VQ=
github.com/getsentry/sentry-go v0.5.1/go.mod h1:B8H7x8TYDPkeWPRzGpIiFO97LZP6rL8A3hEt8lUItMw=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
@@ -108,6 +111,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
@@ -194,6 +198,7 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/libgit2/git2go v0.0.0-20190104134018-ecaeb7a21d47/go.mod h1:4bKN42efkbNYMZlvDfxGDxzl066GhpvIircZDsm8Y+Y=
github.com/lightstep/lightstep-tracer-go v0.15.6 h1:D0GGa7afJ7GcQvu5as6ssLEEKYXvRgKI5d5cevtz8r4=
github.com/lightstep/lightstep-tracer-go v0.15.6/go.mod h1:6AMpwZpsyCFwSovxzM78e+AsYxE8sGwiM6C3TytaWeI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -205,6 +210,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v0.0.0-20190425161501-2444a32a19f4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do=
github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
@@ -227,12 +233,23 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW
github.com/olekukonko/tablewriter v0.0.2 h1:sq53g+DWf0J6/ceFUHpQ0nAEb6WgM++fq16MZ91cS6o=
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/otiai10/copy v1.0.1 h1:gtBjD8aq4nychvRZ2CyJvFWAw0aja+VHazDdruZKGZA=
+github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
+github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
+github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
+github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
+github.com/otiai10/mint v1.2.3 h1:PsrRBmrxR68kyNu6YlqYHbNlItc5vOkuS6LBEsNttVA=
+github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
+github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc=
+github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
@@ -327,6 +344,10 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg=
+gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20200506213341-716e30c55e89 h1:gZwXV5WLPmJ3NDmH+zAZkgWLVhgKvamGAjLXU5W0GiU=
+gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20200506213341-716e30c55e89/go.mod h1:oUGdKtJHWQP4/VQ/NONJZy/8X2E5ow2eGTUDwdzvGsc=
+gitlab.com/gitlab-org/labkit v0.0.0-20190221122536-0c3fc7cdd57c/go.mod h1:rYhLgfrbEcyfinG+R3EvKu6bZSsmwQqcXzLfHWSfUKM=
gitlab.com/gitlab-org/labkit v0.0.0-20200507062444-0149780c759d h1:Q5yZi+AelheHuvq/OK6DiaBzLU1AHrm7eWh88uE8Tsk=
gitlab.com/gitlab-org/labkit v0.0.0-20200507062444-0149780c759d/go.mod h1:SNfxkfUwVNECgtmluVayv0GWFgEjjBs5AzgsowPQuo0=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -341,6 +362,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90Pveol
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
@@ -356,6 +378,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1Zcpyg
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -372,6 +395,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -382,25 +406,21 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -410,7 +430,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -423,18 +442,17 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -469,7 +487,6 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -477,6 +494,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -487,6 +505,7 @@ google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBr
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba h1:pRj9OXZbwNtbtZtOB4dLwfK4u+EVRMvP+e9zKkg2grM=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -495,7 +514,6 @@ google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRn
gopkg.in/DataDog/dd-trace-go.v1 v1.7.0 h1:7wbMayb6JXcbAS95RN7MI42W3o1BCxCcdIzZfVWBAiE=
gopkg.in/DataDog/dd-trace-go.v1 v1.7.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -510,13 +528,12 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/internal/service/hooks/api/access.go b/internal/service/hooks/api/access.go
new file mode 100644
index 000000000..dbf2b7c58
--- /dev/null
+++ b/internal/service/hooks/api/access.go
@@ -0,0 +1,208 @@
+package api
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime"
+ "net/http"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "gitlab.com/gitlab-org/gitaly/internal/helper"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitlab-shell/client"
+)
+
+// AllowedResponse is a response for the internal gitlab api's /allowed endpoint with a subset
+// of fields
+type AllowedResponse struct {
+ Status bool `json:"status"`
+ Message string `json:"message"`
+}
+
+// AllowedRequest is a request for the internal gitlab api /allowed endpoint
+type AllowedRequest struct {
+ Action string `json:"action,omitempty"`
+ GLRepository string `json:"gl_repository,omitempty"`
+ Project string `json:"project,omitempty"`
+ Changes string `json:"changes,omitempty"`
+ Protocol string `json:"protocol,omitempty"`
+ Env string `json:"env,omitempty"`
+ Username string `json:"username,omitempty"`
+ KeyID string `json:"key_id,omitempty"`
+ UserID string `json:"user_id,omitempty"`
+}
+
+// gitObjectDirs generates a json encoded string containing GIT_OBJECT_DIRECTORY_RELATIVE, and GIT_ALTERNATE_OBJECT_DIRECTORIES
+func gitObjectDirs(repoPath, gitObjectDir string, gitAltObjDirs []string) (string, error) {
+ gitObjDirRel, err := filepath.Rel(repoPath, gitObjectDir)
+ if err != nil {
+ return "", err
+ }
+
+ gitAltObjDirsRel, err := relativeAlternativeObjectPaths(repoPath, gitAltObjDirs)
+ if err != nil {
+ return "", err
+ }
+
+ envString, err := json.Marshal(map[string]interface{}{
+ "GIT_OBJECT_DIRECTORY_RELATIVE": gitObjDirRel,
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE": gitAltObjDirsRel,
+ })
+
+ if err != nil {
+ return "", err
+ }
+
+ return string(envString), nil
+}
+
+func relativeAlternativeObjectPaths(repoPath string, gitAltObjDirs []string) ([]string, error) {
+ relPaths := make([]string, 0, len(gitAltObjDirs))
+ for _, objPath := range gitAltObjDirs {
+ relPath, err := filepath.Rel(repoPath, objPath)
+ if err != nil {
+ return relPaths, err
+ }
+ relPaths = append(relPaths, relPath)
+ }
+
+ return relPaths, nil
+}
+
+// API is a wrapper around client.GitlabNetClient with api methods for gitlab git receive hooks
+type API struct {
+ client *client.GitlabNetClient
+}
+
+// New creates a new API
+func New(c *client.GitlabNetClient) *API {
+ return &API{
+ client: c,
+ }
+}
+
+// Allowed checks if a ref change for a given repository is allowed through the gitlab internal api /allowed endpoint
+func (a *API) Allowed(repo *gitalypb.Repository, glRepository, glID, glProtocol, changes string) (bool, error) {
+ repoPath, err := helper.GetRepoPath(repo)
+ if err != nil {
+ return false, fmt.Errorf("getting the repository path: %w", err)
+ }
+
+ gitObjDirVars, err := gitObjectDirs(repoPath, repo.GetGitObjectDirectory(), repo.GetGitAlternateObjectDirectories())
+ if err != nil {
+ return false, fmt.Errorf("when getting git object directories json encoded string: %w", err)
+ }
+
+ req := AllowedRequest{
+ Action: "git-receive-pack",
+ GLRepository: glRepository,
+ Changes: changes,
+ Protocol: glProtocol,
+ Project: strings.Replace(repoPath, "'", "", -1),
+ Env: gitObjDirVars,
+ }
+
+ if err := req.parseAndSetGLID(glID); err != nil {
+ return false, fmt.Errorf("setting gl_id: %w", err)
+ }
+
+ resp, err := a.client.Post("/allowed", &req)
+ if err != nil {
+ return false, fmt.Errorf("http post to gitlab api /allowed endpoint: %w", err)
+ }
+
+ defer func() {
+ io.Copy(ioutil.Discard, resp.Body)
+ resp.Body.Close()
+ }()
+
+ var response AllowedResponse
+
+ switch resp.StatusCode {
+ case http.StatusOK,
+ http.StatusMultipleChoices:
+
+ mtype, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+ if err != nil {
+ return false, fmt.Errorf("/allowed endpoint respond with unsupported content type: %w", err)
+ }
+
+ if mtype != "application/json" {
+ return false, fmt.Errorf("/allowed endpoint respond with unsupported content type: %s", mtype)
+ }
+
+ if err = json.NewDecoder(resp.Body).Decode(&response); err != nil {
+ return false, fmt.Errorf("decoding response from /allowed endpoint: %w", err)
+ }
+ default:
+ return false, fmt.Errorf("API is not accessible: %d", resp.StatusCode)
+ }
+
+ return response.Status, nil
+}
+
+type preReceiveResponse struct {
+ ReferenceCounterIncreased bool `json:"reference_counter_increased"`
+}
+
+// PreReceive increases the reference counter for a push for a given gl_repository through the gitlab internal api /pre_receive endpoint
+func (a *API) PreReceive(glRepository string) (bool, error) {
+ resp, err := a.client.Post("/pre_receive", map[string]string{"gl_repository": glRepository})
+ if err != nil {
+ return false, fmt.Errorf("http post to gitlab api /pre_receive endpoint: %w", err)
+ }
+
+ defer func() {
+ io.Copy(ioutil.Discard, resp.Body)
+ resp.Body.Close()
+ }()
+
+ if resp.StatusCode != http.StatusOK {
+ return false, fmt.Errorf("pre-receive call failed with status: %d", resp.StatusCode)
+ }
+
+ mtype, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+ if err != nil {
+ return false, fmt.Errorf("/pre_receive endpoint respond with unsupported content type: %w", err)
+ }
+
+ if mtype != "application/json" {
+ return false, fmt.Errorf("/pre_receive endpoint respond with unsupported content type: %s", mtype)
+ }
+
+ var result preReceiveResponse
+
+ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
+ return false, fmt.Errorf("decoding response from /pre_receive endpoint: %w", err)
+ }
+
+ return result.ReferenceCounterIncreased, nil
+}
+
+var glIDRegex = regexp.MustCompile(`\A[0-9]+\z`)
+
+func (a *AllowedRequest) parseAndSetGLID(glID string) error {
+ var value string
+
+ switch {
+ case strings.HasPrefix(glID, "username-"):
+ a.Username = strings.TrimPrefix(glID, "username-")
+ return nil
+ case strings.HasPrefix(glID, "key-"):
+ a.KeyID = strings.TrimPrefix(glID, "key-")
+ value = a.KeyID
+ case strings.HasPrefix(glID, "user-"):
+ a.UserID = strings.TrimPrefix(glID, "user-")
+ value = a.UserID
+ }
+
+ if !glIDRegex.MatchString(value) {
+ return fmt.Errorf("gl_id='%s' is invalid!", glID)
+ }
+
+ return nil
+}
diff --git a/internal/service/hooks/api/access_test.go b/internal/service/hooks/api/access_test.go
new file mode 100644
index 000000000..948ccfb90
--- /dev/null
+++ b/internal/service/hooks/api/access_test.go
@@ -0,0 +1,288 @@
+package api_test
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/internal/service/hooks/api"
+ "gitlab.com/gitlab-org/gitaly/internal/testhelper"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitlab-shell/client"
+)
+
+func TestMain(m *testing.M) {
+ testhelper.Configure()
+ os.Exit(m.Run())
+}
+
+func TestAllowedVerifyParams(t *testing.T) {
+ user, password := "user", "password"
+ secretToken := "topsecret"
+ glID, glRepository := "key-123", "repo-1"
+
+ testRepo, testRepoPath, cleanup := testhelper.NewTestRepo(t)
+ defer cleanup()
+ changes := "changes1\nchanges2\nchanges3"
+ protocol := "protocol"
+ gitObjectDir := filepath.Join(testRepoPath, "object/dir")
+ gitAlternateObjectDirs := []string{filepath.Join(testRepoPath, "alt/object/dir1"), filepath.Join(testRepoPath, "alt/object/dir1")}
+
+ testRepo.GitObjectDirectory = gitObjectDir
+ testRepo.GitAlternateObjectDirectories = gitAlternateObjectDirs
+
+ server := testhelper.NewGitlabTestServer(t, testhelper.GitlabTestServerOptions{
+ User: user,
+ Password: password,
+ SecretToken: secretToken,
+ GLID: glID,
+ GLRepository: glRepository,
+ Changes: changes,
+ PostReceiveCounterDecreased: true,
+ Protocol: protocol,
+ GitPushOptions: nil,
+ GitObjectDir: gitObjectDir,
+ GitAlternateObjectDirs: gitAlternateObjectDirs,
+ RepoPath: testRepoPath,
+ })
+
+ defer server.Close()
+
+ httpClient := client.NewHTTPClient(server.URL, "", "", false, 100)
+ gitlabnetClient, err := client.NewGitlabNetClient(user, password, secretToken, httpClient)
+ require.NoError(t, err)
+
+ c := api.New(gitlabnetClient)
+
+ badRepo := *testRepo
+ badRepo.GitObjectDirectory = filepath.Join(testRepoPath, "bad/object/directory")
+
+ testCases := []struct {
+ desc string
+ repo *gitalypb.Repository
+ glRepository, glID, protocol, changes string
+ allowed bool
+ }{
+ {
+ desc: "success",
+ repo: testRepo,
+ glRepository: glRepository,
+ glID: glID,
+ protocol: protocol,
+ changes: changes,
+ allowed: true,
+ },
+ {
+ desc: "repo with bad quarantine directories",
+ repo: &badRepo,
+ glRepository: glRepository,
+ glID: glID,
+ protocol: protocol,
+ changes: changes,
+ allowed: false,
+ },
+ }
+
+ for _, tc := range testCases {
+ allowed, err := c.Allowed(tc.repo, tc.glRepository, tc.glID, tc.protocol, tc.changes)
+ require.NoError(t, err)
+ require.Equal(t, tc.allowed, allowed)
+ }
+}
+
+func TestAllowedResponseHandling(t *testing.T) {
+ testRepo, testRepoPath, cleanup := testhelper.NewTestRepo(t)
+
+ // set git quarantine directories
+ gitObjectDir := filepath.Join(testRepoPath, "quarantine", "object", "dir")
+ testRepo.GitObjectDirectory = gitObjectDir
+ gitAltObjectDir := filepath.Join(testRepoPath, "objects")
+ testRepo.GitAlternateObjectDirectories = []string{gitAltObjectDir}
+
+ defer cleanup()
+
+ testCases := []struct {
+ desc string
+ allowedHandler func(w http.ResponseWriter, r *http.Request)
+ allowed bool
+ errMsg string
+ }{
+ {
+ desc: "allowed",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`{"status": true}`))
+ },
+ allowed: true,
+ errMsg: "",
+ },
+ {
+ desc: "bad content type in response",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "bad mime type")
+ w.WriteHeader(http.StatusOK)
+ },
+ allowed: false,
+ errMsg: "unsupported content type",
+ },
+ {
+ desc: "internal server error",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte(`{"status": true}`))
+ },
+ allowed: false,
+ errMsg: "",
+ },
+ {
+ desc: "bad response",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`this is not json`))
+ },
+ allowed: false,
+ errMsg: "decoding response from /allowed endpoint",
+ },
+ {
+ desc: "status multiple choice",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusMultipleChoices)
+ w.Write([]byte(`{"status": true}`))
+ },
+ allowed: true,
+ errMsg: "",
+ },
+ {
+ desc: "status unauthorized with message",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusUnauthorized)
+ w.Write([]byte(`{"message": "you're not allowed here'"}`))
+ },
+ allowed: false,
+ errMsg: "you're not allowed here",
+ },
+ {
+ desc: "status unauthorized",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusUnauthorized)
+ },
+ allowed: false,
+ errMsg: "Internal API error",
+ },
+ {
+ desc: "status not found",
+ allowedHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusNotFound)
+ w.Write([]byte(`{"message": "not found"}`))
+ },
+ allowed: false,
+ errMsg: "not found",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(tc.allowedHandler))
+ defer server.Close()
+
+ httpClient := client.NewHTTPClient(server.URL, "", "", false, 100)
+ gitlabnetClient, err := client.NewGitlabNetClient("", "", "", httpClient)
+ require.NoError(t, err)
+
+ c := api.New(gitlabnetClient)
+ allowed, err := c.Allowed(testRepo, "repo-1", "key-123", "http", "a\nb\nc\nd")
+ require.Equal(t, tc.allowed, allowed)
+ if err != nil {
+ require.Contains(t, err.Error(), tc.errMsg)
+ }
+ })
+ }
+}
+
+func TestPrereceive(t *testing.T) {
+ testCases := []struct {
+ desc string
+ prereceiveHandler func(w http.ResponseWriter, r *http.Request)
+ success bool
+ errMsg string
+ }{
+ {
+ desc: "everything ok",
+ prereceiveHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`{"reference_counter_increased": true}`))
+ },
+ success: true,
+ errMsg: "",
+ },
+ {
+ desc: "reference counter not increased",
+ prereceiveHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`{"reference_counter_increased": false}`))
+ },
+ success: false,
+ errMsg: "",
+ },
+ {
+ desc: "server unavailable",
+ prereceiveHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusServiceUnavailable)
+ w.Write([]byte(`{"message": "server is down!"}`))
+ },
+ success: false,
+ errMsg: "server is down!",
+ },
+ {
+ desc: "non json content type",
+ prereceiveHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`{"reference_counter_increased": true}`))
+ },
+ success: false,
+ errMsg: "unsupported content type",
+ },
+ {
+ desc: "bad data",
+ prereceiveHandler: func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`not json`))
+ },
+ success: false,
+ errMsg: "decoding response from /pre_receive endpoint",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(tc.prereceiveHandler))
+ defer server.Close()
+
+ httpClient := client.NewHTTPClient(server.URL, "", "", false, 100)
+ gitlabnetClient, err := client.NewGitlabNetClient("", "", "", httpClient)
+ require.NoError(t, err)
+
+ c := api.New(gitlabnetClient)
+ success, err := c.PreReceive("key-123")
+ require.Equal(t, tc.success, success)
+ if err != nil {
+ require.Contains(t, err.Error(), tc.errMsg)
+ }
+ })
+ }
+}
diff --git a/internal/testhelper/testserver.go b/internal/testhelper/testserver.go
index 27c160ca6..8d259c995 100644
--- a/internal/testhelper/testserver.go
+++ b/internal/testhelper/testserver.go
@@ -2,6 +2,7 @@ package testhelper
import (
"context"
+ "encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -9,6 +10,7 @@ import (
"net"
"net/http"
"net/http/httptest"
+ "net/url"
"os"
"os/exec"
"path/filepath"
@@ -277,11 +279,35 @@ func NewServer(tb testing.TB, streamInterceptors []grpc.StreamServerInterceptor,
var changeLineRegex = regexp.MustCompile("^[a-f0-9]{40} [a-f0-9]{40} refs/[^ ]+$")
+const secretHeaderName = "Gitlab-Shared-Secret"
+
+func formToMap(u url.Values) map[string]string {
+ return map[string]string{
+ "action": u.Get("action"),
+ "gl_repository": u.Get("gl_repository"),
+ "project": u.Get("project"),
+ "changes": u.Get("changes"),
+ "protocol": u.Get("protocol"),
+ "env": u.Get("env"),
+ "username": u.Get("username"),
+ "key_id": u.Get("key_id"),
+ "user_id": u.Get("user_id"),
+ }
+}
+
func handleAllowed(t testing.TB, options GitlabTestServerOptions) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
require.NoError(t, r.ParseForm())
require.Equal(t, http.MethodPost, r.Method, "expected http post")
- require.Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type"))
+
+ params := make(map[string]string)
+
+ switch r.Header.Get("Content-Type") {
+ case "application/x-www-form-urlencoded":
+ params = formToMap(r.Form)
+ case "application/json":
+ require.NoError(t, json.NewDecoder(r.Body).Decode(&params))
+ }
user, password, _ := r.BasicAuth()
require.Equal(t, options.User, user)
@@ -293,58 +319,79 @@ func handleAllowed(t testing.TB, options GitlabTestServerOptions) func(w http.Re
switch glidSplit[0] {
case "user":
- require.Equal(t, glidSplit[1], r.Form.Get("user_id"))
+ require.Equal(t, glidSplit[1], params["user_id"])
case "key":
- require.Equal(t, glidSplit[1], r.Form.Get("key_id"))
+ require.Equal(t, glidSplit[1], params["key_id"])
case "username":
- require.Equal(t, glidSplit[1], r.Form.Get("username"))
+ require.Equal(t, glidSplit[1], params["username"])
default:
t.Fatalf("invalid GLID: %q", options.GLID)
}
}
- require.NotEmpty(t, r.Form.Get("gl_repository"), "gl_repository should not be empty")
+ require.NotEmpty(t, params["gl_repository"], "gl_repository should not be empty")
if options.GLRepository != "" {
- require.Equal(t, options.GLRepository, r.Form.Get("gl_repository"), "expected value of gl_repository should match form")
+ require.Equal(t, options.GLRepository, params["gl_repository"], "expected value of gl_repository should match form")
}
- require.NotEmpty(t, r.Form.Get("protocol"), "protocol should not be empty")
+ require.NotEmpty(t, params["protocol"], "protocol should not be empty")
if options.Protocol != "" {
- require.Equal(t, options.Protocol, r.Form.Get("protocol"), "expected value of options.Protocol should match form")
+ require.Equal(t, options.Protocol, params["protocol"], "expected value of options.Protocol should match form")
}
if options.Changes != "" {
- require.Equal(t, options.Changes, r.Form.Get("changes"), "expected value of options.Changes should match form")
+ require.Equal(t, options.Changes, params["changes"], "expected value of options.Changes should match form")
} else {
- changeLines := strings.Split(strings.TrimSuffix(r.Form.Get("changes"), "\n"), "\n")
+ changeLines := strings.Split(strings.TrimSuffix(params["changes"], "\n"), "\n")
for _, line := range changeLines {
require.Regexp(t, changeLineRegex, line)
}
}
- env := r.Form.Get("env")
+ env := params["env"]
require.NotEmpty(t, env)
var gitVars struct {
GitAlternateObjectDirsRel []string `json:"GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE"`
GitObjectDirRel string `json:"GIT_OBJECT_DIRECTORY_RELATIVE"`
}
+
+ w.Header().Set("Content-Type", "application/json")
+
require.NoError(t, json.Unmarshal([]byte(env), &gitVars))
if options.GitObjectDir != "" {
relObjectDir, err := filepath.Rel(options.RepoPath, options.GitObjectDir)
require.NoError(t, err)
- require.Equal(t, relObjectDir, gitVars.GitObjectDirRel)
+ if relObjectDir != gitVars.GitObjectDirRel {
+ w.Write([]byte(`{"status":false}`))
+ return
+ }
}
+
if len(options.GitAlternateObjectDirs) > 0 {
require.Len(t, gitVars.GitAlternateObjectDirsRel, len(options.GitAlternateObjectDirs))
for i, gitAlterateObjectDir := range options.GitAlternateObjectDirs {
relAltObjectDir, err := filepath.Rel(options.RepoPath, gitAlterateObjectDir)
require.NoError(t, err)
- require.Equal(t, relAltObjectDir, gitVars.GitAlternateObjectDirsRel[i])
+ if relAltObjectDir != gitVars.GitAlternateObjectDirsRel[i] {
+ w.Write([]byte(`{"status":false}`))
+ return
+ }
}
}
- w.Header().Set("Content-Type", "application/json")
+ var authenticated bool
if r.Form.Get("secret_token") == options.SecretToken {
+ authenticated = true
+ }
+
+ secretHeader, err := base64.StdEncoding.DecodeString(r.Header.Get(secretHeaderName))
+ if err == nil {
+ if string(secretHeader) == options.SecretToken {
+ authenticated = true
+ }
+ }
+
+ if authenticated {
w.Write([]byte(`{"status":true}`))
return
}
@@ -356,13 +403,44 @@ func handleAllowed(t testing.TB, options GitlabTestServerOptions) func(w http.Re
func handlePreReceive(t testing.TB, options GitlabTestServerOptions) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
require.NoError(t, r.ParseForm())
+
+ params := make(map[string]string)
+
+ switch r.Header.Get("Content-Type") {
+ case "application/x-www-form-urlencoded":
+ b, err := json.Marshal(r.Form)
+ require.NoError(t, err)
+
+ var reqForm struct {
+ GLRepository []string `json:"gl_repository"`
+ }
+
+ require.NoError(t, json.Unmarshal(b, &reqForm))
+ require.Greater(t, len(reqForm.GLRepository), 0)
+ params["gl_repository"] = reqForm.GLRepository[0]
+ case "application/json":
+ require.NoError(t, json.NewDecoder(r.Body).Decode(&params))
+ }
+
require.Equal(t, http.MethodPost, r.Method)
- require.Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type"))
- require.NotEmpty(t, r.Form.Get("gl_repository"), "gl_repository should not be empty")
+ require.NotEmpty(t, params["gl_repository"], "gl_repository should not be empty")
if options.GLRepository != "" {
- require.Equal(t, options.GLRepository, r.Form.Get("gl_repository"), "expected value of gl_repository should match form")
+ require.Equal(t, options.GLRepository, params["gl_repository"], "expected value of gl_repository should match form")
+ }
+
+ var authenticated bool
+ if r.Form.Get("secret_token") == options.SecretToken {
+ authenticated = true
}
- require.Equal(t, options.SecretToken, r.Form.Get("secret_token"), "expected value of secret_token should match form")
+
+ secretHeader, err := base64.StdEncoding.DecodeString(r.Header.Get(secretHeaderName))
+ if err == nil {
+ if string(secretHeader) == options.SecretToken {
+ authenticated = true
+ }
+ }
+
+ require.True(t, authenticated, "expected value of secret_token should request")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
@@ -372,6 +450,7 @@ func handlePreReceive(t testing.TB, options GitlabTestServerOptions) func(w http
func handlePostReceive(t testing.TB, options GitlabTestServerOptions) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
+ defer r.Body.Close()
require.NoError(t, r.ParseForm())
require.Equal(t, http.MethodPost, r.Method)
require.Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type"))