drone/registry/app/api/handler/oci/get_token.go
Arvind Choudhary 479c9b9fe7 feat: [AH-307]: GC interface Integration, event framework; schema changes; UI changes (#2688)
* fix
* Merge branch 'main' into AH-307-plus-url-support-2_no_rbac
* resolve PR comments
* resolve PR comments
* resolve PR comments
* feat: [AH-346]: new api changes for version list and digest list (#2726)

* feat: [AH-346]: new api changes for version list and digest list
* resolve pr comments
* resolve pr comments
* feat: [AH-346]: new api yaml integration (#2716)

* feat: [AH-346]: new api yaml integration
* Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-307-plus-url-support-2_no_rbac
* fix wire check
* fix lint issues
* fix: [AH-357]: migrations
* changes for global artifact listing (#2708)

* changes for global artifact listing
* Merge branch 'main' into AH-307-plus-url-support-2_no_rbac
* merged main
* Merge branch 'main' into AH-307-plus-url-support-2_no_rbac
* [AH-307]: Updated lint
* fix comment
* add new method to spacestore
* feat: [AH-307]: fix after rebase with main
* [AH-307]: Removing comments
* [AH-307]: linting fixes
* feat: [AH-286]: define proto, interface and no-op reporter implementation to publish artifact events (#2657)

* feat: [AH-286]: publish artifact event - no row found is not error
* feat: [AH-286]: publish artifact event - no row found is not error
* feat: [AH-286]: publish artifact event - lint errors, move publishing event outside DB transaction
* feat: [AH-286]: publish artifact event - review comments
* feat: [AH-286]: publish artifact event - address review comments
* feat: [AH-286]: publish artifact event - keep payload generic
* feat: [AH-286]: publish artifact event - as sqlite locks DB, perform db operation outside goroutine publishing of events
* feat: [AH-286]: publish artifact event - make publishing event async
* feat: [AH-286]: publish artifact event - use api types
* feat: [AH-286]: Publish event for SSCA to trigger scans - no need to export spacePathStore
* feat: [AH-286]: Publish event for SSCA to trigger scans - send spacePath instead of parentID
* feat: [AH-286]: Publish event for SSCA to trigger scans - rename scanner as generic reporter
* feat: [AH-286]: Publish event for SSCA to trigger scans - rename scanner as generic reporter
* feat: [AH-286]: publish artifact event - reuse redis.Send()
* feat: [AH-286]: Publish event for SSCA to trigger scans - review comments
* feat: [AH-286]: Publish event for SSCA to trigger scans - remove unused interface
* feat: [AH-286]: Publish event for SSCA to trigger scans - update msg format
* feat: [AH-286]: define proto, interface and no-op reporter implementation to publish artifact events
* feat: [AH-286]: Publish event for SSCA to trigger scans - extract acctID/orgID/projectID from spacepathStore
* feat: [AH-286]: publish artifact event - remove protobuf reference, fix lint errors
* feat: [AH-286]: publish artifact event - fix msg format
* feat: [AH-286]: define proto, interface and no-op reporter implementation to publish artifact events
* feat: [AH-286]: define proto, interface and no-op reporter implementation to publish artifact events
* feat: [AH-321]: make repo form disabled for rbac (#2687)

* feat: [AH-321]: make repo form disabled for rbac
* fix wire-gen
* GC refactoring
* feat: [AH-340]: update UI as per the product feedbacks (#2685)

* feat: [AH-340]: update UI as per the product feedbacks
* feat: [AH-44]: add module data while redirecting to pipeline execution page
* feat: [AH-44]: add build pipeline details in overview cards
* feat: [AH-44]: update view for prod and non prod tag
* feat: [AH-44]: rearrange filters on artifact list apge
* feat: [AH-10]: add schema for overview cards, update artifact list, add ai search input, update api for registry artifact list and update mapping for deployments table
* feat: [AH-307]: add secretSpacePath in upstream password field while sending to BE (#2631)

* feat: [AH-307]: add secretSpacePath in upstream password field while sending to BE
* feat: [AH-299]: support new changes for artifact list page (#2630)

* feat: update har service api version
* feat: [AH-30]: integrate API schema for deployments list content
* feat: [AH-300]: update tag colors for prod and non prod tags
* feat: [AH-300]: Add Deployments table in artiface version details page
* feat: [AH-299]: support new changes for artifact list page
* feat: [AH-299]: support new changes for artifact list page
* feat: [AH-321]: support artifact registry rbac permission on UI (#2671)

* feat: [AH-321]: support artifact registry rbac permission on UI
* enable rbac (#2664)

* fix scope
* enable rbac
* feat: [AH-307]: hide code tab from version details page for both docker and helm
* feat: [AH-240]: add custom handling for enterprise auth type field
* Merge branch 'AH-307-plus-url-support-2_no_rbac' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-307-plus-url-support-2_no_rbac
* feat: [AH-307]: send space_ref in query param while creating registries
* lowercase rootRef
* [AH-307]: updated route
* [AH-307]: Added logs
* [AH-307]: Added logs
* feat: [AH-317]: add space_ref query param
* local
* Merge commit
* Merge commit
* Merge commit
* Added comments
* Revert changes
* Merge commit
* Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-307-plus-url-support-2
* Merge branch 'AH-306d' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-307-plus-url-support-2
* fix space path handling
* Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into AH-307-plus-url-support-2
* Updated URLs to support slashes with + separator
* fix: [AH-306c]: fix anonymous flow
* fix: [AH-306c]: fix anonymous flow
* feat: [AH-307]: plus url support on UI

(cherry picked from commit 3fb6add3ce03498b6668b5f8f6d547e1acedaec4)
* [AH-307]: Added examples

(cherry picked from commit e83e41303da536f421be333be04aed09fbf75f5f)
* [AH-307]: Added Regex request rewrite support

(cherry picked from commit ed7b155256bdcd1134bc228b5705556a1233add6)
* fix: [AH-306c]: fix anonymous flow
2024-09-24 12:47:53 +00:00

222 lines
5.9 KiB
Go

// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oci
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/jwt"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/app/token"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
type TokenResponseOCI struct {
Token string `json:"token"`
}
func (h *Handler) GetToken(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, ok := request.AuthSessionFrom(ctx)
if !ok || session.Principal == auth.AnonymousPrincipal {
returnForbiddenResponse(w, fmt.Errorf("no auth session found"))
return
}
if tokenMetadata, okt := session.Metadata.(*auth.TokenMetadata); okt &&
tokenMetadata.TokenType != enum.TokenTypePAT {
returnForbiddenResponse(w, fmt.Errorf("only personal access token allowed"))
return
}
user, err := h.UserCtrl.FindNoAuth(ctx, session.Principal.UID)
if err != nil {
returnForbiddenResponse(w, err)
return
}
requestedOciAccess := GetRequestedResourceActions(getScopes(r.URL))
var accessPermissionsList = []jwt.AccessPermissions{}
for _, ra := range requestedOciAccess {
space, err := h.getSpace(ctx, ra.Name)
if err != nil {
render.TranslatedUserError(ctx, w, err)
log.Ctx(ctx).Warn().Msgf("failed to find space by ref: %v", err)
continue
}
accessPermissionsList = h.getAccessPermissionList(ctx, space, ra, session, accessPermissionsList)
}
subClaimsAccessPermissions := &jwt.SubClaimsAccessPermissions{
Source: jwt.OciSource,
Permissions: accessPermissionsList,
}
jwtToken, err := h.getTokenDetails(user, subClaimsAccessPermissions)
if err != nil {
returnForbiddenResponse(w, err)
return
}
if jwtToken != "" {
w.WriteHeader(http.StatusOK)
enc := json.NewEncoder(w)
if err := enc.Encode(
TokenResponseOCI{
Token: jwtToken,
},
); err != nil {
log.Error().Msgf("failed to write token response: %v", err)
}
return
}
}
func (h *Handler) getSpace(ctx context.Context, name string) (*types.Space, error) {
spaceRef, _, _ := paths.DisectRoot(name)
space, err := h.SpaceStore.FindByRef(ctx, spaceRef)
return space, err
}
func (h *Handler) getAccessPermissionList(
ctx context.Context, space *types.Space, ra *ResourceActions, session *auth.Session,
accessPermissionsList []jwt.AccessPermissions,
) []jwt.AccessPermissions {
accessPermissions := &jwt.AccessPermissions{SpaceID: space.ID, Permissions: []enum.Permission{}}
for _, a := range ra.Actions {
permission, err := getPermissionFromAction(ctx, a)
if err != nil {
log.Ctx(ctx).Warn().Msgf("failed to get permission from action: %v", err)
continue
}
scopeErr := apiauth.CheckSpaceScope(
ctx,
h.Authorizer,
session,
space,
enum.ResourceTypeRegistry,
permission,
)
if scopeErr != nil {
log.Ctx(ctx).Warn().Msgf("failed to check space scope: %v", scopeErr)
continue
}
accessPermissions.Permissions = append(accessPermissions.Permissions, permission)
}
accessPermissionsList = append(accessPermissionsList, *accessPermissions)
return accessPermissionsList
}
func getPermissionFromAction(ctx context.Context, action string) (enum.Permission, error) {
switch action {
case "pull":
return enum.PermissionArtifactsDownload, nil
case "push":
return enum.PermissionArtifactsUpload, nil
case "delete":
return enum.PermissionArtifactsDelete, nil
default:
err := fmt.Errorf("unknown action: %s", action)
log.Ctx(ctx).Err(err).Msgf("Failed to get permission from action: %v", err)
return "", err
}
}
func returnForbiddenResponse(w http.ResponseWriter, err error) {
w.WriteHeader(http.StatusForbidden)
_, err2 := w.Write([]byte(fmt.Sprintf("requested access to the resource is denied: %v", err)))
if err2 != nil {
log.Error().Msgf("failed to write token response: %v", err2)
}
}
/*
* getTokenDetails attempts to get token details.
*/
func (h *Handler) getTokenDetails(
user *types.User,
accessPermissions *jwt.SubClaimsAccessPermissions,
) (string, error) {
return token.CreateUserWithAccessPermissions(user, accessPermissions)
}
// GetRequestedResourceActions ...
func GetRequestedResourceActions(scopes []string) []*ResourceActions {
var res []*ResourceActions
for _, s := range scopes {
if s == "" {
continue
}
items := strings.Split(s, ":")
length := len(items)
var resourceType string
var resourceName string
actions := make([]string, 0)
switch length {
case 1:
resourceType = items[0]
case 2:
resourceType = items[0]
resourceName = items[1]
default:
resourceType = items[0]
resourceName = strings.Join(items[1:length-1], ":")
if len(items[length-1]) > 0 {
actions = strings.Split(items[length-1], ",")
}
}
res = append(
res, &ResourceActions{
Type: resourceType,
Name: resourceName,
Actions: actions,
},
)
}
return res
}
func getScopes(u *url.URL) []string {
var sector string
var result []string
for _, sector = range u.Query()["scope"] {
result = append(result, strings.Split(sector, " ")...)
}
return result
}
// ResourceActions stores allowed actions on a resource.
type ResourceActions struct {
Type string `json:"type"`
Name string `json:"name"`
Actions []string `json:"actions"`
}