Skip to content

Commit cea3a90

Browse files
committed
Release token correctly, with usable web page
1 parent 35bf9c3 commit cea3a90

File tree

8 files changed

+142
-40
lines changed

8 files changed

+142
-40
lines changed

api/swagger.yml

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,15 +2108,6 @@ components:
21082108
destination:
21092109
$ref: "#/components/schemas/IcebergLocalTable"
21102110

2111-
ReleaseToken:
2112-
type: object
2113-
required:
2114-
- token
2115-
properties:
2116-
token:
2117-
type: string
2118-
description: Login request token to release
2119-
21202111
paths:
21212112
/setup_comm_prefs:
21222113
post:
@@ -3253,19 +3244,22 @@ paths:
32533244
default:
32543245
$ref: "#/components/responses/ServerError"
32553246

3256-
/auth/get-token/release-token:
3257-
post:
3247+
/auth/get-token/release-token/{loginRequestToken}:
3248+
parameters:
3249+
- in: path
3250+
# The mailbox is secret. It is identified by the loginRequestToken
3251+
# - a JWT which is _not_ secret. So it can safely go in the header.
3252+
name: loginRequestToken
3253+
required: true
3254+
schema:
3255+
type: string
3256+
description: login request token returned by getTokenRedirect.
3257+
get: # Called by opening a URL on the browser!
32583258
tags:
32593259
- auth
32603260
- experimental
32613261
operationId: releaseTokenToMailbox
32623262
summary: release a token for the current (authenticated) user to the mailbox of this login request.
3263-
requestBody:
3264-
required: true
3265-
content:
3266-
application/json:
3267-
schema:
3268-
$ref: "#/components/schemas/ReleaseToken"
32693263
responses:
32703264
204:
32713265
description: token released

cmd/lakectl/cmd/login.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"errors"
45
"fmt"
56
"net/http"
67
"net/url"
@@ -24,6 +25,11 @@ type webLoginParams struct {
2425
RedirectURL string
2526
}
2627

28+
var (
29+
errTryAgain = errors.New("HTTP request failed; retry")
30+
errDontTryAgain = backoff.Permanent(errors.New("failed to get token"))
31+
)
32+
2733
var loginCmd = &cobra.Command{
2834
Use: "login",
2935
Short: "Use a web browser to log into lakeFS",
@@ -58,6 +64,12 @@ var loginCmd = &cobra.Command{
5864
if err != nil {
5965
return nil, err
6066
}
67+
if resp.JSON404 != nil {
68+
return nil, errTryAgain
69+
}
70+
if resp.JSON200 == nil {
71+
return nil, errDontTryAgain
72+
}
6173
return resp.JSON200, nil
6274
},
6375
// Initial backoff is rapid, in case user is already logged in. Then
@@ -72,8 +84,9 @@ var loginCmd = &cobra.Command{
7284
DieErr(fmt.Errorf("get login token: %w", err))
7385
}
7486

75-
// BUG(ariels): Remove!
76-
fmt.Printf("TOKEN: %s\nExpires: %d\n", loginToken.Token, *loginToken.TokenExpiration)
87+
if loginToken == nil {
88+
Die("nil login token", 2)
89+
}
7790

7891
cache := getTokenCacheOnce()
7992
err = cache.SaveToken(loginToken)

cmd/lakectl/cmd/root.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/treeverse/lakefs/pkg/api/apigen"
2727
"github.com/treeverse/lakefs/pkg/api/apiutil"
2828
"github.com/treeverse/lakefs/pkg/authentication/externalidp/awsiam"
29+
"github.com/treeverse/lakefs/pkg/authentication/internalidp"
2930
lakefsconfig "github.com/treeverse/lakefs/pkg/config"
3031
"github.com/treeverse/lakefs/pkg/git"
3132
giterror "github.com/treeverse/lakefs/pkg/git/errors"
@@ -627,8 +628,8 @@ func getClient() *apigen.ClientWithResponses {
627628
DieErr(err)
628629
}
629630

630-
useIAMAuth := awsIAMparams != nil && accessKeyID == "" && secretAccessKey == ""
631-
if useIAMAuth {
631+
useJWTAuth := accessKeyID == "" && secretAccessKey == ""
632+
if useJWTAuth {
632633
opts = getClientOptions(awsIAMparams, serverEndpoint)
633634
}
634635

@@ -664,6 +665,14 @@ func CreateTokenCacheCallback() awsiam.TokenCacheCallback {
664665
func getClientOptions(awsIAMparams *awsiam.IAMAuthParams, serverEndpoint string) []apigen.ClientOption {
665666
token := getTokenOnce()
666667

668+
logger := logging.ContextUnavailable().WithField("component", "client_auth")
669+
670+
if awsIAMparams == nil {
671+
return []apigen.ClientOption{
672+
internalidp.WithLoginTokenAuth(logger, internalidp.NewFixedLoginClient(token.Token)),
673+
}
674+
}
675+
667676
tokenCacheCallback := CreateTokenCacheCallback()
668677

669678
awsLogSigning := cfg.Credentials.Provider.AWSIAM.ClientLogPreSigningRequest
@@ -683,7 +692,7 @@ func getClientOptions(awsIAMparams *awsiam.IAMAuthParams, serverEndpoint string)
683692

684693
awsAuthProvider := awsiam.WithAWSIAMRoleAuthProviderOption(
685694
awsIAMparams,
686-
logging.ContextUnavailable(),
695+
logger,
687696
loginClient,
688697
token,
689698
tokenCacheCallback,

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
github.com/jamiealquiza/tachymeter v2.0.0+incompatible
2626
github.com/jedib0t/go-pretty/v6 v6.6.7
2727
github.com/manifoldco/promptui v0.9.0
28-
github.com/matoous/go-nanoid/v2 v2.0.0
28+
github.com/matoous/go-nanoid/v2 v2.1.0
2929
github.com/minio/minio-go/v7 v7.0.63
3030
github.com/mitchellh/go-homedir v1.1.0
3131
github.com/ory/dockertest/v3 v3.12.0
@@ -139,8 +139,9 @@ require (
139139
github.com/barweiss/go-tuple v1.1.2 // indirect
140140
github.com/benbjohnson/clock v1.3.0 // indirect
141141
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
142-
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
142+
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
143143
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
144+
github.com/elnormous/contenttype v1.0.4 // indirect
144145
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
145146
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
146147
github.com/felixge/httpsnoop v1.0.4 // indirect

go.sum

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
391391
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
392392
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
393393
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
394-
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
395-
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
394+
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
395+
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
396396
github.com/deepmap/oapi-codegen v1.5.6 h1:hlUtA13SL2HNoC/5vXDFdGm2AaN1j9/rUu7zedjBiqg=
397397
github.com/deepmap/oapi-codegen v1.5.6/go.mod h1:NoliSkZp7cRAkisw65+PUtvgy56f21QQesX1tVUzS8g=
398398
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
@@ -423,6 +423,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
423423
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
424424
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
425425
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
426+
github.com/elnormous/contenttype v1.0.4 h1:FjmVNkvQOGqSX70yvocph7keC8DtmJaLzTTq6ZOQCI8=
427+
github.com/elnormous/contenttype v1.0.4/go.mod h1:5KTOW8m1kdX1dLMiUJeN9szzR2xkngiv2K+RVZwWBbI=
426428
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
427429
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
428430
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -816,8 +818,8 @@ github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYt
816818
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
817819
github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek=
818820
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
819-
github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
820-
github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
821+
github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
822+
github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM=
821823
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
822824
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
823825
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=

go.work.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,6 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpX
454454
contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc=
455455
contrib.go.opencensus.io/integrations/ocsql v0.1.7 h1:G3k7C0/W44zcqkpRSFyjU9f6HZkbwIrL//qqnlqWZ60=
456456
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
457-
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
458457
gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho=
459458
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
460459
github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk=
@@ -581,6 +580,7 @@ github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53E
581580
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
582581
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
583582
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
583+
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
584584
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
585585
github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA=
586586
github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo=
@@ -638,7 +638,6 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
638638
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
639639
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
640640
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
641-
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
642641
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
643642
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
644643
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
@@ -776,6 +775,7 @@ github.com/mailgun/raymond/v2 v2.0.46 h1:aOYHhvTpF5USySJ0o7cpPno/Uh2I5qg2115K25A
776775
github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
777776
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
778777
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
778+
github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM=
779779
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd h1:HvFwW+cm9bCbZ/+vuGNq7CRWXql8c0y8nGeYpqmpvmk=
780780
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
781781
github.com/mattn/goveralls v0.0.2 h1:7eJB6EqsPhRVxvwEXGnqdO2sJI0PTsrWoTMXEk9/OQc=

pkg/api/controller.go

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/json"
88
"errors"
99
"fmt"
10+
"html/template"
1011
"io"
1112
"mime"
1213
"mime/multipart"
@@ -22,6 +23,7 @@ import (
2223

2324
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
2425
"github.com/davecgh/go-spew/spew"
26+
"github.com/elnormous/contenttype"
2527
"github.com/go-openapi/swag"
2628
"github.com/gorilla/sessions"
2729
authacl "github.com/treeverse/lakefs/contrib/auth/acl"
@@ -866,6 +868,7 @@ func (c *Controller) GetTokenRedirect(w http.ResponseWriter, r *http.Request) {
866868

867869
w.Header().Set("Location", redirect.RedirectURL)
868870
w.Header().Set("X-LakeFS-Mailbox", redirect.Mailbox)
871+
869872
writeResponse(w, r, http.StatusOK, nil)
870873
}
871874

@@ -877,23 +880,97 @@ func (c *Controller) GetTokenFromMailbox(w http.ResponseWriter, r *http.Request,
877880
return
878881
}
879882

883+
// This call is repeated, so only log after we've found the token is valid.
884+
c.LogAction(ctx, "get_token_from_mailbox", r, "", "", "")
885+
886+
c.Logger.
887+
WithContext(r.Context()).
888+
WithFields(logging.Fields{
889+
"mailbox": mailbox,
890+
"token_expiration": expiresAt,
891+
}).
892+
Debug("Got login token")
893+
880894
response := apigen.AuthenticationToken{
881895
Token: token,
882896
TokenExpiration: swag.Int64(expiresAt.Unix()),
883897
}
884898
writeResponse(w, r, http.StatusOK, response)
885899
}
886900

887-
func (c *Controller) ReleaseTokenToMailbox(w http.ResponseWriter, r *http.Request, params apigen.ReleaseTokenToMailboxJSONRequestBody) {
901+
var releasedTokenTemplate = template.Must(
902+
template.New("released-token").
903+
Parse(`{{define "releasedToken" -}}
904+
<!doctype html>
905+
<html>
906+
<title>Logged in</title>
907+
<body>
908+
<div>You are logged in as <code>{{.Username}}</code>. It is safe to close this window.</div>
909+
</body>
910+
</html>
911+
{{- end}}`))
912+
913+
type UserData struct {
914+
Username string
915+
}
916+
917+
var (
918+
textHTML = contenttype.MediaType{"text", "html", nil}
919+
releaseTokenAcceptableMediaTypes = []contenttype.MediaType{
920+
textHTML,
921+
{"*", "*", nil},
922+
}
923+
)
924+
925+
func (c *Controller) ReleaseTokenToMailbox(w http.ResponseWriter, r *http.Request, loginRequestToken string) {
888926
ctx := r.Context()
889927

890-
loginRequestToken := params.Token
928+
c.LogAction(ctx, "release_token_to_mailbox", r, "", "", "")
929+
891930
// Release will release a token for the authenticated user.
892931
err := c.loginTokenProvider.Release(ctx, loginRequestToken)
893932
if c.handleAPIError(ctx, w, r, err) {
894933
return
895934
}
896-
writeResponse(w, r, http.StatusNoContent, nil)
935+
936+
mediaType, _, err := contenttype.GetAcceptableMediaType(r, releaseTokenAcceptableMediaTypes)
937+
if err != nil {
938+
c.Logger.
939+
WithContext(r.Context()).
940+
WithError(err).
941+
WithField("accept", r.Header.Get("Accept")).
942+
Warn("Failed to parse Content-Type - no user-friendly page")
943+
// Keep going - errors are safe here, at worst the user will not get a pretty page.
944+
}
945+
946+
switch {
947+
case mediaType.EqualsMIME(textHTML):
948+
username := ""
949+
user, err := auth.GetUser(ctx)
950+
if err != nil {
951+
// Errors are safe here, at worst we won't tell user their name.
952+
c.Logger.
953+
WithContext(r.Context()).
954+
WithError(err).
955+
WithField("accept", r.Header.Get("Accept")).
956+
Warn("Failed to get user - they won't see their logged-in name on the page")
957+
}
958+
if user != nil {
959+
username = user.Username
960+
}
961+
// This endpoint is _usually_ visited by a browser. Report to the user that
962+
// they logged in, telling them the name they used to log in.
963+
httputil.KeepPrivate(w)
964+
965+
err = releasedTokenTemplate.ExecuteTemplate(w, "releasedToken", &UserData{Username: username})
966+
if c.handleAPIError(ctx, w, r, err) {
967+
return
968+
}
969+
970+
w.WriteHeader(http.StatusOK)
971+
default:
972+
writeResponse(w, r, http.StatusNoContent, nil)
973+
}
897974
}
898975

899976
func (c *Controller) GetPhysicalAddress(w http.ResponseWriter, r *http.Request, repository, branch string, params apigen.GetPhysicalAddressParams) {
@@ -4873,11 +4950,7 @@ func (c *Controller) GetObject(w http.ResponseWriter, r *http.Request, repositor
48734950
w.Header().Set("Last-Modified", lastModified)
48744951
w.Header().Set("Content-Type", entry.ContentType)
48754952
// for security, make sure the browser and any proxies en route don't cache the response
4876-
w.Header().Set("Cache-Control", "no-store, must-revalidate")
4877-
w.Header().Set("Expires", "0")
4878-
w.Header().Set("X-Content-Type-Options", "nosniff")
4879-
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
4880-
w.Header().Set("Content-Security-Policy", "default-src 'none'")
4953+
httputil.KeepPrivate(w)
48814954
w.Header().Set("Content-Disposition", "attachment")
48824955

48834956
// handle partial response if byte range supplied
@@ -6157,8 +6230,7 @@ func (c *Controller) GetUsageReportSummary(w http.ResponseWriter, r *http.Reques
61576230

61586231
// base on content-type return plain text or json (default)
61596232
if r.Header.Get("Accept") == "text/plain" {
6160-
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
6161-
w.Header().Set("X-Content-Type-Options", "nosniff")
6233+
httputil.KeepPrivate(w)
61626234
_, _ = fmt.Fprintf(w, "Usage for installation ID: %s\n", installationID)
61636235
for _, rec := range records {
61646236
_, _ = fmt.Fprintf(w, "%d-%02d: %12d\n", rec.Year, rec.Month, rec.Count)

pkg/httputil/response.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,14 @@ import "net/http"
66
func IsSuccessStatusCode(response *http.Response) bool {
77
return response.StatusCode >= http.StatusOK && response.StatusCode < http.StatusMultipleChoices
88
}
9+
10+
// KeepPrivate sets some headers on response to keep it private: no caching, no sniff,
11+
// sameorigin, etc. It should be used on any non-API response (HTML, text, object contents,
12+
// etc.)
13+
func KeepPrivate(w http.ResponseWriter) {
14+
w.Header().Set("Cache-Control", "no-store, must-revalidate")
15+
w.Header().Set("Expires", "0")
16+
w.Header().Set("X-Content-Type-Options", "nosniff")
17+
w.Header().Set("X-Frame-Options", "SAMEORIGIN")
18+
w.Header().Set("Content-Security-Policy", "default-src 'none'")
19+
}

0 commit comments

Comments
 (0)