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
899976func (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 )
0 commit comments