code.gitea.io/gitea@v1.22.3/services/context/package.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package context 5 6 import ( 7 "fmt" 8 "net/http" 9 10 "code.gitea.io/gitea/models/organization" 11 packages_model "code.gitea.io/gitea/models/packages" 12 "code.gitea.io/gitea/models/perm" 13 "code.gitea.io/gitea/models/unit" 14 user_model "code.gitea.io/gitea/models/user" 15 "code.gitea.io/gitea/modules/setting" 16 "code.gitea.io/gitea/modules/structs" 17 "code.gitea.io/gitea/modules/templates" 18 ) 19 20 // Package contains owner, access mode and optional the package descriptor 21 type Package struct { 22 Owner *user_model.User 23 AccessMode perm.AccessMode 24 Descriptor *packages_model.PackageDescriptor 25 } 26 27 type packageAssignmentCtx struct { 28 *Base 29 Doer *user_model.User 30 ContextUser *user_model.User 31 } 32 33 // PackageAssignment returns a middleware to handle Context.Package assignment 34 func PackageAssignment() func(ctx *Context) { 35 return func(ctx *Context) { 36 errorFn := func(status int, title string, obj any) { 37 err, ok := obj.(error) 38 if !ok { 39 err = fmt.Errorf("%s", obj) 40 } 41 if status == http.StatusNotFound { 42 ctx.NotFound(title, err) 43 } else { 44 ctx.ServerError(title, err) 45 } 46 } 47 paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser} 48 ctx.Package = packageAssignment(paCtx, errorFn) 49 } 50 } 51 52 // PackageAssignmentAPI returns a middleware to handle Context.Package assignment 53 func PackageAssignmentAPI() func(ctx *APIContext) { 54 return func(ctx *APIContext) { 55 paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser} 56 ctx.Package = packageAssignment(paCtx, ctx.Error) 57 } 58 } 59 60 func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) *Package { 61 pkg := &Package{ 62 Owner: ctx.ContextUser, 63 } 64 var err error 65 pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer) 66 if err != nil { 67 errCb(http.StatusInternalServerError, "determineAccessMode", err) 68 return pkg 69 } 70 71 packageType := ctx.Params("type") 72 name := ctx.Params("name") 73 version := ctx.Params("version") 74 if packageType != "" && name != "" && version != "" { 75 pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version) 76 if err != nil { 77 if err == packages_model.ErrPackageNotExist { 78 errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err) 79 } else { 80 errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err) 81 } 82 return pkg 83 } 84 85 pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv) 86 if err != nil { 87 errCb(http.StatusInternalServerError, "GetPackageDescriptor", err) 88 return pkg 89 } 90 } 91 92 return pkg 93 } 94 95 func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) { 96 if setting.Service.RequireSignInView && (doer == nil || doer.IsGhost()) { 97 return perm.AccessModeNone, nil 98 } 99 100 if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) { 101 return perm.AccessModeNone, nil 102 } 103 104 // TODO: ActionUser permission check 105 accessMode := perm.AccessModeNone 106 if pkg.Owner.IsOrganization() { 107 org := organization.OrgFromUser(pkg.Owner) 108 109 if doer != nil && !doer.IsGhost() { 110 // 1. If user is logged in, check all team packages permissions 111 var err error 112 accessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx, doer.ID) 113 if err != nil { 114 return accessMode, err 115 } 116 // If access mode is less than write check every team for more permissions 117 // The minimum possible access mode is read for org members 118 if accessMode < perm.AccessModeWrite { 119 teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID) 120 if err != nil { 121 return accessMode, err 122 } 123 for _, t := range teams { 124 perm := t.UnitAccessMode(ctx, unit.TypePackages) 125 if accessMode < perm { 126 accessMode = perm 127 } 128 } 129 } 130 } 131 if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) { 132 // 2. If user is unauthorized or no org member, check if org is visible 133 accessMode = perm.AccessModeRead 134 } 135 } else { 136 if doer != nil && !doer.IsGhost() { 137 // 1. Check if user is package owner 138 if doer.ID == pkg.Owner.ID { 139 accessMode = perm.AccessModeOwner 140 } else if pkg.Owner.Visibility == structs.VisibleTypePublic || pkg.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited 141 accessMode = perm.AccessModeRead 142 } 143 } else if pkg.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public 144 accessMode = perm.AccessModeRead 145 } 146 } 147 148 return accessMode, nil 149 } 150 151 // PackageContexter initializes a package context for a request. 152 func PackageContexter() func(next http.Handler) http.Handler { 153 renderer := templates.HTMLRenderer() 154 return func(next http.Handler) http.Handler { 155 return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 156 base, baseCleanUp := NewBaseContext(resp, req) 157 defer baseCleanUp() 158 159 // it is still needed when rendering 500 page in a package handler 160 ctx := NewWebContext(base, renderer, nil) 161 ctx.Base.AppendContextValue(WebContextKey, ctx) 162 next.ServeHTTP(ctx.Resp, ctx.Req) 163 }) 164 } 165 }