@ -48,68 +48,75 @@ type HTTPContext struct {
AuthUser * models . User
AuthUser * models . User
}
}
// askCredentials responses HTTP header and status which informs client to provide credentials.
func askCredentials ( c * context . Context , status int , text string ) {
c . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=\".\"" )
c . HandleText ( status , text )
}
func HTTPContexter ( ) macaron . Handler {
func HTTPContexter ( ) macaron . Handler {
return func ( ctx * context . Context ) {
return func ( c * context . Context ) {
ownerName := ctx . Params ( ":username" )
ownerName := c . Params ( ":username" )
repoName := strings . TrimSuffix ( ctx . Params ( ":reponame" ) , ".git" )
repoName := strings . TrimSuffix ( c . Params ( ":reponame" ) , ".git" )
repoName = strings . TrimSuffix ( repoName , ".wiki" )
repoName = strings . TrimSuffix ( repoName , ".wiki" )
isPull := ctx . Query ( "service" ) == "git-upload-pack" ||
isPull := c . Query ( "service" ) == "git-upload-pack" ||
strings . HasSuffix ( ctx . Req . URL . Path , "git-upload-pack" ) ||
strings . HasSuffix ( c . Req . URL . Path , "git-upload-pack" ) ||
ctx . Req . Method == "GET"
c . Req . Method == "GET"
owner , err := models . GetUserByName ( ownerName )
owner , err := models . GetUserByName ( ownerName )
if err != nil {
if err != nil {
ctx . NotFoundOrServerError ( "GetUserByName" , errors . IsUserNotExist , err )
c . NotFoundOrServerError ( "GetUserByName" , errors . IsUserNotExist , err )
return
return
}
}
repo , err := models . GetRepositoryByName ( owner . ID , repoName )
repo , err := models . GetRepositoryByName ( owner . ID , repoName )
if err != nil {
if err != nil {
ctx . NotFoundOrServerError ( "GetRepositoryByName" , errors . IsRepoNotExist , err )
c . NotFoundOrServerError ( "GetRepositoryByName" , errors . IsRepoNotExist , err )
return
return
}
}
// Authentication is not required for pulling from public repositories.
// Authentication is not required for pulling from public repositories.
if isPull && ! repo . IsPrivate && ! setting . Service . RequireSignInView {
if isPull && ! repo . IsPrivate && ! setting . Service . RequireSignInView {
ctx . Map ( & HTTPContext {
c . Map ( & HTTPContext {
Context : ctx ,
Context : c ,
} )
} )
return
return
}
}
// In case user requested a wrong URL and not intended to access Git objects.
// In case user requested a wrong URL and not intended to access Git objects.
action := ctx . Params ( "*" )
action := c . Params ( "*" )
if ! strings . Contains ( action , "git-" ) &&
if ! strings . Contains ( action , "git-" ) &&
! strings . Contains ( action , "info/" ) &&
! strings . Contains ( action , "info/" ) &&
! strings . Contains ( action , "HEAD" ) &&
! strings . Contains ( action , "HEAD" ) &&
! strings . Contains ( action , "objects/" ) {
! strings . Contains ( action , "objects/" ) {
ctx . NotFound ( )
c . NotFound ( )
return
return
}
}
// Handle HTTP Basic Authentication
// Handle HTTP Basic Authentication
authHead := ctx . Req . Header . Get ( "Authorization" )
authHead := c . Req . Header . Get ( "Authorization" )
if len ( authHead ) == 0 {
if len ( authHead ) == 0 {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=\".\"" )
askCredentials ( c , http . StatusUnauthorized , "" )
ctx . Error ( http . StatusUnauthorized )
return
return
}
}
auths := strings . Fields ( authHead )
auths := strings . Fields ( authHead )
if len ( auths ) != 2 || auths [ 0 ] != "Basic" {
if len ( auths ) != 2 || auths [ 0 ] != "Basic" {
ctx . Error ( http . StatusUnauthorized )
askCredentials ( c , http . StatusUnauthorized , "" )
return
return
}
}
authUsername , authPassword , err := base . BasicAuthDecode ( auths [ 1 ] )
authUsername , authPassword , err := base . BasicAuthDecode ( auths [ 1 ] )
if err != nil {
if err != nil {
ctx . Error ( http . StatusUnauthorized )
askCredentials ( c , http . StatusUnauthorized , "" )
return
return
}
}
fmt . Println ( authUsername , authPassword )
authUser , err := models . UserSignIn ( authUsername , authPassword )
authUser , err := models . UserSignIn ( authUsername , authPassword )
if err != nil && ! errors . IsUserNotExist ( err ) {
if err != nil && ! errors . IsUserNotExist ( err ) {
ctx . Handle ( http . StatusInternalServerError , "UserSignIn" , err )
c . Handle ( http . StatusInternalServerError , "UserSignIn" , err )
return
return
}
}
@ -118,9 +125,9 @@ func HTTPContexter() macaron.Handler {
token , err := models . GetAccessTokenBySHA ( authUsername )
token , err := models . GetAccessTokenBySHA ( authUsername )
if err != nil {
if err != nil {
if models . IsErrAccessTokenEmpty ( err ) || models . IsErrAccessTokenNotExist ( err ) {
if models . IsErrAccessTokenEmpty ( err ) || models . IsErrAccessTokenNotExist ( err ) {
ctx . Error ( http . StatusUnauthorized )
askCredentials ( c , http . StatusUnauthorized , "" )
} else {
} else {
ctx . Handle ( http . StatusInternalServerError , "GetAccessTokenBySHA" , err )
c . Handle ( http . StatusInternalServerError , "GetAccessTokenBySHA" , err )
}
}
return
return
}
}
@ -130,31 +137,33 @@ func HTTPContexter() macaron.Handler {
if err != nil {
if err != nil {
// Once we found token, we're supposed to find its related user,
// Once we found token, we're supposed to find its related user,
// thus any error is unexpected.
// thus any error is unexpected.
ctx . Handle ( http . StatusInternalServerError , "GetUserByID" , err )
c . Handle ( http . StatusInternalServerError , "GetUserByID" , err )
return
return
}
}
}
}
log . Trace ( "HTTPGit - Authenticated user: %s" , authUser . Name )
mode := models . ACCESS_MODE_WRITE
mode := models . ACCESS_MODE_WRITE
if isPull {
if isPull {
mode = models . ACCESS_MODE_READ
mode = models . ACCESS_MODE_READ
}
}
has , err := models . HasAccess ( authUser . ID , repo , mode )
has , err := models . HasAccess ( authUser . ID , repo , mode )
if err != nil {
if err != nil {
ctx . Handle ( http . StatusInternalServerError , "HasAccess" , err )
c . Handle ( http . StatusInternalServerError , "HasAccess" , err )
return
return
} else if ! has {
} else if ! has {
ctx . HandleText ( http . StatusForbidden , "User permission denied" )
askCredentials ( c , http . StatusUnauthorized , "User permission denied" )
return
return
}
}
if ! isPull && repo . IsMirror {
if ! isPull && repo . IsMirror {
ctx . HandleText ( http . StatusForbidden , "Mirror repository is read-only" )
c . HandleText ( http . StatusForbidden , "Mirror repository is read-only" )
return
return
}
}
ctx . Map ( & HTTPContext {
c . Map ( & HTTPContext {
Context : ctx ,
Context : c ,
OwnerName : ownerName ,
OwnerName : ownerName ,
OwnerSalt : owner . Salt ,
OwnerSalt : owner . Salt ,
RepoID : repo . ID ,
RepoID : repo . ID ,