2014-03-15 19:01:50 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2020-01-09 21:34:25 +00:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2014-03-15 19:01:50 +08:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-03-11 11:56:52 -05:00
package context
2014-03-15 19:01:50 +08:00
import (
2021-01-26 23:36:53 +08:00
"context"
"crypto/sha256"
"encoding/hex"
2021-01-29 12:33:47 +08:00
"fmt"
2016-11-29 22:49:06 +01:00
"html"
2014-03-23 01:44:02 +08:00
"html/template"
2014-04-16 00:27:29 +08:00
"io"
2014-03-15 19:01:50 +08:00
"net/http"
2018-03-15 23:13:34 +02:00
"net/url"
2017-06-26 03:06:40 +02:00
"path"
2021-01-26 23:36:53 +08:00
"strconv"
2014-03-22 16:40:09 -04:00
"strings"
2014-03-19 21:57:55 +08:00
"time"
2014-03-15 19:01:50 +08:00
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/models"
2021-01-05 21:05:40 +08:00
"code.gitea.io/gitea/modules/auth/sso"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/base"
2021-01-27 22:56:54 +08:00
mc "code.gitea.io/gitea/modules/cache"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
2021-01-30 16:55:53 +08:00
"code.gitea.io/gitea/modules/web/middleware"
2019-08-23 09:40:30 -07:00
2021-01-26 23:36:53 +08:00
"gitea.com/go-chi/cache"
"gitea.com/go-chi/session"
"github.com/go-chi/chi"
2021-03-01 21:08:10 +00:00
jsoniter "github.com/json-iterator/go"
2019-08-23 09:40:30 -07:00
"github.com/unknwon/com"
2021-01-26 23:36:53 +08:00
"github.com/unknwon/i18n"
"github.com/unrolled/render"
"golang.org/x/crypto/pbkdf2"
2014-03-15 19:01:50 +08:00
)
2021-01-26 23:36:53 +08:00
// Render represents a template render
type Render interface {
TemplateLookup ( tmpl string ) * template . Template
HTML ( w io . Writer , status int , name string , binding interface { } , htmlOpt ... render . HTMLOptions ) error
}
2014-03-15 09:17:16 -04:00
// Context represents context of a request.
2014-03-15 19:01:50 +08:00
type Context struct {
2021-01-26 23:36:53 +08:00
Resp ResponseWriter
Req * http . Request
Data map [ string ] interface { }
Render Render
translation . Locale
2014-07-31 17:25:34 -04:00
Cache cache . Cache
2021-01-26 23:36:53 +08:00
csrf CSRF
2021-01-30 16:55:53 +08:00
Flash * middleware . Flash
2014-07-26 00:24:27 -04:00
Session session . Store
2017-06-26 03:06:40 +02:00
Link string // current request URL
2017-11-28 01:43:51 -08:00
EscapedLink string
2014-11-18 11:07:16 -05:00
User * models . User
IsSigned bool
IsBasicAuth bool
2014-03-16 00:03:23 +08:00
2016-03-11 11:56:52 -05:00
Repo * Repository
2016-03-13 17:37:44 -04:00
Org * Organization
2014-03-15 19:01:50 +08:00
}
2021-01-05 21:05:40 +08:00
// GetData returns the data
func ( ctx * Context ) GetData ( ) map [ string ] interface { } {
return ctx . Data
}
2019-04-07 22:49:34 +00:00
// IsUserSiteAdmin returns true if current user is a site admin
func ( ctx * Context ) IsUserSiteAdmin ( ) bool {
return ctx . IsSigned && ctx . User . IsAdmin
}
// IsUserRepoOwner returns true if current user owns current repo
func ( ctx * Context ) IsUserRepoOwner ( ) bool {
return ctx . Repo . IsOwner ( )
}
// IsUserRepoAdmin returns true if current user is admin in current repo
func ( ctx * Context ) IsUserRepoAdmin ( ) bool {
return ctx . Repo . IsAdmin ( )
}
// IsUserRepoWriter returns true if current user has write privilege in current repo
func ( ctx * Context ) IsUserRepoWriter ( unitTypes [ ] models . UnitType ) bool {
for _ , unitType := range unitTypes {
if ctx . Repo . CanWrite ( unitType ) {
return true
}
}
return false
}
// IsUserRepoReaderSpecific returns true if current user can read current repo's specific part
func ( ctx * Context ) IsUserRepoReaderSpecific ( unitType models . UnitType ) bool {
return ctx . Repo . CanRead ( unitType )
}
// IsUserRepoReaderAny returns true if current user can read any part of current repo
func ( ctx * Context ) IsUserRepoReaderAny ( ) bool {
return ctx . Repo . HasAccess ( )
}
2021-01-24 10:23:05 -05:00
// RedirectToUser redirect to a differently-named user
func RedirectToUser ( ctx * Context , userName string , redirectUserID int64 ) {
user , err := models . GetUserByID ( redirectUserID )
if err != nil {
ctx . ServerError ( "GetUserByID" , err )
return
}
redirectPath := strings . Replace (
ctx . Req . URL . Path ,
userName ,
user . Name ,
1 ,
)
if ctx . Req . URL . RawQuery != "" {
redirectPath += "?" + ctx . Req . URL . RawQuery
}
ctx . Redirect ( path . Join ( setting . AppSubURL , redirectPath ) )
}
2016-11-25 14:51:01 +08:00
// HasAPIError returns true if error occurs in form validation.
func ( ctx * Context ) HasAPIError ( ) bool {
2014-05-05 13:08:01 -04:00
hasErr , ok := ctx . Data [ "HasError" ]
if ! ok {
return false
}
return hasErr . ( bool )
}
2016-11-25 14:51:01 +08:00
// GetErrMsg returns error message
2014-05-05 13:08:01 -04:00
func ( ctx * Context ) GetErrMsg ( ) string {
return ctx . Data [ "ErrorMsg" ] . ( string )
}
2014-03-15 10:52:14 -04:00
// HasError returns true if error occurs in form validation.
func ( ctx * Context ) HasError ( ) bool {
hasErr , ok := ctx . Data [ "HasError" ]
if ! ok {
return false
}
2014-04-13 18:12:07 -04:00
ctx . Flash . ErrorMsg = ctx . Data [ "ErrorMsg" ] . ( string )
ctx . Data [ "Flash" ] = ctx . Flash
2014-03-15 10:52:14 -04:00
return hasErr . ( bool )
}
2015-07-08 19:47:56 +08:00
// HasValue returns true if value of given name exists.
func ( ctx * Context ) HasValue ( name string ) bool {
_ , ok := ctx . Data [ name ]
return ok
}
2018-03-15 23:13:34 +02:00
// RedirectToFirst redirects to first not empty URL
func ( ctx * Context ) RedirectToFirst ( location ... string ) {
for _ , loc := range location {
if len ( loc ) == 0 {
continue
}
u , err := url . Parse ( loc )
2020-01-09 21:34:25 +00:00
if err != nil || ( ( u . Scheme != "" || u . Host != "" ) && ! strings . HasPrefix ( strings . ToLower ( loc ) , strings . ToLower ( setting . AppURL ) ) ) {
2018-03-15 23:13:34 +02:00
continue
}
ctx . Redirect ( loc )
return
}
ctx . Redirect ( setting . AppSubURL + "/" )
}
2014-08-02 13:47:33 -04:00
// HTML calls Context.HTML and converts template name to string.
2014-07-26 00:24:27 -04:00
func ( ctx * Context ) HTML ( status int , name base . TplName ) {
2015-12-20 01:06:54 -05:00
log . Debug ( "Template: %s" , name )
2021-01-29 12:33:47 +08:00
var startTime = time . Now ( )
ctx . Data [ "TmplLoadTimes" ] = func ( ) string {
return fmt . Sprint ( time . Since ( startTime ) . Nanoseconds ( ) / 1e6 ) + "ms"
}
2021-01-26 23:36:53 +08:00
if err := ctx . Render . HTML ( ctx . Resp , status , string ( name ) , ctx . Data ) ; err != nil {
2021-02-20 04:26:57 +00:00
if status == http . StatusInternalServerError && name == base . TplName ( "status/500" ) {
ctx . PlainText ( http . StatusInternalServerError , [ ] byte ( "Unable to find status/500 template" ) )
return
}
2021-01-26 23:36:53 +08:00
ctx . ServerError ( "Render failed" , err )
}
}
// HTMLString render content to a string but not http.ResponseWriter
func ( ctx * Context ) HTMLString ( name string , data interface { } ) ( string , error ) {
var buf strings . Builder
2021-01-29 12:33:47 +08:00
var startTime = time . Now ( )
ctx . Data [ "TmplLoadTimes" ] = func ( ) string {
return fmt . Sprint ( time . Since ( startTime ) . Nanoseconds ( ) / 1e6 ) + "ms"
}
2021-01-26 23:36:53 +08:00
err := ctx . Render . HTML ( & buf , 200 , string ( name ) , data )
return buf . String ( ) , err
2014-03-20 07:50:26 -04:00
}
2014-03-15 10:52:14 -04:00
// RenderWithErr used for page has form validation but need to prompt error to users.
2014-07-26 00:24:27 -04:00
func ( ctx * Context ) RenderWithErr ( msg string , tpl base . TplName , form interface { } ) {
2014-04-03 15:50:55 -04:00
if form != nil {
2021-01-30 16:55:53 +08:00
middleware . AssignForm ( form , ctx . Data )
2014-04-03 15:50:55 -04:00
}
2014-04-10 16:36:50 -04:00
ctx . Flash . ErrorMsg = msg
ctx . Data [ "Flash" ] = ctx . Flash
2014-03-20 07:50:26 -04:00
ctx . HTML ( 200 , tpl )
2014-03-15 10:52:14 -04:00
}
2018-01-10 22:34:17 +01:00
// NotFound displays a 404 (Not Found) page and prints the given error, if any.
func ( ctx * Context ) NotFound ( title string , err error ) {
2019-04-09 19:10:42 +01:00
ctx . notFoundInternal ( title , err )
}
func ( ctx * Context ) notFoundInternal ( title string , err error ) {
2014-05-01 18:53:41 -04:00
if err != nil {
2019-04-09 19:10:42 +01:00
log . ErrorWithSkip ( 2 , "%s: %v" , title , err )
2021-01-26 23:36:53 +08:00
if ! setting . IsProd ( ) {
2014-05-01 18:53:41 -04:00
ctx . Data [ "ErrorMsg" ] = err
}
2014-03-19 04:48:45 -04:00
}
2021-04-01 23:11:42 +08:00
// response simple meesage if Accept isn't text/html
reqTypes , has := ctx . Req . Header [ "Accept" ]
if has && len ( reqTypes ) > 0 {
notHTML := true
for _ , part := range reqTypes {
if strings . Contains ( part , "text/html" ) {
notHTML = false
break
}
}
if notHTML {
ctx . PlainText ( 404 , [ ] byte ( "Not found.\n" ) )
return
}
}
2019-02-19 17:09:47 -06:00
ctx . Data [ "IsRepo" ] = ctx . Repo . Repository != nil
2018-01-10 22:34:17 +01:00
ctx . Data [ "Title" ] = "Page Not Found"
ctx . HTML ( http . StatusNotFound , base . TplName ( "status/404" ) )
}
// ServerError displays a 500 (Internal Server Error) page and prints the given
// error, if any.
func ( ctx * Context ) ServerError ( title string , err error ) {
2019-04-09 19:10:42 +01:00
ctx . serverErrorInternal ( title , err )
}
func ( ctx * Context ) serverErrorInternal ( title string , err error ) {
2018-01-10 22:34:17 +01:00
if err != nil {
2019-04-09 19:10:42 +01:00
log . ErrorWithSkip ( 2 , "%s: %v" , title , err )
2021-01-26 23:36:53 +08:00
if ! setting . IsProd ( ) {
2018-01-10 22:34:17 +01:00
ctx . Data [ "ErrorMsg" ] = err
}
2014-05-01 18:53:41 -04:00
}
2018-01-10 22:34:17 +01:00
ctx . Data [ "Title" ] = "Internal Server Error"
2019-01-30 22:00:00 +00:00
ctx . HTML ( http . StatusInternalServerError , base . TplName ( "status/500" ) )
2014-03-15 19:01:50 +08:00
}
2016-08-30 02:08:38 -07:00
// NotFoundOrServerError use error check function to determine if the error
// is about not found. It responses with 404 status code for not found error,
// or error context description for logging purpose of 500 server error.
func ( ctx * Context ) NotFoundOrServerError ( title string , errck func ( error ) bool , err error ) {
2016-07-26 02:48:17 +08:00
if errck ( err ) {
2019-04-09 19:10:42 +01:00
ctx . notFoundInternal ( title , err )
2016-07-26 02:48:17 +08:00
return
}
2019-04-09 19:10:42 +01:00
ctx . serverErrorInternal ( title , err )
2016-07-26 02:48:17 +08:00
}
2021-01-26 23:36:53 +08:00
// Header returns a header
func ( ctx * Context ) Header ( ) http . Header {
return ctx . Resp . Header ( )
}
// FIXME: We should differ Query and Form, currently we just use form as query
// Currently to be compatible with macaron, we keep it.
// Query returns request form as string with default
func ( ctx * Context ) Query ( key string , defaults ... string ) string {
return ( * Forms ) ( ctx . Req ) . MustString ( key , defaults ... )
}
// QueryTrim returns request form as string with default and trimmed spaces
func ( ctx * Context ) QueryTrim ( key string , defaults ... string ) string {
return ( * Forms ) ( ctx . Req ) . MustTrimmed ( key , defaults ... )
}
// QueryStrings returns request form as strings with default
func ( ctx * Context ) QueryStrings ( key string , defaults ... [ ] string ) [ ] string {
return ( * Forms ) ( ctx . Req ) . MustStrings ( key , defaults ... )
}
// QueryInt returns request form as int with default
func ( ctx * Context ) QueryInt ( key string , defaults ... int ) int {
return ( * Forms ) ( ctx . Req ) . MustInt ( key , defaults ... )
}
// QueryInt64 returns request form as int64 with default
func ( ctx * Context ) QueryInt64 ( key string , defaults ... int64 ) int64 {
return ( * Forms ) ( ctx . Req ) . MustInt64 ( key , defaults ... )
}
// QueryBool returns request form as bool with default
func ( ctx * Context ) QueryBool ( key string , defaults ... bool ) bool {
return ( * Forms ) ( ctx . Req ) . MustBool ( key , defaults ... )
}
2016-11-25 14:51:01 +08:00
// HandleText handles HTTP status code
2015-03-28 22:30:05 +08:00
func ( ctx * Context ) HandleText ( status int , title string ) {
2015-07-08 19:47:56 +08:00
if ( status / 100 == 4 ) || ( status / 100 == 5 ) {
2019-04-02 08:48:31 +01:00
log . Error ( "%s" , title )
2015-03-28 22:30:05 +08:00
}
2015-10-15 21:28:12 -04:00
ctx . PlainText ( status , [ ] byte ( title ) )
2015-03-28 22:30:05 +08:00
}
2016-11-25 14:51:01 +08:00
// ServeContent serves content to http request
2014-04-16 00:27:29 +08:00
func ( ctx * Context ) ServeContent ( name string , r io . ReadSeeker , params ... interface { } ) {
modtime := time . Now ( )
for _ , p := range params {
switch v := p . ( type ) {
case time . Time :
modtime = v
}
}
2014-07-26 00:24:27 -04:00
ctx . Resp . Header ( ) . Set ( "Content-Description" , "File Transfer" )
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
ctx . Resp . Header ( ) . Set ( "Content-Disposition" , "attachment; filename=" + name )
ctx . Resp . Header ( ) . Set ( "Content-Transfer-Encoding" , "binary" )
ctx . Resp . Header ( ) . Set ( "Expires" , "0" )
ctx . Resp . Header ( ) . Set ( "Cache-Control" , "must-revalidate" )
ctx . Resp . Header ( ) . Set ( "Pragma" , "public" )
2020-08-13 18:18:18 +01:00
ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" , "Content-Disposition" )
2021-01-26 23:36:53 +08:00
http . ServeContent ( ctx . Resp , ctx . Req , name , modtime , r )
}
// PlainText render content as plain text
func ( ctx * Context ) PlainText ( status int , bs [ ] byte ) {
ctx . Resp . WriteHeader ( status )
2021-01-29 21:42:47 +08:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , "text/plain;charset=utf-8" )
2021-01-26 23:36:53 +08:00
if _ , err := ctx . Resp . Write ( bs ) ; err != nil {
ctx . ServerError ( "Render JSON failed" , err )
}
}
// ServeFile serves given file to response.
func ( ctx * Context ) ServeFile ( file string , names ... string ) {
var name string
if len ( names ) > 0 {
name = names [ 0 ]
} else {
name = path . Base ( file )
}
ctx . Resp . Header ( ) . Set ( "Content-Description" , "File Transfer" )
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
ctx . Resp . Header ( ) . Set ( "Content-Disposition" , "attachment; filename=" + name )
ctx . Resp . Header ( ) . Set ( "Content-Transfer-Encoding" , "binary" )
ctx . Resp . Header ( ) . Set ( "Expires" , "0" )
ctx . Resp . Header ( ) . Set ( "Cache-Control" , "must-revalidate" )
ctx . Resp . Header ( ) . Set ( "Pragma" , "public" )
http . ServeFile ( ctx . Resp , ctx . Req , file )
}
// Error returned an error to web browser
func ( ctx * Context ) Error ( status int , contents ... string ) {
var v = http . StatusText ( status )
if len ( contents ) > 0 {
v = contents [ 0 ]
}
http . Error ( ctx . Resp , v , status )
}
// JSON render content as JSON
func ( ctx * Context ) JSON ( status int , content interface { } ) {
2021-01-29 21:42:47 +08:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/json;charset=utf-8" )
2021-01-27 22:46:22 +01:00
ctx . Resp . WriteHeader ( status )
2021-03-01 21:08:10 +00:00
json := jsoniter . ConfigCompatibleWithStandardLibrary
2021-01-26 23:36:53 +08:00
if err := json . NewEncoder ( ctx . Resp ) . Encode ( content ) ; err != nil {
ctx . ServerError ( "Render JSON failed" , err )
}
}
// Redirect redirect the request
func ( ctx * Context ) Redirect ( location string , status ... int ) {
code := http . StatusFound
if len ( status ) == 1 {
code = status [ 0 ]
}
http . Redirect ( ctx . Resp , ctx . Req , location , code )
}
2021-03-07 08:12:43 +00:00
// SetCookie convenience function to set most cookies consistently
// CSRF and a few others are the exception here
func ( ctx * Context ) SetCookie ( name , value string , expiry int ) {
middleware . SetCookie ( ctx . Resp , name , value ,
expiry ,
setting . AppSubURL ,
setting . SessionConfig . Domain ,
setting . SessionConfig . Secure ,
true ,
middleware . SameSite ( setting . SessionConfig . SameSite ) )
}
// DeleteCookie convenience function to delete most cookies consistently
// CSRF and a few others are the exception here
func ( ctx * Context ) DeleteCookie ( name string ) {
middleware . SetCookie ( ctx . Resp , name , "" ,
- 1 ,
setting . AppSubURL ,
setting . SessionConfig . Domain ,
setting . SessionConfig . Secure ,
true ,
middleware . SameSite ( setting . SessionConfig . SameSite ) )
2021-01-26 23:36:53 +08:00
}
// GetCookie returns given cookie value from request header.
func ( ctx * Context ) GetCookie ( name string ) string {
2021-01-30 16:55:53 +08:00
return middleware . GetCookie ( ctx . Req , name )
2021-01-26 23:36:53 +08:00
}
// GetSuperSecureCookie returns given cookie value from request header with secret string.
func ( ctx * Context ) GetSuperSecureCookie ( secret , name string ) ( string , bool ) {
val := ctx . GetCookie ( name )
2021-03-07 08:12:43 +00:00
return ctx . CookieDecrypt ( secret , val )
}
// CookieDecrypt returns given value from with secret string.
func ( ctx * Context ) CookieDecrypt ( secret , val string ) ( string , bool ) {
2021-01-26 23:36:53 +08:00
if val == "" {
return "" , false
}
text , err := hex . DecodeString ( val )
if err != nil {
return "" , false
}
key := pbkdf2 . Key ( [ ] byte ( secret ) , [ ] byte ( secret ) , 1000 , 16 , sha256 . New )
text , err = com . AESGCMDecrypt ( key , text )
return string ( text ) , err == nil
}
// SetSuperSecureCookie sets given cookie value to response header with secret string.
2021-03-07 08:12:43 +00:00
func ( ctx * Context ) SetSuperSecureCookie ( secret , name , value string , expiry int ) {
text := ctx . CookieEncrypt ( secret , value )
ctx . SetCookie ( name , text , expiry )
}
// CookieEncrypt encrypts a given value using the provided secret
func ( ctx * Context ) CookieEncrypt ( secret , value string ) string {
2021-01-26 23:36:53 +08:00
key := pbkdf2 . Key ( [ ] byte ( secret ) , [ ] byte ( secret ) , 1000 , 16 , sha256 . New )
text , err := com . AESGCMEncrypt ( key , [ ] byte ( value ) )
if err != nil {
panic ( "error encrypting cookie: " + err . Error ( ) )
}
2021-03-07 08:12:43 +00:00
return hex . EncodeToString ( text )
2021-01-26 23:36:53 +08:00
}
// GetCookieInt returns cookie result in int type.
func ( ctx * Context ) GetCookieInt ( name string ) int {
r , _ := strconv . Atoi ( ctx . GetCookie ( name ) )
return r
}
// GetCookieInt64 returns cookie result in int64 type.
func ( ctx * Context ) GetCookieInt64 ( name string ) int64 {
r , _ := strconv . ParseInt ( ctx . GetCookie ( name ) , 10 , 64 )
return r
}
// GetCookieFloat64 returns cookie result in float64 type.
func ( ctx * Context ) GetCookieFloat64 ( name string ) float64 {
v , _ := strconv . ParseFloat ( ctx . GetCookie ( name ) , 64 )
return v
}
// RemoteAddr returns the client machie ip address
func ( ctx * Context ) RemoteAddr ( ) string {
return ctx . Req . RemoteAddr
}
// Params returns the param on route
func ( ctx * Context ) Params ( p string ) string {
s , _ := url . PathUnescape ( chi . URLParam ( ctx . Req , strings . TrimPrefix ( p , ":" ) ) )
return s
}
// ParamsInt64 returns the param on route as int64
func ( ctx * Context ) ParamsInt64 ( p string ) int64 {
v , _ := strconv . ParseInt ( ctx . Params ( p ) , 10 , 64 )
return v
}
// SetParams set params into routes
func ( ctx * Context ) SetParams ( k , v string ) {
chiCtx := chi . RouteContext ( ctx . Req . Context ( ) )
chiCtx . URLParams . Add ( strings . TrimPrefix ( k , ":" ) , url . PathEscape ( v ) )
}
// Write writes data to webbrowser
func ( ctx * Context ) Write ( bs [ ] byte ) ( int , error ) {
return ctx . Resp . Write ( bs )
}
// Written returns true if there are something sent to web browser
func ( ctx * Context ) Written ( ) bool {
return ctx . Resp . Status ( ) > 0
}
// Status writes status code
func ( ctx * Context ) Status ( status int ) {
ctx . Resp . WriteHeader ( status )
}
// Handler represents a custom handler
type Handler func ( * Context )
// enumerate all content
var (
contextKey interface { } = "default_context"
)
// WithContext set up install context in request
func WithContext ( req * http . Request , ctx * Context ) * http . Request {
return req . WithContext ( context . WithValue ( req . Context ( ) , contextKey , ctx ) )
}
// GetContext retrieves install context from request
func GetContext ( req * http . Request ) * Context {
return req . Context ( ) . Value ( contextKey ) . ( * Context )
}
2021-01-28 01:46:35 +08:00
// SignedUserName returns signed user's name via context
func SignedUserName ( req * http . Request ) string {
2021-01-30 16:55:53 +08:00
if middleware . IsInternalPath ( req ) {
2021-01-28 01:46:35 +08:00
return ""
}
2021-01-30 16:55:53 +08:00
if middleware . IsAPIPath ( req ) {
2021-01-28 01:46:35 +08:00
ctx , ok := req . Context ( ) . Value ( apiContextKey ) . ( * APIContext )
if ok {
v := ctx . Data [ "SignedUserName" ]
if res , ok := v . ( string ) ; ok {
return res
}
}
} else {
ctx , ok := req . Context ( ) . Value ( contextKey ) . ( * Context )
if ok {
v := ctx . Data [ "SignedUserName" ]
if res , ok := v . ( string ) ; ok {
return res
}
}
}
return ""
}
2021-01-26 23:36:53 +08:00
func getCsrfOpts ( ) CsrfOptions {
return CsrfOptions {
Secret : setting . SecretKey ,
Cookie : setting . CSRFCookieName ,
SetCookie : true ,
Secure : setting . SessionConfig . Secure ,
CookieHTTPOnly : setting . CSRFCookieHTTPOnly ,
Header : "X-Csrf-Token" ,
CookieDomain : setting . SessionConfig . Domain ,
CookiePath : setting . SessionConfig . CookiePath ,
2021-03-07 08:12:43 +00:00
SameSite : setting . SessionConfig . SameSite ,
2021-01-26 23:36:53 +08:00
}
2014-04-10 14:37:43 -04:00
}
2014-07-26 00:24:27 -04:00
// Contexter initializes a classic context for a request.
2021-01-26 23:36:53 +08:00
func Contexter ( ) func ( next http . Handler ) http . Handler {
2021-01-27 22:56:54 +08:00
var rnd = templates . HTMLRenderer ( )
2021-01-26 23:36:53 +08:00
var csrfOpts = getCsrfOpts ( )
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
2021-01-30 16:55:53 +08:00
var locale = middleware . Locale ( resp , req )
2021-01-26 23:36:53 +08:00
var startTime = time . Now ( )
var link = setting . AppSubURL + strings . TrimSuffix ( req . URL . EscapedPath ( ) , "/" )
var ctx = Context {
Resp : NewResponse ( resp ) ,
2021-01-27 22:56:54 +08:00
Cache : mc . GetCache ( ) ,
2021-01-26 23:36:53 +08:00
Locale : locale ,
Link : link ,
Render : rnd ,
Session : session . GetSession ( req ) ,
Repo : & Repository {
PullRequest : & PullRequest { } ,
} ,
Org : & Organization { } ,
Data : map [ string ] interface { } {
"CurrentURL" : setting . AppSubURL + req . URL . RequestURI ( ) ,
"PageStartTime" : startTime ,
2021-01-29 12:33:47 +08:00
"Link" : link ,
2021-01-26 23:36:53 +08:00
} ,
}
ctx . Req = WithContext ( req , & ctx )
ctx . csrf = Csrfer ( csrfOpts , & ctx )
// Get flash.
flashCookie := ctx . GetCookie ( "macaron_flash" )
vals , _ := url . ParseQuery ( flashCookie )
if len ( vals ) > 0 {
2021-01-30 16:55:53 +08:00
f := & middleware . Flash {
2021-01-26 23:36:53 +08:00
DataStore : & ctx ,
Values : vals ,
ErrorMsg : vals . Get ( "error" ) ,
SuccessMsg : vals . Get ( "success" ) ,
InfoMsg : vals . Get ( "info" ) ,
WarningMsg : vals . Get ( "warning" ) ,
}
ctx . Data [ "Flash" ] = f
}
2021-01-30 16:55:53 +08:00
f := & middleware . Flash {
2021-01-26 23:36:53 +08:00
DataStore : & ctx ,
Values : url . Values { } ,
ErrorMsg : "" ,
WarningMsg : "" ,
InfoMsg : "" ,
SuccessMsg : "" ,
}
ctx . Resp . Before ( func ( resp ResponseWriter ) {
if flash := f . Encode ( ) ; len ( flash ) > 0 {
2021-01-30 16:55:53 +08:00
middleware . SetCookie ( resp , "macaron_flash" , flash , 0 ,
2021-01-27 22:56:54 +08:00
setting . SessionConfig . CookiePath ,
2021-01-30 16:55:53 +08:00
middleware . Domain ( setting . SessionConfig . Domain ) ,
middleware . HTTPOnly ( true ) ,
middleware . Secure ( setting . SessionConfig . Secure ) ,
2021-03-07 08:12:43 +00:00
middleware . SameSite ( setting . SessionConfig . SameSite ) ,
2021-01-27 22:56:54 +08:00
)
return
2021-01-26 23:36:53 +08:00
}
2021-03-07 08:12:43 +00:00
middleware . SetCookie ( ctx . Resp , "macaron_flash" , "" , - 1 ,
2021-01-26 23:36:53 +08:00
setting . SessionConfig . CookiePath ,
2021-01-30 16:55:53 +08:00
middleware . Domain ( setting . SessionConfig . Domain ) ,
middleware . HTTPOnly ( true ) ,
middleware . Secure ( setting . SessionConfig . Secure ) ,
2021-03-07 08:12:43 +00:00
middleware . SameSite ( setting . SessionConfig . SameSite ) ,
2021-01-26 23:36:53 +08:00
)
} )
ctx . Flash = f
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
if ctx . Req . Method == "POST" && strings . Contains ( ctx . Req . Header . Get ( "Content-Type" ) , "multipart/form-data" ) {
if err := ctx . Req . ParseMultipartForm ( setting . Attachment . MaxSize << 20 ) ; err != nil && ! strings . Contains ( err . Error ( ) , "EOF" ) { // 32MB max size
ctx . ServerError ( "ParseMultipartForm" , err )
return
}
}
2018-08-27 04:23:27 +02:00
2021-01-26 23:36:53 +08:00
// Get user from session if logged in.
ctx . User , ctx . IsBasicAuth = sso . SignedInUser ( ctx . Req , ctx . Resp , & ctx , ctx . Session )
if ctx . User != nil {
ctx . IsSigned = true
ctx . Data [ "IsSigned" ] = ctx . IsSigned
ctx . Data [ "SignedUser" ] = ctx . User
ctx . Data [ "SignedUserID" ] = ctx . User . ID
ctx . Data [ "SignedUserName" ] = ctx . User . Name
ctx . Data [ "IsAdmin" ] = ctx . User . IsAdmin
} else {
ctx . Data [ "SignedUserID" ] = int64 ( 0 )
ctx . Data [ "SignedUserName" ] = ""
}
2015-02-06 21:16:23 -05:00
2021-01-26 23:36:53 +08:00
ctx . Resp . Header ( ) . Set ( ` X-Frame-Options ` , ` SAMEORIGIN ` )
ctx . Data [ "CsrfToken" ] = html . EscapeString ( ctx . csrf . GetToken ( ) )
ctx . Data [ "CsrfTokenHtml" ] = template . HTML ( ` <input type="hidden" name="_csrf" value=" ` + ctx . Data [ "CsrfToken" ] . ( string ) + ` "> ` )
log . Debug ( "Session ID: %s" , ctx . Session . ID ( ) )
log . Debug ( "CSRF Token: %v" , ctx . Data [ "CsrfToken" ] )
ctx . Data [ "IsLandingPageHome" ] = setting . LandingPageURL == setting . LandingPageHome
ctx . Data [ "IsLandingPageExplore" ] = setting . LandingPageURL == setting . LandingPageExplore
ctx . Data [ "IsLandingPageOrganizations" ] = setting . LandingPageURL == setting . LandingPageOrganizations
ctx . Data [ "ShowRegistrationButton" ] = setting . Service . ShowRegistrationButton
ctx . Data [ "ShowMilestonesDashboardPage" ] = setting . Service . ShowMilestonesDashboardPage
ctx . Data [ "ShowFooterBranding" ] = setting . ShowFooterBranding
ctx . Data [ "ShowFooterVersion" ] = setting . ShowFooterVersion
ctx . Data [ "EnableSwagger" ] = setting . API . EnableSwagger
ctx . Data [ "EnableOpenIDSignIn" ] = setting . Service . EnableOpenIDSignIn
ctx . Data [ "DisableMigrations" ] = setting . Repository . DisableMigrations
ctx . Data [ "ManifestData" ] = setting . ManifestData
ctx . Data [ "i18n" ] = locale
ctx . Data [ "Tr" ] = i18n . Tr
ctx . Data [ "Lang" ] = locale . Language ( )
ctx . Data [ "AllLangs" ] = translation . AllLangs ( )
for _ , lang := range translation . AllLangs ( ) {
if lang . Lang == locale . Language ( ) {
ctx . Data [ "LangName" ] = lang . Name
break
}
}
2020-12-22 12:13:50 +01:00
2021-01-26 23:36:53 +08:00
next . ServeHTTP ( ctx . Resp , ctx . Req )
} )
2014-03-15 19:01:50 +08:00
}
}