2014-03-15 15:01:50 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package middleware
import (
2014-03-23 00:40:09 +04:00
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
2014-03-15 17:17:16 +04:00
"fmt"
2014-03-22 21:44:02 +04:00
"html/template"
2014-03-15 15:01:50 +04:00
"net/http"
2014-03-23 00:40:09 +04:00
"strconv"
"strings"
2014-03-19 17:57:55 +04:00
"time"
2014-03-15 15:01:50 +04:00
2014-03-30 20:11:28 +04:00
"github.com/go-martini/martini"
2014-03-15 15:01:50 +04:00
2014-03-21 17:06:47 +04:00
"github.com/gogits/cache"
2014-03-30 06:09:59 +04:00
"github.com/gogits/git"
2014-03-22 16:49:53 +04:00
"github.com/gogits/session"
2014-03-21 17:06:47 +04:00
2014-03-15 15:01:50 +04:00
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
2014-03-21 17:31:47 +04:00
"github.com/gogits/gogs/modules/base"
2014-03-15 15:01:50 +04:00
"github.com/gogits/gogs/modules/log"
)
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 {
2014-03-19 17:57:55 +04:00
* Render
2014-03-15 15:01:50 +04:00
c martini . Context
p martini . Params
Req * http . Request
Res http . ResponseWriter
2014-03-22 16:49:53 +04:00
Session session . SessionStore
2014-03-21 17:06:47 +04:00
Cache cache . Cache
2014-03-15 15:01:50 +04:00
User * models . User
IsSigned bool
2014-03-15 20:03:23 +04:00
2014-03-22 21:44:02 +04:00
csrfToken string
2014-03-15 20:03:23 +04:00
Repo struct {
IsOwner bool
2014-03-20 07:48:30 +04:00
IsWatching bool
2014-03-30 06:09:59 +04:00
IsBranch bool
IsTag bool
IsCommit bool
2014-03-15 20:03:23 +04:00
Repository * models . Repository
2014-03-15 20:14:26 +04:00
Owner * models . User
2014-03-30 06:09:59 +04:00
Commit * git . Commit
GitRepo * git . Repository
BranchName string
CommitId string
RepoLink string
2014-03-20 08:12:33 +04:00
CloneLink struct {
SSH string
HTTPS string
Git string
}
2014-03-15 20:03:23 +04:00
}
2014-03-15 15:01:50 +04:00
}
2014-03-15 17:17:16 +04:00
// Query querys form parameter.
2014-03-15 15:01:50 +04:00
func ( ctx * Context ) Query ( name string ) string {
ctx . Req . ParseForm ( )
return ctx . Req . Form . Get ( name )
}
// func (ctx *Context) Param(name string) string {
// return ctx.p[name]
// }
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
}
return hasErr . ( bool )
}
2014-03-20 15:50:26 +04:00
// HTML calls render.HTML underlying but reduce one argument.
func ( ctx * Context ) HTML ( status int , name string , htmlOpt ... HTMLOptions ) {
ctx . Render . HTML ( status , name , ctx . Data , htmlOpt ... )
}
2014-03-15 18:52:14 +04:00
// RenderWithErr used for page has form validation but need to prompt error to users.
func ( ctx * Context ) RenderWithErr ( msg , tpl string , form auth . Form ) {
ctx . Data [ "HasError" ] = true
ctx . Data [ "ErrorMsg" ] = msg
2014-04-03 23:50:55 +04:00
if form != nil {
auth . AssignForm ( form , ctx . Data )
}
2014-03-20 15:50:26 +04:00
ctx . HTML ( 200 , tpl )
2014-03-15 18:52:14 +04:00
}
2014-03-15 17:17:16 +04:00
// Handle handles and logs error by given status.
func ( ctx * Context ) Handle ( status int , title string , err error ) {
log . Error ( "%s: %v" , title , err )
2014-03-19 12:48:45 +04:00
if martini . Dev == martini . Prod {
2014-03-20 15:50:26 +04:00
ctx . HTML ( 500 , "status/500" )
2014-03-19 12:48:45 +04:00
return
}
ctx . Data [ "ErrorMsg" ] = err
2014-03-20 15:50:26 +04:00
ctx . HTML ( status , fmt . Sprintf ( "status/%d" , status ) )
2014-03-15 15:01:50 +04:00
}
2014-03-30 06:09:59 +04:00
func ( ctx * Context ) Debug ( msg string , args ... interface { } ) {
log . Debug ( msg , args ... )
}
2014-03-22 21:44:02 +04:00
func ( ctx * Context ) GetCookie ( name string ) string {
cookie , err := ctx . Req . Cookie ( name )
if err != nil {
return ""
}
return cookie . Value
}
func ( ctx * Context ) SetCookie ( name string , value string , others ... interface { } ) {
cookie := http . Cookie { }
cookie . Name = name
cookie . Value = value
if len ( others ) > 0 {
switch v := others [ 0 ] . ( type ) {
case int :
cookie . MaxAge = v
case int64 :
cookie . MaxAge = int ( v )
case int32 :
cookie . MaxAge = int ( v )
}
}
// default "/"
if len ( others ) > 1 {
if v , ok := others [ 1 ] . ( string ) ; ok && len ( v ) > 0 {
cookie . Path = v
}
} else {
cookie . Path = "/"
}
// default empty
if len ( others ) > 2 {
if v , ok := others [ 2 ] . ( string ) ; ok && len ( v ) > 0 {
cookie . Domain = v
}
}
// default empty
if len ( others ) > 3 {
switch v := others [ 3 ] . ( type ) {
case bool :
cookie . Secure = v
default :
if others [ 3 ] != nil {
cookie . Secure = true
}
}
}
// default false. for session cookie default true
if len ( others ) > 4 {
if v , ok := others [ 4 ] . ( bool ) ; ok && v {
cookie . HttpOnly = true
}
}
ctx . Res . Header ( ) . Add ( "Set-Cookie" , cookie . String ( ) )
}
2014-03-23 00:40:09 +04:00
// Get secure cookie from request by a given key.
func ( ctx * Context ) GetSecureCookie ( Secret , key string ) ( string , bool ) {
val := ctx . GetCookie ( key )
if val == "" {
return "" , false
}
parts := strings . SplitN ( val , "|" , 3 )
if len ( parts ) != 3 {
return "" , false
}
vs := parts [ 0 ]
timestamp := parts [ 1 ]
sig := parts [ 2 ]
h := hmac . New ( sha1 . New , [ ] byte ( Secret ) )
fmt . Fprintf ( h , "%s%s" , vs , timestamp )
if fmt . Sprintf ( "%02x" , h . Sum ( nil ) ) != sig {
return "" , false
}
res , _ := base64 . URLEncoding . DecodeString ( vs )
return string ( res ) , true
}
// Set Secure cookie for response.
func ( ctx * Context ) SetSecureCookie ( Secret , name , value string , others ... interface { } ) {
vs := base64 . URLEncoding . EncodeToString ( [ ] byte ( value ) )
timestamp := strconv . FormatInt ( time . Now ( ) . UnixNano ( ) , 10 )
h := hmac . New ( sha1 . New , [ ] byte ( Secret ) )
fmt . Fprintf ( h , "%s%s" , vs , timestamp )
sig := fmt . Sprintf ( "%02x" , h . Sum ( nil ) )
cookie := strings . Join ( [ ] string { vs , timestamp , sig } , "|" )
ctx . SetCookie ( name , cookie , others ... )
}
2014-03-22 21:44:02 +04:00
func ( ctx * Context ) CsrfToken ( ) string {
if len ( ctx . csrfToken ) > 0 {
return ctx . csrfToken
}
token := ctx . GetCookie ( "_csrf" )
if len ( token ) == 0 {
token = base . GetRandomString ( 30 )
ctx . SetCookie ( "_csrf" , token )
}
ctx . csrfToken = token
return token
}
func ( ctx * Context ) CsrfTokenValid ( ) bool {
token := ctx . Query ( "_csrf" )
if token == "" {
token = ctx . Req . Header . Get ( "X-Csrf-Token" )
}
if token == "" {
return false
} else if ctx . csrfToken != token {
return false
}
return true
}
2014-03-15 17:17:16 +04:00
// InitContext initializes a classic context for a request.
2014-03-15 15:01:50 +04:00
func InitContext ( ) martini . Handler {
2014-03-22 16:49:53 +04:00
return func ( res http . ResponseWriter , r * http . Request , c martini . Context , rd * Render ) {
2014-03-15 15:01:50 +04:00
ctx := & Context {
c : c ,
// p: p,
2014-03-22 16:49:53 +04:00
Req : r ,
Res : res ,
Cache : base . Cache ,
Render : rd ,
2014-03-15 15:01:50 +04:00
}
2014-03-22 21:44:02 +04:00
ctx . Data [ "PageStartTime" ] = time . Now ( )
2014-03-22 16:49:53 +04:00
// start session
ctx . Session = base . SessionManager . SessionStart ( res , r )
2014-03-22 21:44:02 +04:00
rw := res . ( martini . ResponseWriter )
rw . Before ( func ( martini . ResponseWriter ) {
2014-03-22 16:49:53 +04:00
ctx . Session . SessionRelease ( res )
2014-03-22 21:44:02 +04:00
} )
2014-03-22 16:49:53 +04:00
2014-03-15 15:01:50 +04:00
// Get user from session if logined.
2014-03-22 16:49:53 +04:00
user := auth . SignedInUser ( ctx . Session )
2014-03-15 15:01:50 +04:00
ctx . User = user
2014-03-15 16:50:17 +04:00
ctx . IsSigned = user != nil
2014-03-15 15:01:50 +04:00
2014-03-19 17:57:55 +04:00
ctx . Data [ "IsSigned" ] = ctx . IsSigned
2014-03-15 16:50:17 +04:00
if user != nil {
2014-03-19 17:57:55 +04:00
ctx . Data [ "SignedUser" ] = user
ctx . Data [ "SignedUserId" ] = user . Id
2014-03-30 13:03:00 +04:00
ctx . Data [ "SignedUserName" ] = user . Name
2014-03-20 16:02:14 +04:00
ctx . Data [ "IsAdmin" ] = ctx . User . IsAdmin
2014-03-15 16:50:17 +04:00
}
2014-03-15 15:01:50 +04:00
2014-03-22 21:44:02 +04:00
// get or create csrf token
ctx . Data [ "CsrfToken" ] = ctx . CsrfToken ( )
ctx . Data [ "CsrfTokenHtml" ] = template . HTML ( ` <input type="hidden" name="_csrf" value=" ` + ctx . csrfToken + ` "> ` )
2014-03-19 17:57:55 +04:00
2014-03-15 15:01:50 +04:00
c . Map ( ctx )
c . Next ( )
}
}