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  }