|
4 | 4 | package packages |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "fmt" |
7 | 8 | "net/http" |
8 | 9 | "regexp" |
9 | 10 | "strings" |
@@ -63,6 +64,20 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) { |
63 | 64 | ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin") |
64 | 65 | return |
65 | 66 | } |
| 67 | + |
| 68 | + // check if scope only applies to public resources |
| 69 | + publicOnly, err := scope.PublicOnly() |
| 70 | + if err != nil { |
| 71 | + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) |
| 72 | + return |
| 73 | + } |
| 74 | + |
| 75 | + if publicOnly { |
| 76 | + if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { |
| 77 | + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages") |
| 78 | + return |
| 79 | + } |
| 80 | + } |
66 | 81 | } |
67 | 82 | } |
68 | 83 |
|
@@ -643,11 +658,54 @@ func CommonRoutes() *web.Router { |
643 | 658 | }) |
644 | 659 | }) |
645 | 660 | }, reqPackageAccess(perm.AccessModeRead)) |
646 | | - }, context.UserAssignmentWeb(), context.PackageAssignment()) |
| 661 | + }, context.UserAssignmentWeb(), context.PackageAssignment(), checkPackageTokenScope) |
647 | 662 |
|
648 | 663 | return r |
649 | 664 | } |
650 | 665 |
|
| 666 | +func checkPackageTokenScope(ctx *context.Context) { |
| 667 | + // Need OAuth2 token to be present. |
| 668 | + scope, scopeExists := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) |
| 669 | + if ctx.Data["IsApiToken"] != true || !scopeExists { |
| 670 | + return |
| 671 | + } |
| 672 | + |
| 673 | + // use the http method to determine the access level |
| 674 | + requiredScopeLevel := auth_model.Read |
| 675 | + if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" { |
| 676 | + requiredScopeLevel = auth_model.Write |
| 677 | + } |
| 678 | + |
| 679 | + // get the required scope for the given access level and category |
| 680 | + requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, auth_model.AccessTokenScopeCategoryPackage) |
| 681 | + allow, err := scope.HasScope(requiredScopes...) |
| 682 | + if err != nil { |
| 683 | + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) |
| 684 | + return |
| 685 | + } |
| 686 | + |
| 687 | + if !allow { |
| 688 | + ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) |
| 689 | + return |
| 690 | + } |
| 691 | + |
| 692 | + // check if scope only applies to public resources |
| 693 | + publicOnly, err := scope.PublicOnly() |
| 694 | + if err != nil { |
| 695 | + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) |
| 696 | + return |
| 697 | + } |
| 698 | + |
| 699 | + if !publicOnly { |
| 700 | + return |
| 701 | + } |
| 702 | + |
| 703 | + if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { |
| 704 | + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages") |
| 705 | + return |
| 706 | + } |
| 707 | +} |
| 708 | + |
651 | 709 | // ContainerRoutes provides endpoints that implement the OCI API to serve containers |
652 | 710 | // These have to be mounted on `/v2/...` to comply with the OCI spec: |
653 | 711 | // https://github.com/opencontainers/distribution-spec/blob/main/spec.md |
|
0 commit comments