* 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.gotags/v1.21.12.1
| @@ -362,6 +362,17 @@ also set the `resetBytes` to the cached `resetBytes`. | |||
| The `colorBytes` and `resetBytes` are not exported to prevent | |||
| 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 | |||
| In order to protect the logs from being spoofed with cleverly | |||
| @@ -392,5 +403,5 @@ func newNewoneLogService() { | |||
| } | |||
| ``` | |||
| You should then add `newOneLogService` to `NewServices()` in | |||
| You should then add `newOneLogService` to `NewServices()` in | |||
| `modules/setting/setting.go` | |||
| @@ -1,10 +1,15 @@ | |||
| // 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 | |||
| // license that can be found in the LICENSE file. | |||
| package models | |||
| import "fmt" | |||
| import ( | |||
| "fmt" | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| // AccessMode specifies the users access mode | |||
| 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. | |||
| func ParseAccessMode(permission string) AccessMode { | |||
| switch permission { | |||
| @@ -34,6 +34,16 @@ type Team struct { | |||
| 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 | |||
| func (t *Team) GetUnits() error { | |||
| return t.getUnits(x) | |||
| @@ -18,6 +18,7 @@ import ( | |||
| "path/filepath" | |||
| "regexp" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| @@ -210,6 +211,24 @@ type Repository struct { | |||
| 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. | |||
| func (repo *Repository) AfterLoad() { | |||
| // FIXME: use models migration to solve all at once. | |||
| @@ -4,6 +4,12 @@ | |||
| package models | |||
| import ( | |||
| "fmt" | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| // Permission contains all the permissions related variables to a repository for a user | |||
| type Permission struct { | |||
| AccessMode AccessMode | |||
| @@ -90,12 +96,67 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool { | |||
| 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 | |||
| func GetUserRepoPermission(repo *Repository, user *User) (Permission, error) { | |||
| return getUserRepoPermission(x, repo, user) | |||
| } | |||
| 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. | |||
| // TODO: anonymous user visit public unit of private repo??? | |||
| if user == nil && repo.IsPrivate { | |||
| @@ -5,7 +5,10 @@ | |||
| package models | |||
| import ( | |||
| "fmt" | |||
| "strings" | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| // UnitType is Unit's Type | |||
| @@ -22,6 +25,33 @@ const ( | |||
| 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 ( | |||
| // allRepUnitTypes contains all the unit types | |||
| allRepUnitTypes = []UnitType{ | |||
| @@ -146,6 +146,13 @@ type User struct { | |||
| 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. | |||
| func (u *User) BeforeUpdate() { | |||
| if u.MaxRepoCreation < -1 { | |||
| @@ -6,6 +6,8 @@ package context | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| macaron "gopkg.in/macaron.v1" | |||
| ) | |||
| @@ -45,6 +47,22 @@ func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler { | |||
| func RequireRepoReader(unitType models.UnitType) macaron.Handler { | |||
| return func(ctx *Context) { | |||
| 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) | |||
| return | |||
| } | |||
| @@ -59,6 +77,25 @@ func RequireRepoReaderOr(unitTypes ...models.UnitType) macaron.Handler { | |||
| 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) | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ package log | |||
| import ( | |||
| "fmt" | |||
| "io" | |||
| "reflect" | |||
| "strconv" | |||
| "strings" | |||
| ) | |||
| @@ -195,11 +196,12 @@ normalLoop: | |||
| lasti := i | |||
| if c.mode == escapeAll { | |||
| for i < end && (bytes[i] >= ' ' || bytes[i] == '\n') { | |||
| for i < end && (bytes[i] >= ' ' || bytes[i] == '\n' || bytes[i] == '\t') { | |||
| i++ | |||
| } | |||
| } else { | |||
| for i < end && bytes[i] >= ' ' { | |||
| // Allow tabs if we're not escaping everything | |||
| for i < end && (bytes[i] >= ' ' || bytes[i] == '\t') { | |||
| i++ | |||
| } | |||
| } | |||
| @@ -266,6 +268,39 @@ normalLoop: | |||
| 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 | |||
| type ColoredValue struct { | |||
| 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) { | |||
| 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)) | |||
| fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value)) | |||
| s.Write([]byte(*cv.resetBytes)) | |||
| @@ -69,11 +69,7 @@ func (l *Logger) Log(skip int, level Level, format string, v ...interface{}) err | |||
| } | |||
| msg := format | |||
| if len(v) > 0 { | |||
| args := make([]interface{}, len(v)) | |||
| for i := 0; i < len(args); i++ { | |||
| args[i] = NewColoredValuePointer(&v[i]) | |||
| } | |||
| msg = fmt.Sprintf(format, args...) | |||
| msg = ColorSprintf(format, v...) | |||
| } | |||
| stack := "" | |||
| if l.GetStacktraceLevel() <= level { | |||
| @@ -369,6 +369,22 @@ func orgAssignment(args ...bool) macaron.Handler { | |||
| func mustEnableIssues(ctx *context.APIContext) { | |||
| 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() | |||
| return | |||
| } | |||
| @@ -376,6 +392,22 @@ func mustEnableIssues(ctx *context.APIContext) { | |||
| func mustAllowPulls(ctx *context.APIContext) { | |||
| 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() | |||
| return | |||
| } | |||
| @@ -384,6 +416,24 @@ func mustAllowPulls(ctx *context.APIContext) { | |||
| func mustEnableIssuesOrPulls(ctx *context.APIContext) { | |||
| if !ctx.Repo.CanRead(models.UnitTypeIssues) && | |||
| !(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() | |||
| return | |||
| } | |||
| @@ -672,7 +672,12 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) | |||
| return nil, nil, nil, nil, "", "" | |||
| } | |||
| 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() | |||
| 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 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) | |||
| } | |||
| @@ -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 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) | |||
| 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 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) | |||
| return | |||
| } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode { | |||
| @@ -11,6 +11,7 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| // 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 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) | |||
| return | |||
| } | |||
| @@ -64,6 +64,18 @@ func getForkRepository(ctx *context.Context) *models.Repository { | |||
| } | |||
| 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) | |||
| return nil | |||
| } | |||
| @@ -704,7 +716,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, * | |||
| return nil, nil, nil, nil, "", "" | |||
| } | |||
| 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) | |||
| return nil, nil, nil, nil, "", "" | |||
| } | |||
| @@ -16,6 +16,7 @@ import ( | |||
| "code.gitea.io/gitea/modules/base" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/git" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/markup" | |||
| "code.gitea.io/gitea/modules/markup/markdown" | |||
| "code.gitea.io/gitea/modules/util" | |||
| @@ -32,6 +33,15 @@ const ( | |||
| func MustEnableWiki(ctx *context.Context) { | |||
| if !ctx.Repo.CanRead(models.UnitTypeWiki) && | |||
| !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) | |||
| return | |||
| } | |||
| @@ -14,6 +14,7 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/base" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/modules/util" | |||
| @@ -308,6 +309,14 @@ func Issues(ctx *context.Context) { | |||
| return | |||
| } | |||
| 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) | |||
| return | |||
| } | |||