mirror of https://github.com/gogs/gogs.git
repo/http: fix client is not informed to provide credentials
When Git client has cached credentials for a site, missing response header 'WWW-Authenticate: Basic realm="."' will result in Git client does not prompt user to input credentials again but plain error message and halts push/pull process.pull/4248/merge
parent
d05395fe90
commit
fe25effe7c
|
@ -48,68 +48,75 @@ type HTTPContext struct {
|
|||
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 {
|
||||
return func(ctx *context.Context) {
|
||||
ownerName := ctx.Params(":username")
|
||||
repoName := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
|
||||
return func(c *context.Context) {
|
||||
ownerName := c.Params(":username")
|
||||
repoName := strings.TrimSuffix(c.Params(":reponame"), ".git")
|
||||
repoName = strings.TrimSuffix(repoName, ".wiki")
|
||||
|
||||
isPull := ctx.Query("service") == "git-upload-pack" ||
|
||||
strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") ||
|
||||
ctx.Req.Method == "GET"
|
||||
isPull := c.Query("service") == "git-upload-pack" ||
|
||||
strings.HasSuffix(c.Req.URL.Path, "git-upload-pack") ||
|
||||
c.Req.Method == "GET"
|
||||
|
||||
owner, err := models.GetUserByName(ownerName)
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
|
||||
c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := models.GetRepositoryByName(owner.ID, repoName)
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err)
|
||||
c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Authentication is not required for pulling from public repositories.
|
||||
if isPull && !repo.IsPrivate && !setting.Service.RequireSignInView {
|
||||
ctx.Map(&HTTPContext{
|
||||
Context: ctx,
|
||||
c.Map(&HTTPContext{
|
||||
Context: c,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 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-") &&
|
||||
!strings.Contains(action, "info/") &&
|
||||
!strings.Contains(action, "HEAD") &&
|
||||
!strings.Contains(action, "objects/") {
|
||||
ctx.NotFound()
|
||||
c.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle HTTP Basic Authentication
|
||||
authHead := ctx.Req.Header.Get("Authorization")
|
||||
authHead := c.Req.Header.Get("Authorization")
|
||||
if len(authHead) == 0 {
|
||||
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
|
||||
ctx.Error(http.StatusUnauthorized)
|
||||
askCredentials(c, http.StatusUnauthorized, "")
|
||||
return
|
||||
}
|
||||
|
||||
auths := strings.Fields(authHead)
|
||||
if len(auths) != 2 || auths[0] != "Basic" {
|
||||
ctx.Error(http.StatusUnauthorized)
|
||||
askCredentials(c, http.StatusUnauthorized, "")
|
||||
return
|
||||
}
|
||||
authUsername, authPassword, err := base.BasicAuthDecode(auths[1])
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusUnauthorized)
|
||||
askCredentials(c, http.StatusUnauthorized, "")
|
||||
return
|
||||
}
|
||||
fmt.Println(authUsername, authPassword)
|
||||
|
||||
authUser, err := models.UserSignIn(authUsername, authPassword)
|
||||
if err != nil && !errors.IsUserNotExist(err) {
|
||||
ctx.Handle(http.StatusInternalServerError, "UserSignIn", err)
|
||||
|
||||
c.Handle(http.StatusInternalServerError, "UserSignIn", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -118,9 +125,9 @@ func HTTPContexter() macaron.Handler {
|
|||
token, err := models.GetAccessTokenBySHA(authUsername)
|
||||
if err != nil {
|
||||
if models.IsErrAccessTokenEmpty(err) || models.IsErrAccessTokenNotExist(err) {
|
||||
ctx.Error(http.StatusUnauthorized)
|
||||
askCredentials(c, http.StatusUnauthorized, "")
|
||||
} else {
|
||||
ctx.Handle(http.StatusInternalServerError, "GetAccessTokenBySHA", err)
|
||||
c.Handle(http.StatusInternalServerError, "GetAccessTokenBySHA", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -130,31 +137,33 @@ func HTTPContexter() macaron.Handler {
|
|||
if err != nil {
|
||||
// Once we found token, we're supposed to find its related user,
|
||||
// thus any error is unexpected.
|
||||
ctx.Handle(http.StatusInternalServerError, "GetUserByID", err)
|
||||
c.Handle(http.StatusInternalServerError, "GetUserByID", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace("HTTPGit - Authenticated user: %s", authUser.Name)
|
||||
|
||||
mode := models.ACCESS_MODE_WRITE
|
||||
if isPull {
|
||||
mode = models.ACCESS_MODE_READ
|
||||
}
|
||||
has, err := models.HasAccess(authUser.ID, repo, mode)
|
||||
if err != nil {
|
||||
ctx.Handle(http.StatusInternalServerError, "HasAccess", err)
|
||||
c.Handle(http.StatusInternalServerError, "HasAccess", err)
|
||||
return
|
||||
} else if !has {
|
||||
ctx.HandleText(http.StatusForbidden, "User permission denied")
|
||||
askCredentials(c, http.StatusUnauthorized, "User permission denied")
|
||||
return
|
||||
}
|
||||
|
||||
if !isPull && repo.IsMirror {
|
||||
ctx.HandleText(http.StatusForbidden, "Mirror repository is read-only")
|
||||
c.HandleText(http.StatusForbidden, "Mirror repository is read-only")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Map(&HTTPContext{
|
||||
Context: ctx,
|
||||
c.Map(&HTTPContext{
|
||||
Context: c,
|
||||
OwnerName: ownerName,
|
||||
OwnerSalt: owner.Salt,
|
||||
RepoID: repo.ID,
|
||||
|
|
Loading…
Reference in New Issue