Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gitlab-org/gitlab-pages
  • rfwatson/gitlab-pages
  • masakura/gitlab-pages
  • gordio/gitlab-pages
  • zloster/gitlab-pages
  • jonnymbgood/gitlab-pages
  • shinya.maeda/gitlab-pages
  • xer0x/gitlab-pages
  • esabelhaus/gitlab-pages
  • frolvlad/gitlab-pages
  • mgresko/gitlab-pages
  • fzipi/gitlab-pages
  • naysayer1/gitlab-pages
  • yaowenli/gitlab-pages
  • tuomoa/gitlab-pages
  • ahodgen/gitlab-pages
  • techonomics_/gitlab-pages
  • icode1/gitlab-pages
  • Maritonette/gitlab-pages
  • uros.simovic/gitlab-pages
  • randallscott99/gitlab-pages
  • nolith/gitlab-pages
  • Ww3.Google.com/gitlab-pages
  • Ambyjkl/gitlab-pages
  • pknw1/gitlab-pages
  • macstacks/gitlab-pages
  • lpasselin/gitlab-pages
  • itoijala/gitlab-pages
  • JonathonReinhart/gitlab-pages
  • Azaradel/gitlab-pages
  • fundasecgin32/gitlab-pages
  • BageDevimo/gitlab-pages
  • jmkim/gitlab-pages
  • siemens/gitlab-pages
  • tardyp/gitlab-pages
  • michaels3d/gitlab-pages
  • tuxillo/gitlab-pages
  • karrick/gitlab-pages
  • sca_gitlab/gitlab-pages
  • alerque/gitlab-pages
  • unaheidi/gitlab-pages
  • maryomotayo2/gitlab-pages
  • liuzyhn/gitlab-pages
  • xingdi25/gitlab-pages
  • chefashiful.islam/gitlab-pages
  • johan.ramirez/gitlab-pages
  • Laurenslill/gitlab-pages
  • yigithankardas/gitlab-pages
  • leolara/gitlab-pages
  • nuwe1/gitlab-pages
  • GoroD/gitlab-pages
  • PopeDrFreud/gitlab-pages
  • hemanthdev/gitlab-pages
  • ba2014sheer/gitlab-pages
  • NicoleS021/gitlab-pages
  • boboso1971/gitlab-pages
  • mark2472-repos/gitlab-pages
  • derekmiller111978.dm/gitlab-pages
  • 229083520/gitlab-pages
  • armbiant/gitlab-pages
  • 358253885/gitlab-pages
  • seavhong0001/gitlab-pages
  • ashimtriv/gitlab-pages
  • ayushontop/gitlab-pages
  • jaime/gitlab-pages
  • sdwolfz/gitlab-pages
  • mipmip/gitlab-pages
  • ksashikumar/gitlab-pages
  • poppygo/gitlab-pages
  • xmarlem/gitlab-pages
  • realFlowControl/gitlab-pages
  • feistel/gitlab-pages
  • Shefali321/gitlab-pages
  • we88c0de/gitlab-pages
  • sathieu/gitlab-pages
  • bhuvantmey/gitlab-pages
  • kevin.kengne1/gitlab-pages
  • Dishon/gitlab-pages
  • ljdo.lj/gitlab-pages
  • ercan.ucan/gitlab-pages
  • davidleger95/gitlab-pages
  • jenslauterbach/gitlab-pages
  • kinohub1/gitlab-pages
  • psykomal/gitlab-pages
  • d.esterman/gitlab-pages
  • nfriend/gitlab-pages
  • ax10336/gitlab-pages
  • bhiseviraj/gitlab-pages
  • Virajbhise/gitlab-pages
  • nikovega21/gitlab-pages
  • HaroldKnowlden/gitlab-pages
  • mlegner/gitlab-pages
  • lenharo/gitlab-pages
  • danscott/gitlab-pages
  • Kolan92/gitlab-pages
  • HuseyinEmreAksoy/gitlab-pages
  • test_jaime/gitlab-pages
  • Osmanilge/gitlab-pages
  • cs-ic/gitlab-pages
  • TamerlanG1/gitlab-pages
  • aravind.mylsamy/gitlab-pages
  • quiqr/gitlab-pages
  • andrew.c3/gitlab-pages
  • altiayirem/gitlab-pages
  • sayeedahmad/gitlab-pages
  • chriscrabtree2021/gitlab-pages
  • leshkatruhachev/gitlab-pages
  • l.dijkman/gitlab-pages
  • casfahrenfort/gitlab-pages
  • dtjdtrj/gitlab-pages
  • kevin.rojas/wr-gitlab-pages
  • gitlab-renovate-forks/gitlab-pages
  • slumericanbds1/gitlab-pages
  • ayuryshev/gitlab-pages
  • abitrolly/gitlab-pages
  • sitedata/gitlab-pages
  • tidys/gitlab-pages
  • Mohamad.Elsuity/gitlab-pages
  • principallksk/gitlab-pages
  • gitlab-community/gitlab-pages
  • rafaelgamboatumtum/gitlab-pages
  • armbiant/gnome-gitlab-pages
  • ollevche/gitlab-pages
  • xMoelletschi/gitlab-pages
124 results
Show changes
Commits on Source (7)
......@@ -43,10 +43,10 @@ cover:
code_quality:
extends: .tests-common
image: golangci/golangci-lint:v1.52.2
image: golangci/golangci-lint:v1.64.6
variables:
REPORT_FILE: gl-code-quality-report.json
LINT_FLAGS: "--color never --deadline 15m"
LINT_FLAGS: "--color never --timeout=15m"
OUT_FORMAT: code-climate
script:
- golangci-lint run ./... --out-format ${OUT_FORMAT} ${LINT_FLAGS} | tee ${REPORT_FILE}
......
golang 1.23.4
golang 1.24.0
......@@ -3,7 +3,7 @@
OUT_FORMAT ?= colored-line-number
LINT_FLAGS ?= $(if $V,-v)
REPORT_FILE ?=
GOLANGCI_VERSION=v1.52.2
GOLANGCI_VERSION=v1.64.6
GOTESTSUM_VERSION=v1.10.0
COVERAGE_PACKAGES=$(shell (go list ./... | grep -v -e "test/acceptance" | tr "\n", "," | sed 's/\(.*\),/\1 /'))
......
......@@ -115,7 +115,9 @@ func (a *theApp) getTLSConfig() (*cryptotls.Config, error) {
func (a *theApp) serveFileOrNotFoundHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer metrics.ServingTime.Observe(time.Since(start).Seconds())
defer func() {
metrics.ServingTime.Observe(time.Since(start).Seconds())
}()
domain := domain.FromRequest(r)
fileServed := domain.ServeFileHTTP(w, r)
......
......@@ -5,7 +5,9 @@ module gitlab.com/gitlab-org/gitlab-pages
// - make sure the internal/vfs/serving package is synced with upstream
// - which can be done in the go repository with:
// git log --oneline -s -L:serveContent:src/net/http/fs.go --since="2021-02-16"
go 1.23
go 1.23.0
toolchain go1.23.6
require (
github.com/3th1nk/cidr v0.2.0
......@@ -30,7 +32,7 @@ require (
gitlab.com/gitlab-org/labkit v1.21.2
go.uber.org/automaxprocs v1.6.0
go.uber.org/goleak v1.3.0
golang.org/x/crypto v0.33.0
golang.org/x/crypto v0.35.0
golang.org/x/net v0.35.0
golang.org/x/sync v0.11.0
golang.org/x/time v0.9.0
......
......@@ -308,8 +308,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
......
......@@ -430,7 +430,7 @@ func (a *Auth) checkSessionIsValid(w http.ResponseWriter, r *http.Request, domai
}
// redirect to /auth?domain=%s&state=%s
if a.checkTokenExists(session, w, r, domain) {
if !isAuthInHeader(r) && a.checkTokenExists(session, w, r, domain) {
return nil
}
......@@ -511,44 +511,42 @@ func (a *Auth) IsAuthSupported() bool {
return a != nil
}
// checkAuthentication checks if user is authenticated and has access to the project
// will return contentServed = false when authFailed = true
func (a *Auth) checkAuthentication(w http.ResponseWriter, r *http.Request, domain internal.Domain) bool {
logRequest(r).Debug("Authenticate request")
if a == nil {
logRequest(r).Error(errAuthNotConfigured)
errortracking.CaptureErrWithReqAndStackTrace(errAuthNotConfigured, r)
httperrors.Serve500(w)
return true
}
session := a.checkSessionIsValid(w, r, domain)
if session == nil {
return true
}
projectID := domain.GetProjectID(r)
// Access token exists, authorize request
var url string
if projectID > 0 {
url = fmt.Sprintf(apiURLProjectTemplate, a.internalGitlabServer, projectID)
} else {
url = fmt.Sprintf(apiURLUserTemplate, a.internalGitlabServer)
}
req, err := http.NewRequestWithContext(r.Context(), "GET", url, nil)
req, err := a.buildAuthRequest(r, projectID, session)
if err != nil {
logRequest(r).WithError(err).Error(failAuthErrMsg)
errortracking.CaptureErrWithReqAndStackTrace(err, r)
handleAuthError(r, err, failAuthErrMsg)
httperrors.Serve500(w)
return true
}
req.Header.Add("Authorization", "Bearer "+session.Values["access_token"].(string))
resp, err := a.apiClient.Do(req)
if err != nil {
if errors.Is(err, context.Canceled) {
httperrors.Serve404(w)
return true
} else {
handleAuthError(r, err, "Failed to retrieve info with token")
domain.ServeNotFoundAuthFailed(w, r)
}
logRequest(r).WithError(err).Error("Failed to retrieve info with token")
errortracking.CaptureErrWithReqAndStackTrace(err, r)
// call serve404 handler when auth fails
domain.ServeNotFoundAuthFailed(w, r)
return true
}
defer resp.Body.Close()
if checkResponseForInvalidToken(resp, session, w, r) {
......@@ -570,6 +568,36 @@ func (a *Auth) checkAuthentication(w http.ResponseWriter, r *http.Request, domai
return false
}
// buildAuthURL constructs the API URL based on project ID.
func (a *Auth) buildAuthURL(projectID uint64) string {
if projectID > 0 {
return fmt.Sprintf(apiURLProjectTemplate, a.internalGitlabServer, projectID)
}
return fmt.Sprintf(apiURLUserTemplate, a.internalGitlabServer)
}
// buildAuthRequest creates an authenticated HTTP request.
func (a *Auth) buildAuthRequest(r *http.Request, projectID uint64, session *hostSession) (*http.Request, error) {
authURL := a.buildAuthURL(projectID)
req, err := http.NewRequestWithContext(r.Context(), "GET", authURL, nil)
if err != nil {
return nil, err
}
if isAuthInHeader(r) {
logRequest(r).Info("Private pages accessed using authorization header")
req.Header.Add("Authorization", r.Header.Get("Authorization"))
} else {
req.Header.Add("Authorization", "Bearer "+session.Values["access_token"].(string))
}
return req, nil
}
// handleAuthError logs the error and captures it in error tracking.
func handleAuthError(r *http.Request, err error, message string) {
logRequest(r).WithError(err).Error(message)
errortracking.CaptureErrWithReqAndStackTrace(err, r)
}
// CheckAuthenticationWithoutProject checks if user is authenticated and has a valid token
func (a *Auth) CheckAuthenticationWithoutProject(w http.ResponseWriter, r *http.Request, domain internal.Domain) bool {
if a == nil {
......@@ -603,22 +631,6 @@ func (a *Auth) RequireAuth(w http.ResponseWriter, r *http.Request, domain intern
return a.checkSessionIsValid(w, r, domain) == nil
}
// CheckAuthentication checks if user is authenticated and has access to the project
// will return contentServed = false when authFailed = true
func (a *Auth) CheckAuthentication(w http.ResponseWriter, r *http.Request, domain internal.Domain) bool {
logRequest(r).Debug("Authenticate request")
if a == nil {
logRequest(r).Error(errAuthNotConfigured)
errortracking.CaptureErrWithReqAndStackTrace(errAuthNotConfigured, r)
httperrors.Serve500(w)
return true
}
return a.checkAuthentication(w, r, domain)
}
// CheckResponseForInvalidToken checks response for invalid token and destroys session if it was invalid
func (a *Auth) CheckResponseForInvalidToken(w http.ResponseWriter, r *http.Request,
resp *http.Response,
......@@ -665,7 +677,11 @@ func checkResponseForInvalidToken(resp *http.Response, session *hostSession, w h
return false
}
if errResp.Error == "invalid_token" {
if isAuthInHeader(r) {
logRequest(r).Warn("Authorization token provided in request header was invalid")
httperrors.Serve404(w)
return true
} else if errResp.Error == "invalid_token" {
// Token is invalid
logRequest(r).Warn("Access token was invalid, destroying session")
......@@ -677,6 +693,14 @@ func checkResponseForInvalidToken(resp *http.Response, session *hostSession, w h
return false
}
func isAuthInHeader(r *http.Request) bool {
if !feature.AuthorizationHeader.Enabled() {
return false
}
token := strings.TrimSpace(r.Header.Get("Authorization"))
return token != ""
}
func logRequest(r *http.Request) *logrus.Entry {
return logging.LogRequest(r).WithField("state", r.URL.Query().Get("state"))
}
......
......@@ -202,7 +202,7 @@ func TestCheckAuthenticationWhenStateIsAlreadySet(t *testing.T) {
"state": "given_state",
})
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.True(t, contentServed)
// check if the state was re-used instead of re-created
......@@ -234,7 +234,7 @@ func TestProjectPrefixInSessionValues(t *testing.T) {
r, err := http.NewRequest("Get", "https://example.com/test", nil)
require.NoError(t, err)
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000, projectPrefix: "/test/"})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000, projectPrefix: "/test/"})
require.True(t, contentServed)
cookieString := result.Header().Get("Set-Cookie")
......@@ -357,10 +357,10 @@ func TestCheckAuthenticationWhenAccess(t *testing.T) {
require.NoError(t, err)
setSessionValues(t, r, auth, map[interface{}]interface{}{"access_token": "abc"})
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.False(t, contentServed)
// notFoundContent wasn't served so the default response from CheckAuthentication should be 200
// notFoundContent wasn't served so the default response from checkAuthentication should be 200
require.Equal(t, http.StatusOK, result.Code)
}
......@@ -379,7 +379,7 @@ func TestCheckAuthenticationWhenContextCanceled(t *testing.T) {
// cancel context explicitly and expect not found
cancel()
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.True(t, contentServed)
require.Equal(t, http.StatusNotFound, result.Code)
}
......@@ -411,7 +411,7 @@ func TestCheckAuthenticationWhenNoAccess(t *testing.T) {
"access_token": "abc",
})
contentServed := auth.CheckAuthentication(w, r, &domainMock{projectID: 1000, notFoundContent: "Generic 404"})
contentServed := auth.checkAuthentication(w, r, &domainMock{projectID: 1000, notFoundContent: "Generic 404"})
require.True(t, contentServed)
res := w.Result()
testhelpers.Close(t, res.Body)
......@@ -448,7 +448,7 @@ func TestCheckAuthenticationWithSessionFromDifferentHost(t *testing.T) {
r, err = http.NewRequest("Get", "https://example.com/", nil)
require.NoError(t, err)
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.True(t, contentServed)
// should redirect to auth
......@@ -488,11 +488,84 @@ func TestCheckAuthenticationWhenInvalidToken(t *testing.T) {
"access_token": "abc",
})
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.True(t, contentServed)
require.Equal(t, http.StatusFound, result.Code)
}
func testCheckAuthenticationWhenValidAndInvalidAuthorizationHeader(t *testing.T, tests []AuthHeader) {
apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v4/projects/1000/pages_access":
authHeader := r.Header.Get("Authorization")
if authHeader == "Bearer abc" {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusUnauthorized) // Handle invalid token case
}
default:
t.Logf("Unexpected r.URL.Path: %q", r.URL.Path)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
}
}))
t.Cleanup(apiServer.Close)
auth := createTestAuth(t, apiServer.URL, "")
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
r, err := http.NewRequest("GET", "https://example.com", nil)
require.NoError(t, err)
if tc.authHeader != "" {
r.Header.Set("Authorization", tc.authHeader)
}
result := httptest.NewRecorder()
// Call authentication check
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.Equal(t, tc.expectContent, contentServed)
require.Equal(t, tc.expectedCode, result.Code)
if tc.expectedCode == http.StatusFound {
redirectURL, err := url.Parse(result.Header().Get("Location"))
require.NoError(t, err)
require.Equal(t, "pages.gitlab-example.com", redirectURL.Host)
require.Equal(t, "/auth", redirectURL.Path)
require.Equal(t, "https://example.com", redirectURL.Query().Get("domain"))
}
})
}
}
type AuthHeader struct {
name string
authHeader string
expectedCode int
expectContent bool
}
func TestCheckAuthenticationWhenValidAndInvalidAuthorizationHeaderFFEnabled(t *testing.T) {
t.Setenv(feature.AuthorizationHeader.EnvVariable, "true")
tests := []AuthHeader{
{"Valid Token", "Bearer abc", http.StatusOK, false},
{"Invalid Token", "Bearer invalid", http.StatusNotFound, true},
{"Missing Token", "", http.StatusFound, true},
}
testCheckAuthenticationWhenValidAndInvalidAuthorizationHeader(t, tests)
}
func TestCheckAuthenticationWhenValidAndInvalidAuthorizationHeaderFFDisabled(t *testing.T) {
t.Setenv(feature.AuthorizationHeader.EnvVariable, "false")
tests := []AuthHeader{
{"Valid Token", "Bearer abc", http.StatusFound, true},
{"Invalid Token", "Bearer invalid", http.StatusFound, true},
{"Missing Token", "", http.StatusFound, true},
}
testCheckAuthenticationWhenValidAndInvalidAuthorizationHeader(t, tests)
}
func TestCheckAuthenticationWithoutProject(t *testing.T) {
apiServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
......@@ -700,7 +773,7 @@ func testNamespaceInPath(t *testing.T, allowNamespaceInPath bool, namespace stri
namespaceInPathKey: strings.TrimPrefix(expectedPath, "/"),
})
contentServed := auth.CheckAuthentication(result, r, &domainMock{projectID: 1000})
contentServed := auth.checkAuthentication(result, r, &domainMock{projectID: 1000})
require.True(t, contentServed)
// check if the state was re-used instead of re-created
......
......@@ -51,7 +51,7 @@ func (a *Auth) AuthorizationMiddleware(handler http.Handler) http.Handler {
// Only for projects that have access control enabled
if lp.HasAccessControl {
// accessControlMiddleware
if a.CheckAuthentication(w, r, domain) {
if a.checkAuthentication(w, r, domain) {
return
}
}
......
......@@ -37,3 +37,9 @@ var ProjectPrefixCookiePath = Feature{
EnvVariable: "FF_ENABLE_PROJECT_PREFIX_COOKIE_PATH",
defaultEnabled: true,
}
// AuthorizationHeader enables support for Authorization header to bypass login
var AuthorizationHeader = Feature{
EnvVariable: "FF_AUTHORIZATION_HEADER",
defaultEnabled: true,
}
......@@ -49,8 +49,8 @@ func New(op string, opts ...Option) *Cache {
configuration := ccache.Configure[any]()
configuration.MaxSize(c.maxSize)
configuration.ItemsToPrune(uint32(c.maxSize) / itemsToPruneDiv)
configuration.GetsPerPromote(getsPerPromote) // if item gets requested frequently promote it
configuration.ItemsToPrune(uint32(c.maxSize) / itemsToPruneDiv) // nolint:gosec
configuration.GetsPerPromote(getsPerPromote) // if item gets requested frequently promote it
configuration.OnDelete(func(*ccache.Item[any]) {
if c.metricCachedEntries != nil {
c.metricCachedEntries.WithLabelValues(op).Dec()
......
......@@ -139,9 +139,9 @@ func WithKeyFunc(f KeyFunc) Option {
}
// WithCloseConnection configures closeConnection bool whether to send connection close header or not
func WithCloseConnection(close bool) Option {
func WithCloseConnection(closeCon bool) Option {
return func(rl *RateLimiter) {
rl.closeConnection = close
rl.closeConnection = closeCon
}
}
......
......@@ -26,10 +26,10 @@ func fabricateLookupPath(size int, lookup api.LookupPath) *serving.LookupPath {
Path: lookup.Source.Path,
SHA256: lookup.Source.SHA256,
Prefix: lookup.Prefix,
IsNamespaceProject: (lookup.Prefix == "/" && size > 1),
IsNamespaceProject: lookup.Prefix == "/" && size > 1,
IsHTTPSOnly: lookup.HTTPSOnly,
HasAccessControl: lookup.AccessControl,
ProjectID: uint64(lookup.ProjectID),
ProjectID: uint64(lookup.ProjectID), //nolint:gosec
UniqueHost: lookup.UniqueHost,
RootDirectory: lookup.RootDirectory,
PrimaryDomain: lookup.PrimaryDomain,
......
......@@ -221,7 +221,7 @@ func (a *zipArchive) Open(ctx context.Context, name string) (vfs.File, error) {
}
// only read from dataOffset up to the size of the compressed file
reader := a.reader.SectionReader(ctx, dataOffset.(int64), int64(file.CompressedSize64))
reader := a.reader.SectionReader(ctx, dataOffset.(int64), int64(file.CompressedSize64)) //nolint:gosec
switch file.Method {
case zip.Deflate:
......
......@@ -2,9 +2,7 @@ package main
import (
"fmt"
"math/rand"
"os"
"time"
"github.com/sirupsen/logrus"
"gitlab.com/gitlab-org/labkit/errortracking"
......@@ -99,8 +97,6 @@ func printVersion(showVersion bool, version string) {
func main() {
logrus.SetOutput(os.Stderr)
rand.Seed(time.Now().UnixNano())
metrics.MustRegister()
if err := appMain(); err != nil {
......
......@@ -20,7 +20,7 @@ func TestWhenAuthIsDisabledPrivateIsNotAccessible(t *testing.T) {
rsp, err := GetPageFromListener(t, httpListener, "group.auth.gitlab-example.com", "private.project/")
require.NoError(t, err)
rsp.Body.Close()
require.NoError(t, rsp.Body.Close())
require.Equal(t, http.StatusInternalServerError, rsp.StatusCode)
}
......@@ -39,25 +39,60 @@ func TestWhenAuthIsEnabledPrivateWillRedirectToAuthorize(t *testing.T) {
require.Equal(t, http.StatusFound, rsp.StatusCode)
require.Len(t, rsp.Header["Location"], 1)
url, err := url.Parse(rsp.Header.Get("Location"))
locationURL, err := url.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
rsp, err = GetRedirectPage(t, httpsListener, url.Host, url.Path+"?"+url.RawQuery)
rsp, err = GetRedirectPage(t, httpsListener, locationURL.Host, locationURL.Path+"?"+locationURL.RawQuery)
require.NoError(t, err)
testhelpers.Close(t, rsp.Body)
require.Equal(t, http.StatusFound, rsp.StatusCode)
require.Len(t, rsp.Header["Location"], 1)
url, err = url.Parse(rsp.Header.Get("Location"))
locationURL, err = locationURL.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
require.Equal(t, "https", url.Scheme)
require.Equal(t, "public-gitlab-auth.com", url.Host)
require.Equal(t, "/oauth/authorize", url.Path)
require.Equal(t, "clientID", url.Query().Get("client_id"))
require.Equal(t, "https://projects.gitlab-example.com/auth", url.Query().Get("redirect_uri"))
require.NotEmpty(t, url.Query().Get("scope"))
require.NotEmpty(t, url.Query().Get("state"))
require.Equal(t, "https", locationURL.Scheme)
require.Equal(t, "public-gitlab-auth.com", locationURL.Host)
require.Equal(t, "/oauth/authorize", locationURL.Path)
require.Equal(t, "clientID", locationURL.Query().Get("client_id"))
require.Equal(t, "https://projects.gitlab-example.com/auth", locationURL.Query().Get("redirect_uri"))
require.NotEmpty(t, locationURL.Query().Get("scope"))
require.NotEmpty(t, locationURL.Query().Get("state"))
}
func TestAuthorizationHeaderPrivatePagesAccess(t *testing.T) {
RunPagesProcess(t,
withListeners([]ListenSpec{httpsListener}),
withArguments([]string{
"-config=" + defaultAuthConfig(t),
}),
)
testCases := []struct {
name string
authHeader http.Header
urlSuffix string
expectedStatus int
}{
{"Valid Token", http.Header{"Authorization": []string{"Bearer abc"}}, "private.project/", http.StatusOK},
{"Empty Authorization Header", http.Header{"Authorization": []string{""}}, "private.project/", http.StatusFound},
{"Valid Token but Unauthorized access", http.Header{"Authorization": []string{"Bearer abc"}}, "private.project.1/", http.StatusNotFound},
{"Invalid Token", http.Header{"Authorization": []string{"Bearer abc"}}, "private.project.2/", http.StatusNotFound},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rsp, err := GetRedirectPageWithHeaders(t, httpsListener, "group.auth.gitlab-example.com", tc.urlSuffix, tc.authHeader)
require.NoError(t, err)
testhelpers.Close(t, rsp.Body)
require.Equal(t, tc.expectedStatus, rsp.StatusCode)
if tc.expectedStatus == http.StatusFound {
require.Len(t, rsp.Header["Location"], 1)
}
})
}
}
func TestWhenAuthDeniedWillCauseUnauthorized(t *testing.T) {
......@@ -112,7 +147,7 @@ func TestWhenLoginCallbackWithUnencryptedCode(t *testing.T) {
cookie := rsp.Header.Get("Set-Cookie")
url, err := url.Parse(rsp.Header.Get("Location"))
locationURL, err := url.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
header := http.Header{
......@@ -121,7 +156,7 @@ func TestWhenLoginCallbackWithUnencryptedCode(t *testing.T) {
// Go to auth page with correct state will cause fetching the token
authrsp, err := GetPageFromListenerWithHeaders(t, httpsListener, "group.auth.gitlab-example.com", "/auth?code=1&state="+
url.Query().Get("state"), header)
locationURL.Query().Get("state"), header)
require.NoError(t, err)
testhelpers.Close(t, authrsp.Body)
......@@ -276,13 +311,13 @@ func TestCustomErrorPageWithAuth(t *testing.T) {
cookie := rsp.Header.Get("Set-Cookie")
url, err := url.Parse(rsp.Header.Get("Location"))
locationURL, err := url.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
state := url.Query().Get("state")
require.Equal(t, "http://"+tt.domain, url.Query().Get("domain"))
state := locationURL.Query().Get("state")
require.Equal(t, "http://"+tt.domain, locationURL.Query().Get("domain"))
pagesrsp, err := GetRedirectPage(t, httpListener, url.Host, url.Path+"?"+url.RawQuery)
pagesrsp, err := GetRedirectPage(t, httpListener, locationURL.Host, locationURL.Path+"?"+locationURL.RawQuery)
require.NoError(t, err)
testhelpers.Close(t, pagesrsp.Body)
......@@ -295,15 +330,15 @@ func TestCustomErrorPageWithAuth(t *testing.T) {
require.NoError(t, err)
testhelpers.Close(t, authrsp.Body)
url, err = url.Parse(authrsp.Header.Get("Location"))
locationURL, err = locationURL.Parse(authrsp.Header.Get("Location"))
require.NoError(t, err)
// Will redirect to custom domain
require.Equal(t, tt.domain, url.Host)
require.Equal(t, tt.domain, locationURL.Host)
// code must have changed since it's encrypted
code := url.Query().Get("code")
code := locationURL.Query().Get("code")
require.NotEqual(t, "1", code)
require.Equal(t, state, url.Query().Get("state"))
require.Equal(t, state, locationURL.Query().Get("state"))
// Run auth callback in custom domain
authrsp, err = GetRedirectPageWithCookie(t, httpListener, tt.domain, "/auth?code="+code+"&state="+
......@@ -316,11 +351,11 @@ func TestCustomErrorPageWithAuth(t *testing.T) {
groupCookie := authrsp.Header.Get("Set-Cookie")
require.Equal(t, http.StatusFound, authrsp.StatusCode)
url, err = url.Parse(authrsp.Header.Get("Location"))
locationURL, err = locationURL.Parse(authrsp.Header.Get("Location"))
require.NoError(t, err)
// Will redirect to custom domain error page
require.Equal(t, "http://"+tt.domain+tt.path, url.String())
require.Equal(t, "http://"+tt.domain+tt.path, locationURL.String())
// Fetch page in custom domain
anotherResp, err := GetRedirectPageWithCookie(t, httpListener, tt.domain, tt.path, groupCookie)
......@@ -350,12 +385,12 @@ func TestAccessControlUnderCustomDomainWithHTTPSProxy(t *testing.T) {
cookie := rsp.Header.Get("Set-Cookie")
url, err := url.Parse(rsp.Header.Get("Location"))
locationURL, err := url.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
state := url.Query().Get("state")
require.Equal(t, url.Query().Get("domain"), "https://private.domain.com")
pagesrsp, err := GetProxyRedirectPageWithCookie(t, proxyListener, url.Host, url.Path+"?"+url.RawQuery, "", true)
state := locationURL.Query().Get("state")
require.Equal(t, locationURL.Query().Get("domain"), "https://private.domain.com")
pagesrsp, err := GetProxyRedirectPageWithCookie(t, proxyListener, locationURL.Host, locationURL.Path+"?"+locationURL.RawQuery, "", true)
require.NoError(t, err)
testhelpers.Close(t, pagesrsp.Body)
......@@ -369,15 +404,15 @@ func TestAccessControlUnderCustomDomainWithHTTPSProxy(t *testing.T) {
require.NoError(t, err)
testhelpers.Close(t, authrsp.Body)
url, err = url.Parse(authrsp.Header.Get("Location"))
locationURL, err = locationURL.Parse(authrsp.Header.Get("Location"))
require.NoError(t, err)
// Will redirect to custom domain
require.Equal(t, "private.domain.com", url.Host)
require.Equal(t, "private.domain.com", locationURL.Host)
// code must have changed since it's encrypted
code := url.Query().Get("code")
code := locationURL.Query().Get("code")
require.NotEqual(t, "1", code)
require.Equal(t, state, url.Query().Get("state"))
require.Equal(t, state, locationURL.Query().Get("state"))
// Run auth callback in custom domain
authrsp, err = GetProxyRedirectPageWithCookie(t, proxyListener, "private.domain.com",
......@@ -390,11 +425,11 @@ func TestAccessControlUnderCustomDomainWithHTTPSProxy(t *testing.T) {
cookie = authrsp.Header.Get("Set-Cookie")
require.Equal(t, http.StatusFound, authrsp.StatusCode)
url, err = url.Parse(authrsp.Header.Get("Location"))
locationURL, err = locationURL.Parse(authrsp.Header.Get("Location"))
require.NoError(t, err)
// Will redirect to custom domain
require.Equal(t, "https://private.domain.com/", url.String())
require.Equal(t, "https://private.domain.com/", locationURL.String())
// Fetch page in custom domain
authrsp, err = GetProxyRedirectPageWithCookie(t, proxyListener, "private.domain.com", "/",
cookie, true)
......@@ -416,10 +451,10 @@ func TestAccessControlGroupDomain404RedirectsAuth(t *testing.T) {
testhelpers.Close(t, rsp.Body)
require.Equal(t, http.StatusFound, rsp.StatusCode)
// Redirects to the projects under gitlab pages domain for authentication flow
url, err := url.Parse(rsp.Header.Get("Location"))
locationURL, err := url.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
require.Equal(t, "projects.gitlab-example.com", url.Host)
require.Equal(t, "/auth", url.Path)
require.Equal(t, "projects.gitlab-example.com", locationURL.Host)
require.Equal(t, "/auth", locationURL.Path)
}
func TestAccessControlProject404DoesNotRedirect(t *testing.T) {
......@@ -649,25 +684,25 @@ func TestWhenAuthIsEnabledPrivateWillRedirectToAuthorizeWithValidMTLS(t *testing
require.Equal(t, http.StatusFound, rsp.StatusCode)
require.Len(t, rsp.Header["Location"], 1)
url, err := url.Parse(rsp.Header.Get("Location"))
locationURL, err := url.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
rsp, err = GetRedirectPage(t, httpsListener, url.Host, url.Path+"?"+url.RawQuery)
rsp, err = GetRedirectPage(t, httpsListener, locationURL.Host, locationURL.Path+"?"+locationURL.RawQuery)
require.NoError(t, err)
testhelpers.Close(t, rsp.Body)
require.Equal(t, http.StatusFound, rsp.StatusCode)
require.Len(t, rsp.Header["Location"], 1)
url, err = url.Parse(rsp.Header.Get("Location"))
locationURL, err = locationURL.Parse(rsp.Header.Get("Location"))
require.NoError(t, err)
require.Equal(t, "https", url.Scheme)
require.Equal(t, "public-gitlab-auth.com", url.Host)
require.Equal(t, "/oauth/authorize", url.Path)
require.Equal(t, "clientID", url.Query().Get("client_id"))
require.Equal(t, "https://projects.gitlab-example.com/auth", url.Query().Get("redirect_uri"))
require.NotEmpty(t, url.Query().Get("scope"))
require.NotEmpty(t, url.Query().Get("state"))
require.Equal(t, "https", locationURL.Scheme)
require.Equal(t, "public-gitlab-auth.com", locationURL.Host)
require.Equal(t, "/oauth/authorize", locationURL.Path)
require.Equal(t, "clientID", locationURL.Query().Get("client_id"))
require.Equal(t, "https://projects.gitlab-example.com/auth", locationURL.Query().Get("redirect_uri"))
require.NotEmpty(t, locationURL.Query().Get("scope"))
require.NotEmpty(t, locationURL.Query().Get("state"))
}
func TestWhenAuthIsEnabledPrivateWillRedirectToAuthorizeWithInvalidMTLS(t *testing.T) {
......