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:
parent
b83114f140
commit
be666b03ee
17 changed files with 418 additions and 12 deletions
|
@ -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 a new issue