mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Trace Logging on Permission Denied & ColorFormat (#6618)
* Add log.ColorFormat and log.ColorFormatted Structs can now implement log.ColorFormatted to provide their own colored format when logged with `%-v` or additional flags. Signed-off-by: Andrew Thornton <art27@cantab.net> * Add basic ColorFormat to repository and user Signed-off-by: Andrew Thornton <art27@cantab.net> * Add basic ColorFormat to access and unit Signed-off-by: Andrew Thornton <art27@cantab.net> * Add ColorFormat to permission and on trace log it Signed-off-by: Andrew Thornton <art27@cantab.net> * Add log.NewColoredIDValue to make ID value coloring consistent Signed-off-by: Andrew Thornton <art27@cantab.net> * formatting changes * Add some better tracing to permission denied for read issues/pulls Signed-off-by: Andrew Thornton <art27@cantab.net> * Add Trace logging on permission denied Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove isTrace() check from deferred func * Adjust repo and allow logging of team * use FormatInt instead of Itoa * Add blank line Signed-off-by: Andrew Thornton <art27@cantab.net> * Update access.go
This commit is contained in:
		@@ -362,6 +362,17 @@ also set the `resetBytes` to the cached `resetBytes`.
 | 
				
			|||||||
The `colorBytes` and `resetBytes` are not exported to prevent
 | 
					The `colorBytes` and `resetBytes` are not exported to prevent
 | 
				
			||||||
accidental overwriting of internal values.
 | 
					accidental overwriting of internal values.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## ColorFormat & ColorFormatted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Structs may implement the `log.ColorFormatted` interface by implementing the `ColorFormat(fmt.State)` function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If a `log.ColorFormatted` struct is logged with `%-v` format, its `ColorFormat` will be used instead of the usual `%v`. The full `fmt.State` will be passed to allow implementers to look at additional flags.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to help implementers provide `ColorFormat` methods. There is a
 | 
				
			||||||
 | 
					`log.ColorFprintf(...)` function in the log module that will wrap values in `log.ColoredValue` and recognise `%-v`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In general it is recommended not to make the results of this function too verbose to help increase its versatility. Usually this should simply be an `ID`:`Name`. If you wish to make a more verbose result, it is recommended to use `%-+v` as your marker.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Log Spoofing protection
 | 
					## Log Spoofing protection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In order to protect the logs from being spoofed with cleverly
 | 
					In order to protect the logs from being spoofed with cleverly
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,15 @@
 | 
				
			|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
					// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "fmt"
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AccessMode specifies the users access mode
 | 
					// AccessMode specifies the users access mode
 | 
				
			||||||
type AccessMode int
 | 
					type AccessMode int
 | 
				
			||||||
@@ -37,6 +42,13 @@ func (mode AccessMode) String() string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormat provides a ColorFormatted version of this AccessMode
 | 
				
			||||||
 | 
					func (mode AccessMode) ColorFormat(s fmt.State) {
 | 
				
			||||||
 | 
						log.ColorFprintf(s, "%d:%s",
 | 
				
			||||||
 | 
							log.NewColoredIDValue(mode),
 | 
				
			||||||
 | 
							mode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseAccessMode returns corresponding access mode to given permission string.
 | 
					// ParseAccessMode returns corresponding access mode to given permission string.
 | 
				
			||||||
func ParseAccessMode(permission string) AccessMode {
 | 
					func ParseAccessMode(permission string) AccessMode {
 | 
				
			||||||
	switch permission {
 | 
						switch permission {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,16 @@ type Team struct {
 | 
				
			|||||||
	Units       []*TeamUnit `xorm:"-"`
 | 
						Units       []*TeamUnit `xorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormat provides a basic color format for a Team
 | 
				
			||||||
 | 
					func (t *Team) ColorFormat(s fmt.State) {
 | 
				
			||||||
 | 
						log.ColorFprintf(s, "%d:%s (OrgID: %d) %-v",
 | 
				
			||||||
 | 
							log.NewColoredIDValue(t.ID),
 | 
				
			||||||
 | 
							t.Name,
 | 
				
			||||||
 | 
							log.NewColoredIDValue(t.OrgID),
 | 
				
			||||||
 | 
							t.Authorize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetUnits return a list of available units for a team
 | 
					// GetUnits return a list of available units for a team
 | 
				
			||||||
func (t *Team) GetUnits() error {
 | 
					func (t *Team) GetUnits() error {
 | 
				
			||||||
	return t.getUnits(x)
 | 
						return t.getUnits(x)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ import (
 | 
				
			|||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -210,6 +211,24 @@ type Repository struct {
 | 
				
			|||||||
	UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
 | 
						UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormat returns a colored string to represent this repo
 | 
				
			||||||
 | 
					func (repo *Repository) ColorFormat(s fmt.State) {
 | 
				
			||||||
 | 
						var ownerName interface{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if repo.OwnerName != "" {
 | 
				
			||||||
 | 
							ownerName = repo.OwnerName
 | 
				
			||||||
 | 
						} else if repo.Owner != nil {
 | 
				
			||||||
 | 
							ownerName = repo.Owner.Name
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							ownerName = log.NewColoredIDValue(strconv.FormatInt(repo.OwnerID, 10))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.ColorFprintf(s, "%d:%s/%s",
 | 
				
			||||||
 | 
							log.NewColoredIDValue(repo.ID),
 | 
				
			||||||
 | 
							ownerName,
 | 
				
			||||||
 | 
							repo.Name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
 | 
					// AfterLoad is invoked from XORM after setting the values of all fields of this object.
 | 
				
			||||||
func (repo *Repository) AfterLoad() {
 | 
					func (repo *Repository) AfterLoad() {
 | 
				
			||||||
	// FIXME: use models migration to solve all at once.
 | 
						// FIXME: use models migration to solve all at once.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Permission contains all the permissions related variables to a repository for a user
 | 
					// Permission contains all the permissions related variables to a repository for a user
 | 
				
			||||||
type Permission struct {
 | 
					type Permission struct {
 | 
				
			||||||
	AccessMode AccessMode
 | 
						AccessMode AccessMode
 | 
				
			||||||
@@ -90,12 +96,67 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
 | 
				
			|||||||
	return p.CanWrite(UnitTypeIssues)
 | 
						return p.CanWrite(UnitTypeIssues)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormat writes a colored string for these Permissions
 | 
				
			||||||
 | 
					func (p *Permission) ColorFormat(s fmt.State) {
 | 
				
			||||||
 | 
						noColor := log.ColorBytes(log.Reset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						format := "AccessMode: %-v, %d Units, %d UnitsMode(s): [ "
 | 
				
			||||||
 | 
						args := []interface{}{
 | 
				
			||||||
 | 
							p.AccessMode,
 | 
				
			||||||
 | 
							log.NewColoredValueBytes(len(p.Units), &noColor),
 | 
				
			||||||
 | 
							log.NewColoredValueBytes(len(p.UnitsMode), &noColor),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if s.Flag('+') {
 | 
				
			||||||
 | 
							for i, unit := range p.Units {
 | 
				
			||||||
 | 
								config := ""
 | 
				
			||||||
 | 
								if unit.Config != nil {
 | 
				
			||||||
 | 
									configBytes, err := unit.Config.ToDB()
 | 
				
			||||||
 | 
									config = string(configBytes)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										config = string(err.Error())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								format += "\nUnits[%d]: ID: %d RepoID: %d Type: %-v Config: %s"
 | 
				
			||||||
 | 
								args = append(args,
 | 
				
			||||||
 | 
									log.NewColoredValueBytes(i, &noColor),
 | 
				
			||||||
 | 
									log.NewColoredIDValue(unit.ID),
 | 
				
			||||||
 | 
									log.NewColoredIDValue(unit.RepoID),
 | 
				
			||||||
 | 
									unit.Type,
 | 
				
			||||||
 | 
									config)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for key, value := range p.UnitsMode {
 | 
				
			||||||
 | 
								format += "\nUnitMode[%-v]: %-v"
 | 
				
			||||||
 | 
								args = append(args,
 | 
				
			||||||
 | 
									key,
 | 
				
			||||||
 | 
									value)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							format += "..."
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						format += " ]"
 | 
				
			||||||
 | 
						log.ColorFprintf(s, format, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetUserRepoPermission returns the user permissions to the repository
 | 
					// GetUserRepoPermission returns the user permissions to the repository
 | 
				
			||||||
func GetUserRepoPermission(repo *Repository, user *User) (Permission, error) {
 | 
					func GetUserRepoPermission(repo *Repository, user *User) (Permission, error) {
 | 
				
			||||||
	return getUserRepoPermission(x, repo, user)
 | 
						return getUserRepoPermission(x, repo, user)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permission, err error) {
 | 
					func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permission, err error) {
 | 
				
			||||||
 | 
						if log.IsTrace() {
 | 
				
			||||||
 | 
							defer func() {
 | 
				
			||||||
 | 
								if user == nil {
 | 
				
			||||||
 | 
									log.Trace("Permission Loaded for anonymous user in %-v:\nPermissions: %-+v",
 | 
				
			||||||
 | 
										repo,
 | 
				
			||||||
 | 
										perm)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								log.Trace("Permission Loaded for %-v in %-v:\nPermissions: %-+v",
 | 
				
			||||||
 | 
									user,
 | 
				
			||||||
 | 
									repo,
 | 
				
			||||||
 | 
									perm)
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// anonymous user visit private repo.
 | 
						// anonymous user visit private repo.
 | 
				
			||||||
	// TODO: anonymous user visit public unit of private repo???
 | 
						// TODO: anonymous user visit public unit of private repo???
 | 
				
			||||||
	if user == nil && repo.IsPrivate {
 | 
						if user == nil && repo.IsPrivate {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,10 @@
 | 
				
			|||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UnitType is Unit's Type
 | 
					// UnitType is Unit's Type
 | 
				
			||||||
@@ -22,6 +25,33 @@ const (
 | 
				
			|||||||
	UnitTypeExternalTracker                     // 7 ExternalTracker
 | 
						UnitTypeExternalTracker                     // 7 ExternalTracker
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u UnitType) String() string {
 | 
				
			||||||
 | 
						switch u {
 | 
				
			||||||
 | 
						case UnitTypeCode:
 | 
				
			||||||
 | 
							return "UnitTypeCode"
 | 
				
			||||||
 | 
						case UnitTypeIssues:
 | 
				
			||||||
 | 
							return "UnitTypeIssues"
 | 
				
			||||||
 | 
						case UnitTypePullRequests:
 | 
				
			||||||
 | 
							return "UnitTypePullRequests"
 | 
				
			||||||
 | 
						case UnitTypeReleases:
 | 
				
			||||||
 | 
							return "UnitTypeReleases"
 | 
				
			||||||
 | 
						case UnitTypeWiki:
 | 
				
			||||||
 | 
							return "UnitTypeWiki"
 | 
				
			||||||
 | 
						case UnitTypeExternalWiki:
 | 
				
			||||||
 | 
							return "UnitTypeExternalWiki"
 | 
				
			||||||
 | 
						case UnitTypeExternalTracker:
 | 
				
			||||||
 | 
							return "UnitTypeExternalTracker"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf("Unknown UnitType %d", u)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormat provides a ColorFormatted version of this UnitType
 | 
				
			||||||
 | 
					func (u UnitType) ColorFormat(s fmt.State) {
 | 
				
			||||||
 | 
						log.ColorFprintf(s, "%d:%s",
 | 
				
			||||||
 | 
							log.NewColoredIDValue(u),
 | 
				
			||||||
 | 
							u)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	// allRepUnitTypes contains all the unit types
 | 
						// allRepUnitTypes contains all the unit types
 | 
				
			||||||
	allRepUnitTypes = []UnitType{
 | 
						allRepUnitTypes = []UnitType{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -146,6 +146,13 @@ type User struct {
 | 
				
			|||||||
	Theme         string `xorm:"NOT NULL DEFAULT ''"`
 | 
						Theme         string `xorm:"NOT NULL DEFAULT ''"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormat writes a colored string to identify this struct
 | 
				
			||||||
 | 
					func (u *User) ColorFormat(s fmt.State) {
 | 
				
			||||||
 | 
						log.ColorFprintf(s, "%d:%s",
 | 
				
			||||||
 | 
							log.NewColoredIDValue(u.ID),
 | 
				
			||||||
 | 
							log.NewColoredValue(u.Name))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BeforeUpdate is invoked from XORM before updating this object.
 | 
					// BeforeUpdate is invoked from XORM before updating this object.
 | 
				
			||||||
func (u *User) BeforeUpdate() {
 | 
					func (u *User) BeforeUpdate() {
 | 
				
			||||||
	if u.MaxRepoCreation < -1 {
 | 
						if u.MaxRepoCreation < -1 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ package context
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	macaron "gopkg.in/macaron.v1"
 | 
						macaron "gopkg.in/macaron.v1"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,6 +47,22 @@ func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler {
 | 
				
			|||||||
func RequireRepoReader(unitType models.UnitType) macaron.Handler {
 | 
					func RequireRepoReader(unitType models.UnitType) macaron.Handler {
 | 
				
			||||||
	return func(ctx *Context) {
 | 
						return func(ctx *Context) {
 | 
				
			||||||
		if !ctx.Repo.CanRead(unitType) {
 | 
							if !ctx.Repo.CanRead(unitType) {
 | 
				
			||||||
 | 
								if log.IsTrace() {
 | 
				
			||||||
 | 
									if ctx.IsSigned {
 | 
				
			||||||
 | 
										log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
											"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
											ctx.User,
 | 
				
			||||||
 | 
											unitType,
 | 
				
			||||||
 | 
											ctx.Repo.Repository,
 | 
				
			||||||
 | 
											ctx.Repo.Permission)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
											"Anonymous user in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
											unitType,
 | 
				
			||||||
 | 
											ctx.Repo.Repository,
 | 
				
			||||||
 | 
											ctx.Repo.Permission)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			ctx.NotFound(ctx.Req.RequestURI, nil)
 | 
								ctx.NotFound(ctx.Req.RequestURI, nil)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -59,6 +77,25 @@ func RequireRepoReaderOr(unitTypes ...models.UnitType) macaron.Handler {
 | 
				
			|||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								var format string
 | 
				
			||||||
 | 
								var args []interface{}
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									format = "Permission Denied: User %-v cannot read ["
 | 
				
			||||||
 | 
									args = append(args, ctx.User)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									format = "Permission Denied: Anonymous user cannot read ["
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for _, unit := range unitTypes {
 | 
				
			||||||
 | 
									format += "%-v, "
 | 
				
			||||||
 | 
									args = append(args, unit)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								format = format[:len(format)-2] + "] in Repo %-v\n" +
 | 
				
			||||||
 | 
									"User in Repo has Permissions: %-+v"
 | 
				
			||||||
 | 
								args = append(args, ctx.Repo.Repository, ctx.Repo.Permission)
 | 
				
			||||||
 | 
								log.Trace(format, args...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound(ctx.Req.RequestURI, nil)
 | 
							ctx.NotFound(ctx.Req.RequestURI, nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package log
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -195,11 +196,12 @@ normalLoop:
 | 
				
			|||||||
		lasti := i
 | 
							lasti := i
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if c.mode == escapeAll {
 | 
							if c.mode == escapeAll {
 | 
				
			||||||
			for i < end && (bytes[i] >= ' ' || bytes[i] == '\n') {
 | 
								for i < end && (bytes[i] >= ' ' || bytes[i] == '\n' || bytes[i] == '\t') {
 | 
				
			||||||
				i++
 | 
									i++
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			for i < end && bytes[i] >= ' ' {
 | 
								// Allow tabs if we're not escaping everything
 | 
				
			||||||
 | 
								for i < end && (bytes[i] >= ' ' || bytes[i] == '\t') {
 | 
				
			||||||
				i++
 | 
									i++
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -266,6 +268,39 @@ normalLoop:
 | 
				
			|||||||
	return totalWritten, nil
 | 
						return totalWritten, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorSprintf returns a colored string from a format and arguments
 | 
				
			||||||
 | 
					// arguments will be wrapped in ColoredValues to protect against color spoofing
 | 
				
			||||||
 | 
					func ColorSprintf(format string, args ...interface{}) string {
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							v := make([]interface{}, len(args))
 | 
				
			||||||
 | 
							for i := 0; i < len(v); i++ {
 | 
				
			||||||
 | 
								v[i] = NewColoredValuePointer(&args[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Sprintf(format, v...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf(format)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFprintf will write to the provided writer similar to ColorSprintf
 | 
				
			||||||
 | 
					func ColorFprintf(w io.Writer, format string, args ...interface{}) (int, error) {
 | 
				
			||||||
 | 
						if len(args) > 0 {
 | 
				
			||||||
 | 
							v := make([]interface{}, len(args))
 | 
				
			||||||
 | 
							for i := 0; i < len(v); i++ {
 | 
				
			||||||
 | 
								v[i] = NewColoredValuePointer(&args[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Fprintf(w, format, v...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Fprintf(w, format)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ColorFormatted structs provide their own colored string when formatted with ColorSprintf
 | 
				
			||||||
 | 
					type ColorFormatted interface {
 | 
				
			||||||
 | 
						// ColorFormat provides the colored representation of the value
 | 
				
			||||||
 | 
						ColorFormat(s fmt.State)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var colorFormattedType = reflect.TypeOf((*ColorFormatted)(nil)).Elem()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ColoredValue will Color the provided value
 | 
					// ColoredValue will Color the provided value
 | 
				
			||||||
type ColoredValue struct {
 | 
					type ColoredValue struct {
 | 
				
			||||||
	colorBytes *[]byte
 | 
						colorBytes *[]byte
 | 
				
			||||||
@@ -316,8 +351,33 @@ func NewColoredValueBytes(value interface{}, colorBytes *[]byte) *ColoredValue {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Format will format the provided value and protect against ANSI spoofing within the value
 | 
					// NewColoredIDValue is a helper function to create a ColoredValue from a Value
 | 
				
			||||||
 | 
					// The Value will be colored with FgCyan
 | 
				
			||||||
 | 
					// If a ColoredValue is provided it is not changed
 | 
				
			||||||
 | 
					func NewColoredIDValue(value interface{}) *ColoredValue {
 | 
				
			||||||
 | 
						return NewColoredValueBytes(&value, &fgCyanBytes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Format will format the provided value and protect against ANSI color spoofing within the value
 | 
				
			||||||
 | 
					// If the wrapped value is ColorFormatted and the format is "%-v" then its ColorString will
 | 
				
			||||||
 | 
					// be used. It is presumed that this ColorString is safe.
 | 
				
			||||||
func (cv *ColoredValue) Format(s fmt.State, c rune) {
 | 
					func (cv *ColoredValue) Format(s fmt.State, c rune) {
 | 
				
			||||||
 | 
						if c == 'v' && s.Flag('-') {
 | 
				
			||||||
 | 
							if val, ok := (*cv.Value).(ColorFormatted); ok {
 | 
				
			||||||
 | 
								val.ColorFormat(s)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v := reflect.ValueOf(*cv.Value)
 | 
				
			||||||
 | 
							t := v.Type()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if reflect.PtrTo(t).Implements(colorFormattedType) {
 | 
				
			||||||
 | 
								vp := reflect.New(t)
 | 
				
			||||||
 | 
								vp.Elem().Set(v)
 | 
				
			||||||
 | 
								val := vp.Interface().(ColorFormatted)
 | 
				
			||||||
 | 
								val.ColorFormat(s)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	s.Write([]byte(*cv.colorBytes))
 | 
						s.Write([]byte(*cv.colorBytes))
 | 
				
			||||||
	fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value))
 | 
						fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value))
 | 
				
			||||||
	s.Write([]byte(*cv.resetBytes))
 | 
						s.Write([]byte(*cv.resetBytes))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,11 +69,7 @@ func (l *Logger) Log(skip int, level Level, format string, v ...interface{}) err
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	msg := format
 | 
						msg := format
 | 
				
			||||||
	if len(v) > 0 {
 | 
						if len(v) > 0 {
 | 
				
			||||||
		args := make([]interface{}, len(v))
 | 
							msg = ColorSprintf(format, v...)
 | 
				
			||||||
		for i := 0; i < len(args); i++ {
 | 
					 | 
				
			||||||
			args[i] = NewColoredValuePointer(&v[i])
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		msg = fmt.Sprintf(format, args...)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	stack := ""
 | 
						stack := ""
 | 
				
			||||||
	if l.GetStacktraceLevel() <= level {
 | 
						if l.GetStacktraceLevel() <= level {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -369,6 +369,22 @@ func orgAssignment(args ...bool) macaron.Handler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func mustEnableIssues(ctx *context.APIContext) {
 | 
					func mustEnableIssues(ctx *context.APIContext) {
 | 
				
			||||||
	if !ctx.Repo.CanRead(models.UnitTypeIssues) {
 | 
						if !ctx.Repo.CanRead(models.UnitTypeIssues) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										models.UnitTypeIssues,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
										"Anonymous user in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										models.UnitTypeIssues,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound()
 | 
							ctx.NotFound()
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -376,6 +392,22 @@ func mustEnableIssues(ctx *context.APIContext) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func mustAllowPulls(ctx *context.APIContext) {
 | 
					func mustAllowPulls(ctx *context.APIContext) {
 | 
				
			||||||
	if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
 | 
						if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
 | 
				
			||||||
 | 
							if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										models.UnitTypePullRequests,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
										"Anonymous user in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										models.UnitTypePullRequests,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound()
 | 
							ctx.NotFound()
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -384,6 +416,24 @@ func mustAllowPulls(ctx *context.APIContext) {
 | 
				
			|||||||
func mustEnableIssuesOrPulls(ctx *context.APIContext) {
 | 
					func mustEnableIssuesOrPulls(ctx *context.APIContext) {
 | 
				
			||||||
	if !ctx.Repo.CanRead(models.UnitTypeIssues) &&
 | 
						if !ctx.Repo.CanRead(models.UnitTypeIssues) &&
 | 
				
			||||||
		!(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
 | 
							!(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
 | 
				
			||||||
 | 
							if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v cannot read %-v and %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										models.UnitTypeIssues,
 | 
				
			||||||
 | 
										models.UnitTypePullRequests,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Anonymous user cannot read %-v and %-v in Repo %-v\n"+
 | 
				
			||||||
 | 
										"Anonymous user in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										models.UnitTypeIssues,
 | 
				
			||||||
 | 
										models.UnitTypePullRequests,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound()
 | 
							ctx.NotFound()
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -672,7 +672,12 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 | 
				
			|||||||
		return nil, nil, nil, nil, "", ""
 | 
							return nil, nil, nil, nil, "", ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !perm.CanReadIssuesOrPulls(true) {
 | 
						if !perm.CanReadIssuesOrPulls(true) {
 | 
				
			||||||
		log.Trace("ParseCompareInfo[%d]: cannot create/read pull requests", baseRepo.ID)
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								log.Trace("Permission Denied: User %-v cannot create/read pull requests in Repo %-v\nUser in headRepo has Permissions: %-+v",
 | 
				
			||||||
 | 
									ctx.User,
 | 
				
			||||||
 | 
									headRepo,
 | 
				
			||||||
 | 
									perm)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound()
 | 
							ctx.NotFound()
 | 
				
			||||||
		return nil, nil, nil, nil, "", ""
 | 
							return nil, nil, nil, nil, "", ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1163,6 +1163,24 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
 | 
						if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									issueType := "issues"
 | 
				
			||||||
 | 
									if issue.IsPull {
 | 
				
			||||||
 | 
										issueType = "pulls"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										log.NewColoredIDValue(issue.PosterID),
 | 
				
			||||||
 | 
										issueType,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Not logged in")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Error(403)
 | 
							ctx.Error(403)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1353,6 +1371,24 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
 | 
						if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									issueType := "issues"
 | 
				
			||||||
 | 
									if issue.IsPull {
 | 
				
			||||||
 | 
										issueType = "pulls"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										log.NewColoredIDValue(issue.PosterID),
 | 
				
			||||||
 | 
										issueType,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Not logged in")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Error(403)
 | 
							ctx.Error(403)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1432,6 +1468,24 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull)) {
 | 
						if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull)) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									issueType := "issues"
 | 
				
			||||||
 | 
									if comment.Issue.IsPull {
 | 
				
			||||||
 | 
										issueType = "pulls"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										log.NewColoredIDValue(comment.Issue.PosterID),
 | 
				
			||||||
 | 
										issueType,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Not logged in")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Error(403)
 | 
							ctx.Error(403)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	} else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode {
 | 
						} else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueWatch sets issue watching
 | 
					// IssueWatch sets issue watching
 | 
				
			||||||
@@ -21,6 +22,23 @@ func IssueWatch(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
 | 
						if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								if ctx.IsSigned {
 | 
				
			||||||
 | 
									issueType := "issues"
 | 
				
			||||||
 | 
									if issue.IsPull {
 | 
				
			||||||
 | 
										issueType = "pulls"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n"+
 | 
				
			||||||
 | 
										"User in Repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										log.NewColoredIDValue(issue.PosterID),
 | 
				
			||||||
 | 
										issueType,
 | 
				
			||||||
 | 
										ctx.Repo.Repository,
 | 
				
			||||||
 | 
										ctx.Repo.Permission)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: Not logged in")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.Error(403)
 | 
							ctx.Error(403)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,6 +64,18 @@ func getForkRepository(ctx *context.Context) *models.Repository {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if forkRepo.IsEmpty || !perm.CanRead(models.UnitTypeCode) {
 | 
						if forkRepo.IsEmpty || !perm.CanRead(models.UnitTypeCode) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								if forkRepo.IsEmpty {
 | 
				
			||||||
 | 
									log.Trace("Empty fork repository %-v", forkRepo)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v cannot read %-v of forkRepo %-v\n"+
 | 
				
			||||||
 | 
										"User in forkRepo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctx.User,
 | 
				
			||||||
 | 
										models.UnitTypeCode,
 | 
				
			||||||
 | 
										ctx.Repo,
 | 
				
			||||||
 | 
										perm)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound("getForkRepository", nil)
 | 
							ctx.NotFound("getForkRepository", nil)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -704,7 +716,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
 | 
				
			|||||||
		return nil, nil, nil, nil, "", ""
 | 
							return nil, nil, nil, nil, "", ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !perm.CanReadIssuesOrPulls(true) {
 | 
						if !perm.CanReadIssuesOrPulls(true) {
 | 
				
			||||||
		log.Trace("ParseCompareInfo[%d]: cannot create/read pull requests", baseRepo.ID)
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in headRepo has Permissions: %-+v",
 | 
				
			||||||
 | 
									ctx.User,
 | 
				
			||||||
 | 
									headRepo,
 | 
				
			||||||
 | 
									perm)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound("ParseCompareInfo", nil)
 | 
							ctx.NotFound("ParseCompareInfo", nil)
 | 
				
			||||||
		return nil, nil, nil, nil, "", ""
 | 
							return nil, nil, nil, nil, "", ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup/markdown"
 | 
						"code.gitea.io/gitea/modules/markup/markdown"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -32,6 +33,15 @@ const (
 | 
				
			|||||||
func MustEnableWiki(ctx *context.Context) {
 | 
					func MustEnableWiki(ctx *context.Context) {
 | 
				
			||||||
	if !ctx.Repo.CanRead(models.UnitTypeWiki) &&
 | 
						if !ctx.Repo.CanRead(models.UnitTypeWiki) &&
 | 
				
			||||||
		!ctx.Repo.CanRead(models.UnitTypeExternalWiki) {
 | 
							!ctx.Repo.CanRead(models.UnitTypeExternalWiki) {
 | 
				
			||||||
 | 
							if log.IsTrace() {
 | 
				
			||||||
 | 
								log.Trace("Permission Denied: User %-v cannot read %-v or %-v of repo %-v\n"+
 | 
				
			||||||
 | 
									"User in repo has Permissions: %-+v",
 | 
				
			||||||
 | 
									ctx.User,
 | 
				
			||||||
 | 
									models.UnitTypeWiki,
 | 
				
			||||||
 | 
									models.UnitTypeExternalWiki,
 | 
				
			||||||
 | 
									ctx.Repo.Repository,
 | 
				
			||||||
 | 
									ctx.Repo.Permission)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.NotFound("MustEnableWiki", nil)
 | 
							ctx.NotFound("MustEnableWiki", nil)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -308,6 +309,14 @@ func Issues(ctx *context.Context) {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !perm.CanRead(models.UnitTypeIssues) {
 | 
							if !perm.CanRead(models.UnitTypeIssues) {
 | 
				
			||||||
 | 
								if log.IsTrace() {
 | 
				
			||||||
 | 
									log.Trace("Permission Denied: User %-v cannot read %-v of repo %-v\n"+
 | 
				
			||||||
 | 
										"User in repo has Permissions: %-+v",
 | 
				
			||||||
 | 
										ctxUser,
 | 
				
			||||||
 | 
										models.UnitTypeIssues,
 | 
				
			||||||
 | 
										repo,
 | 
				
			||||||
 | 
										perm)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			ctx.Status(404)
 | 
								ctx.Status(404)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user