2014-03-15 15:01:50 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2020-01-10 00:34:25 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2014-03-15 15:01:50 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-03-11 19:56:52 +03:00
package context
2014-03-15 15:01:50 +04:00
import (
2021-01-26 18:36:53 +03:00
"context"
"crypto/sha256"
"encoding/hex"
2021-01-29 07:33:47 +03:00
"fmt"
2016-11-30 00:49:06 +03:00
"html"
2014-03-22 21:44:02 +04:00
"html/template"
2014-04-15 20:27:29 +04:00
"io"
2014-03-15 15:01:50 +04:00
"net/http"
2018-03-16 00:13:34 +03:00
"net/url"
2017-06-26 04:06:40 +03:00
"path"
2021-01-26 18:36:53 +03:00
"strconv"
2014-03-23 00:40:09 +04:00
"strings"
2014-03-19 17:57:55 +04:00
"time"
2014-03-15 15:01:50 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
2021-01-27 17:56:54 +03:00
mc "code.gitea.io/gitea/modules/cache"
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
2021-01-30 11:55:53 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2021-06-09 20:53:16 +03:00
"code.gitea.io/gitea/services/auth"
2019-08-23 19:40:30 +03:00
2021-01-26 18:36:53 +03:00
"gitea.com/go-chi/cache"
"gitea.com/go-chi/session"
"github.com/go-chi/chi"
2019-08-23 19:40:30 +03:00
"github.com/unknwon/com"
2021-01-26 18:36:53 +03:00
"github.com/unknwon/i18n"
"github.com/unrolled/render"
"golang.org/x/crypto/pbkdf2"
2014-03-15 15:01:50 +04:00
)
2021-01-26 18:36:53 +03: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 17:17:16 +04:00
// Context represents context of a request.
2014-03-15 15:01:50 +04:00
type Context struct {
2021-01-26 18:36:53 +03:00
Resp ResponseWriter
Req * http . Request
Data map [ string ] interface { }
Render Render
translation . Locale
2014-08-01 01:25:34 +04:00
Cache cache . Cache
2021-01-26 18:36:53 +03:00
csrf CSRF
2021-01-30 11:55:53 +03:00
Flash * middleware . Flash
2014-07-26 08:24:27 +04:00
Session session . Store
2017-06-26 04:06:40 +03:00
Link string // current request URL
2017-11-28 12:43:51 +03:00
EscapedLink string
2014-11-18 19:07:16 +03:00
User * models . User
IsSigned bool
IsBasicAuth bool
2014-03-15 20:03:23 +04:00
2016-03-11 19:56:52 +03:00
Repo * Repository
2016-03-14 00:37:44 +03:00
Org * Organization
2014-03-15 15:01:50 +04:00
}
2021-01-05 16:05:40 +03:00
// GetData returns the data
func ( ctx * Context ) GetData ( ) map [ string ] interface { } {
return ctx . Data
}
2019-04-08 01:49:34 +03: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 18:23:05 +03: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 09:51:01 +03:00
// HasAPIError returns true if error occurs in form validation.
func ( ctx * Context ) HasAPIError ( ) bool {
2014-05-05 21:08:01 +04:00
hasErr , ok := ctx . Data [ "HasError" ]
if ! ok {
return false
}
return hasErr . ( bool )
}
2016-11-25 09:51:01 +03:00
// GetErrMsg returns error message
2014-05-05 21:08:01 +04:00
func ( ctx * Context ) GetErrMsg ( ) string {
return ctx . Data [ "ErrorMsg" ] . ( string )
}
2014-03-15 18: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-14 02:12:07 +04:00
ctx . Flash . ErrorMsg = ctx . Data [ "ErrorMsg" ] . ( string )
ctx . Data [ "Flash" ] = ctx . Flash
2014-03-15 18:52:14 +04:00
return hasErr . ( bool )
}
2015-07-08 14:47:56 +03: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-16 00:13:34 +03: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-10 00:34:25 +03:00
if err != nil || ( ( u . Scheme != "" || u . Host != "" ) && ! strings . HasPrefix ( strings . ToLower ( loc ) , strings . ToLower ( setting . AppURL ) ) ) {
2018-03-16 00:13:34 +03:00
continue
}
ctx . Redirect ( loc )
return
}
ctx . Redirect ( setting . AppSubURL + "/" )
}
2014-08-02 21:47:33 +04:00
// HTML calls Context.HTML and converts template name to string.
2014-07-26 08:24:27 +04:00
func ( ctx * Context ) HTML ( status int , name base . TplName ) {
2015-12-20 09:06:54 +03:00
log . Debug ( "Template: %s" , name )
2021-01-29 07:33:47 +03:00
var startTime = time . Now ( )
ctx . Data [ "TmplLoadTimes" ] = func ( ) string {
return fmt . Sprint ( time . Since ( startTime ) . Nanoseconds ( ) / 1e6 ) + "ms"
}
2021-01-26 18:36:53 +03:00
if err := ctx . Render . HTML ( ctx . Resp , status , string ( name ) , ctx . Data ) ; err != nil {
2021-02-20 07:26:57 +03: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 18:36:53 +03: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 07:33:47 +03:00
var startTime = time . Now ( )
ctx . Data [ "TmplLoadTimes" ] = func ( ) string {
return fmt . Sprint ( time . Since ( startTime ) . Nanoseconds ( ) / 1e6 ) + "ms"
}
2021-01-26 18:36:53 +03:00
err := ctx . Render . HTML ( & buf , 200 , string ( name ) , data )
return buf . String ( ) , err
2014-03-20 15:50:26 +04:00
}
2014-03-15 18:52:14 +04:00
// RenderWithErr used for page has form validation but need to prompt error to users.
2014-07-26 08:24:27 +04:00
func ( ctx * Context ) RenderWithErr ( msg string , tpl base . TplName , form interface { } ) {
2014-04-03 23:50:55 +04:00
if form != nil {
2021-01-30 11:55:53 +03:00
middleware . AssignForm ( form , ctx . Data )
2014-04-03 23:50:55 +04:00
}
2014-04-11 00:36:50 +04:00
ctx . Flash . ErrorMsg = msg
ctx . Data [ "Flash" ] = ctx . Flash
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tpl )
2014-03-15 18:52:14 +04:00
}
2018-01-11 00:34:17 +03: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 21:10:42 +03:00
ctx . notFoundInternal ( title , err )
}
func ( ctx * Context ) notFoundInternal ( title string , err error ) {
2014-05-02 02:53:41 +04:00
if err != nil {
2019-04-09 21:10:42 +03:00
log . ErrorWithSkip ( 2 , "%s: %v" , title , err )
2021-01-26 18:36:53 +03:00
if ! setting . IsProd ( ) {
2014-05-02 02:53:41 +04:00
ctx . Data [ "ErrorMsg" ] = err
}
2014-03-19 12:48:45 +04:00
}
2021-04-01 18:11:42 +03: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-20 02:09:47 +03:00
ctx . Data [ "IsRepo" ] = ctx . Repo . Repository != nil
2018-01-11 00:34:17 +03: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 21:10:42 +03:00
ctx . serverErrorInternal ( title , err )
}
func ( ctx * Context ) serverErrorInternal ( title string , err error ) {
2018-01-11 00:34:17 +03:00
if err != nil {
2019-04-09 21:10:42 +03:00
log . ErrorWithSkip ( 2 , "%s: %v" , title , err )
2021-01-26 18:36:53 +03:00
if ! setting . IsProd ( ) {
2018-01-11 00:34:17 +03:00
ctx . Data [ "ErrorMsg" ] = err
}
2014-05-02 02:53:41 +04:00
}
2018-01-11 00:34:17 +03:00
ctx . Data [ "Title" ] = "Internal Server Error"
2019-01-31 01:00:00 +03:00
ctx . HTML ( http . StatusInternalServerError , base . TplName ( "status/500" ) )
2014-03-15 15:01:50 +04:00
}
2016-08-30 12:08:38 +03: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-25 21:48:17 +03:00
if errck ( err ) {
2019-04-09 21:10:42 +03:00
ctx . notFoundInternal ( title , err )
2016-07-25 21:48:17 +03:00
return
}
2019-04-09 21:10:42 +03:00
ctx . serverErrorInternal ( title , err )
2016-07-25 21:48:17 +03:00
}
2021-01-26 18:36:53 +03:00
// Header returns a header
func ( ctx * Context ) Header ( ) http . Header {
return ctx . Resp . Header ( )
}
2016-11-25 09:51:01 +03:00
// HandleText handles HTTP status code
2015-03-28 17:30:05 +03:00
func ( ctx * Context ) HandleText ( status int , title string ) {
2015-07-08 14:47:56 +03:00
if ( status / 100 == 4 ) || ( status / 100 == 5 ) {
2019-04-02 10:48:31 +03:00
log . Error ( "%s" , title )
2015-03-28 17:30:05 +03:00
}
2015-10-16 04:28:12 +03:00
ctx . PlainText ( status , [ ] byte ( title ) )
2015-03-28 17:30:05 +03:00
}
2016-11-25 09:51:01 +03:00
// ServeContent serves content to http request
2014-04-15 20:27:29 +04: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 08: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 20:18:18 +03:00
ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" , "Content-Disposition" )
2021-01-26 18:36:53 +03: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 16:42:47 +03:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , "text/plain;charset=utf-8" )
2021-01-26 18:36:53 +03: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 )
}
2021-06-24 00:12:38 +03:00
// ServeStream serves file via io stream
func ( ctx * Context ) ServeStream ( rd io . Reader , name string ) {
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" )
_ , err := io . Copy ( ctx . Resp , rd )
if err != nil {
ctx . ServerError ( "Download file failed" , err )
}
}
2021-01-26 18:36:53 +03:00
// 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 16:42:47 +03:00
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/json;charset=utf-8" )
2021-01-28 00:46:22 +03:00
ctx . Resp . WriteHeader ( status )
2021-01-26 18:36:53 +03: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 11:12:43 +03: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 18:36:53 +03:00
}
// GetCookie returns given cookie value from request header.
func ( ctx * Context ) GetCookie ( name string ) string {
2021-01-30 11:55:53 +03:00
return middleware . GetCookie ( ctx . Req , name )
2021-01-26 18:36:53 +03: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 11:12:43 +03: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 18:36:53 +03: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 11:12:43 +03: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 18:36:53 +03: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 11:12:43 +03:00
return hex . EncodeToString ( text )
2021-01-26 18:36:53 +03: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 ) {
2021-05-31 09:18:11 +03:00
chiCtx := chi . RouteContext ( ctx )
2021-01-26 18:36:53 +03:00
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 )
}
2021-05-31 09:18:11 +03:00
// Deadline is part of the interface for context.Context and we pass this to the request context
func ( ctx * Context ) Deadline ( ) ( deadline time . Time , ok bool ) {
return ctx . Req . Context ( ) . Deadline ( )
}
// Done is part of the interface for context.Context and we pass this to the request context
func ( ctx * Context ) Done ( ) <- chan struct { } {
return ctx . Req . Context ( ) . Done ( )
}
// Err is part of the interface for context.Context and we pass this to the request context
func ( ctx * Context ) Err ( ) error {
return ctx . Req . Context ( ) . Err ( )
}
// Value is part of the interface for context.Context and we pass this to the request context
func ( ctx * Context ) Value ( key interface { } ) interface { } {
return ctx . Req . Context ( ) . Value ( key )
}
2021-01-26 18:36:53 +03:00
// 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-09-28 16:13:04 +03:00
// GetContextUser returns context user
func GetContextUser ( req * http . Request ) * models . User {
if apiContext , ok := req . Context ( ) . Value ( apiContextKey ) . ( * APIContext ) ; ok {
return apiContext . User
}
if ctx , ok := req . Context ( ) . Value ( contextKey ) . ( * Context ) ; ok {
return ctx . User
}
return nil
}
2021-01-27 20:46:35 +03:00
// SignedUserName returns signed user's name via context
func SignedUserName ( req * http . Request ) string {
2021-01-30 11:55:53 +03:00
if middleware . IsInternalPath ( req ) {
2021-01-27 20:46:35 +03:00
return ""
}
2021-01-30 11:55:53 +03:00
if middleware . IsAPIPath ( req ) {
2021-01-27 20:46:35 +03: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 18:36:53 +03: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 11:12:43 +03:00
SameSite : setting . SessionConfig . SameSite ,
2021-01-26 18:36:53 +03:00
}
2014-04-10 22:37:43 +04:00
}
2021-06-09 20:53:16 +03:00
// Auth converts auth.Auth as a middleware
2021-07-24 13:16:34 +03:00
func Auth ( authMethod auth . Method ) func ( * Context ) {
2021-06-09 20:53:16 +03:00
return func ( ctx * Context ) {
ctx . User = authMethod . Verify ( ctx . Req , ctx . Resp , ctx , ctx . Session )
if ctx . User != nil {
ctx . IsBasicAuth = ctx . Data [ "AuthedMethod" ] . ( string ) == new ( auth . Basic ) . Name ( )
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" ] = ""
// ensure the session uid is deleted
_ = ctx . Session . Delete ( "uid" )
}
}
}
2014-07-26 08:24:27 +04:00
// Contexter initializes a classic context for a request.
2021-01-26 18:36:53 +03:00
func Contexter ( ) func ( next http . Handler ) http . Handler {
2021-01-27 17:56:54 +03:00
var rnd = templates . HTMLRenderer ( )
2021-01-26 18:36:53 +03:00
var csrfOpts = getCsrfOpts ( )
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
2021-01-30 11:55:53 +03:00
var locale = middleware . Locale ( resp , req )
2021-01-26 18:36:53 +03:00
var startTime = time . Now ( )
var link = setting . AppSubURL + strings . TrimSuffix ( req . URL . EscapedPath ( ) , "/" )
var ctx = Context {
Resp : NewResponse ( resp ) ,
2021-01-27 17:56:54 +03:00
Cache : mc . GetCache ( ) ,
2021-01-26 18:36:53 +03: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 07:33:47 +03:00
"Link" : link ,
2021-01-26 18:36:53 +03: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 11:55:53 +03:00
f := & middleware . Flash {
2021-01-26 18:36:53 +03: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 11:55:53 +03:00
f := & middleware . Flash {
2021-01-26 18:36:53 +03: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 11:55:53 +03:00
middleware . SetCookie ( resp , "macaron_flash" , flash , 0 ,
2021-01-27 17:56:54 +03:00
setting . SessionConfig . CookiePath ,
2021-01-30 11:55:53 +03:00
middleware . Domain ( setting . SessionConfig . Domain ) ,
middleware . HTTPOnly ( true ) ,
middleware . Secure ( setting . SessionConfig . Secure ) ,
2021-03-07 11:12:43 +03:00
middleware . SameSite ( setting . SessionConfig . SameSite ) ,
2021-01-27 17:56:54 +03:00
)
return
2021-01-26 18:36:53 +03:00
}
2021-03-07 11:12:43 +03:00
middleware . SetCookie ( ctx . Resp , "macaron_flash" , "" , - 1 ,
2021-01-26 18:36:53 +03:00
setting . SessionConfig . CookiePath ,
2021-01-30 11:55:53 +03:00
middleware . Domain ( setting . SessionConfig . Domain ) ,
middleware . HTTPOnly ( true ) ,
middleware . Secure ( setting . SessionConfig . Secure ) ,
2021-03-07 11:12:43 +03:00
middleware . SameSite ( setting . SessionConfig . SameSite ) ,
2021-01-26 18:36:53 +03: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 05:23:27 +03:00
2021-08-06 23:47:10 +03:00
ctx . Resp . Header ( ) . Set ( ` X-Frame-Options ` , setting . CORSConfig . XFrameOptions )
2021-01-26 18:36:53 +03:00
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" ] )
2021-05-05 00:48:31 +03:00
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
2021-01-26 18:36:53 +03:00
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
2021-04-15 19:53:57 +03:00
ctx . Data [ "DisableStars" ] = setting . Repository . DisableStars
2021-01-26 18:36:53 +03:00
ctx . Data [ "ManifestData" ] = setting . ManifestData
2021-05-05 00:48:31 +03:00
ctx . Data [ "UnitWikiGlobalDisabled" ] = models . UnitTypeWiki . UnitGlobalDisabled ( )
ctx . Data [ "UnitIssuesGlobalDisabled" ] = models . UnitTypeIssues . UnitGlobalDisabled ( )
ctx . Data [ "UnitPullsGlobalDisabled" ] = models . UnitTypePullRequests . UnitGlobalDisabled ( )
ctx . Data [ "UnitProjectsGlobalDisabled" ] = models . UnitTypeProjects . UnitGlobalDisabled ( )
2021-01-26 18:36:53 +03:00
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 14:13:50 +03:00
2021-01-26 18:36:53 +03:00
next . ServeHTTP ( ctx . Resp , ctx . Req )
2021-08-04 20:26:30 +03:00
// Handle adding signedUserName to the context for the AccessLogger
usernameInterface := ctx . Data [ "SignedUserName" ]
identityPtrInterface := ctx . Req . Context ( ) . Value ( signedUserNameStringPointerKey )
if usernameInterface != nil && identityPtrInterface != nil {
username := usernameInterface . ( string )
identityPtr := identityPtrInterface . ( * string )
if identityPtr != nil && username != "" {
* identityPtr = username
}
}
2021-01-26 18:36:53 +03:00
} )
2014-03-15 15:01:50 +04:00
}
}