Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RBAC Done Properly #28

Merged
merged 1 commit into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ GOBIN := $(if $(shell go env GOBIN),$(shell go env GOBIN),$(GOPATH)/bin)
FLAGS=-trimpath -ldflags '-X $(MODULE)/pkg/constants.Version=$(VERSION) -X $(MODULE)/pkg/constants.Revision=$(REVISION)'

# Defines the linter version.
LINT_VERSION=v1.57.1
LINT_VERSION=v1.59.1

# Defines the version of the CRD generation tools to use.
CONTROLLER_TOOLS_VERSION=v0.14.0
Expand Down
4 changes: 2 additions & 2 deletions charts/region/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn's Region Controller

type: application

version: v0.1.18
appVersion: v0.1.18
version: v0.1.19
appVersion: v0.1.19

icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png

Expand Down
20 changes: 19 additions & 1 deletion charts/region/crds/region.unikorn-cloud.org_identities.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ spec:
to "openstack".
properties:
projectID:
description: ProjectIS is the ID of the project created for the
description: ProjectID is the ID of the project created for the
identity.
type: string
userID:
Expand All @@ -77,6 +77,24 @@ spec:
enum:
- openstack
type: string
tags:
description: |-
Tags are an abitrary list of key/value pairs that a client
may populate to store metadata for the resource.
items:
description: Tag is an arbirary key/value.
properties:
name:
description: Name of the tag.
type: string
value:
description: Value of the tag.
type: string
required:
- name
- value
type: object
type: array
required:
- provider
type: object
Expand Down
92 changes: 92 additions & 0 deletions charts/region/crds/region.unikorn-cloud.org_physicalnetworks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
name: physicalnetworks.region.unikorn-cloud.org
spec:
group: region.unikorn-cloud.org
names:
categories:
- unikorn
kind: PhysicalNetwork
listKind: PhysicalNetworkList
plural: physicalnetworks
singular: physicalnetwork
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.conditions[?(@.type=="Available")].reason
name: status
type: string
- jsonPath: .metadata.creationTimestamp
name: age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: PhysicalNetwork defines a physical network beloning to an identity.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
providerNetwork:
description: |-
ProviderNetwork is the provider network for port allocation of
virtual machines.
properties:
id:
description: ID is the network ID.
type: string
vlanID:
description: VlanID is the ID if the VLAN for IPAM.
type: integer
required:
- id
- vlanID
type: object
tags:
description: |-
Tags are an abitrary list of key/value pairs that a client
may populate to store metadata for the resource.
items:
description: Tag is an arbirary key/value.
properties:
name:
description: Name of the tag.
type: string
value:
description: Value of the tag.
type: string
required:
- name
- value
type: object
type: array
type: object
status:
type: object
required:
- spec
- status
type: object
served: true
storage: true
subresources: {}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ require (
github.com/oapi-codegen/runtime v1.1.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/unikorn-cloud/core v0.1.55
github.com/unikorn-cloud/identity v0.2.11
github.com/unikorn-cloud/core v0.1.57
github.com/unikorn-cloud/identity v0.2.20
go.opentelemetry.io/otel v1.27.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
go.opentelemetry.io/otel/sdk v1.27.0
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unikorn-cloud/core v0.1.55 h1:Oy5r3UBTNWb0qFDcmehLrgBwMx9xCo9x2nOEzNZoYUU=
github.com/unikorn-cloud/core v0.1.55/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
github.com/unikorn-cloud/identity v0.2.11 h1:q6mkJ3qTRjwhlvLS9Jv0I4wlJhnsbJZHu2rbNdnXBYk=
github.com/unikorn-cloud/identity v0.2.11/go.mod h1:4KHNdHiIKpKERD0slunDDXhdC59M7eiN+Y1wSfHbQwQ=
github.com/unikorn-cloud/core v0.1.57 h1:/5LjDH/z2C+NZSbfRUNBgP2IRwe/7OfnR7OgDtJAREs=
github.com/unikorn-cloud/core v0.1.57/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
github.com/unikorn-cloud/identity v0.2.20 h1:djsq9+F7siK3sUpGpaEM5LnCbCRtL0BIvbkOEAz5YSU=
github.com/unikorn-cloud/identity v0.2.20/go.mod h1:xeFcIJ4yZ7wRQCW8Fs1rL/NvP04LBOiv+Y1KQF3etl4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand Down
73 changes: 39 additions & 34 deletions pkg/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,28 @@ package handler

import (
"cmp"
"context"
"encoding/base64"
"fmt"
"net/http"
"slices"
"time"

"github.com/unikorn-cloud/core/pkg/authorization/constants"
"github.com/unikorn-cloud/core/pkg/authorization/userinfo"
coreopenapi "github.com/unikorn-cloud/core/pkg/openapi"
coreapi "github.com/unikorn-cloud/core/pkg/openapi"
"github.com/unikorn-cloud/core/pkg/server/conversion"
"github.com/unikorn-cloud/core/pkg/server/errors"
coreutil "github.com/unikorn-cloud/core/pkg/util"
"github.com/unikorn-cloud/identity/pkg/authorization"
identityclient "github.com/unikorn-cloud/identity/pkg/client"
identityapi "github.com/unikorn-cloud/identity/pkg/openapi"
"github.com/unikorn-cloud/identity/pkg/rbac"
unikornv1 "github.com/unikorn-cloud/region/pkg/apis/unikorn/v1alpha1"
"github.com/unikorn-cloud/region/pkg/constants"
"github.com/unikorn-cloud/region/pkg/handler/region"
"github.com/unikorn-cloud/region/pkg/openapi"
"github.com/unikorn-cloud/region/pkg/providers"
"github.com/unikorn-cloud/region/pkg/server/util"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -77,27 +78,8 @@ func (h *Handler) setUncacheable(w http.ResponseWriter) {
w.Header().Add("Cache-Control", "no-cache")
}

//nolint:unparam
func (h *Handler) checkRBAC(ctx context.Context, organizationID, scope string, permission constants.Permission) error {
identity, err := h.identity.Client(ctx)
if err != nil {
return err
}

authorizer, err := userinfo.NewAuthorizer(ctx, authorization.NewIdentityACLGetter(identity, organizationID))
if err != nil {
return errors.HTTPForbidden("operation is not allowed by rbac").WithError(err)
}

if err := authorizer.Allow(ctx, scope, permission); err != nil {
return errors.HTTPForbidden("operation is not allowed by rbac").WithError(err)
}

return nil
}

func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil {
errors.HandleError(w, r, err)
return
}
Expand Down Expand Up @@ -125,7 +107,7 @@ func convertGpuVendor(in providers.GPUVendor) openapi.GpuVendor {

func convertFlavor(in providers.Flavor) openapi.Flavor {
out := openapi.Flavor{
Metadata: coreopenapi.StaticResourceMetadata{
Metadata: coreapi.StaticResourceMetadata{
Id: in.ID,
Name: in.Name,
},
Expand All @@ -150,7 +132,7 @@ func convertFlavor(in providers.Flavor) openapi.Flavor {
}

func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDFlavors(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil {
errors.HandleError(w, r, err)
return
}
Expand Down Expand Up @@ -193,7 +175,7 @@ func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsReg

func convertImage(in providers.Image) openapi.Image {
out := openapi.Image{
Metadata: coreopenapi.StaticResourceMetadata{
Metadata: coreapi.StaticResourceMetadata{
Id: in.ID,
Name: in.Name,
CreationTime: in.Created,
Expand All @@ -211,7 +193,7 @@ func convertImage(in providers.Image) openapi.Image {
}

func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDImages(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil {
errors.HandleError(w, r, err)
return
}
Expand Down Expand Up @@ -266,9 +248,29 @@ func convertTags(in unikornv1.TagList) openapi.TagList {
return out
}

func regionScopedResourceReadMetadata(in metav1.Object, status coreapi.ResourceProvisioningStatus) coreapi.RegionScopedResourceMetadata {
labels := in.GetLabels()

temp := conversion.ProjectScopedResourceReadMetadata(in, status)

out := coreapi.RegionScopedResourceMetadata{
Id: temp.Id,
Name: temp.Name,
Description: temp.Description,
CreatedBy: temp.CreatedBy,
CreationTime: temp.CreationTime,
ProvisioningStatus: temp.ProvisioningStatus,
OrganizationId: temp.OrganizationId,
ProjectId: temp.ProjectId,
RegionId: labels[constants.RegionLabel],
}

return out
}

func convertIdentity(identity *unikornv1.Identity, in *providers.CloudConfig) *openapi.IdentityRead {
out := &openapi.IdentityRead{
Metadata: conversion.ProjectScopedResourceReadMetadata(identity, coreopenapi.ResourceProvisioningStatusProvisioned),
Metadata: regionScopedResourceReadMetadata(identity, coreapi.ResourceProvisioningStatusProvisioned),
}

if tags := convertTags(identity.Spec.Tags); tags != nil {
Expand All @@ -291,8 +293,11 @@ func convertIdentity(identity *unikornv1.Identity, in *providers.CloudConfig) *o
return out
}

func (h *Handler) GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter) {
}

func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Create); err != nil {
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Create, organizationID, projectID); err != nil {
errors.HandleError(w, r, err)
return
}
Expand Down Expand Up @@ -322,7 +327,7 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRe

func convertPhysicalNetwork(in *unikornv1.PhysicalNetwork) *openapi.PhysicalNetworkRead {
out := &openapi.PhysicalNetworkRead{
Metadata: conversion.ProjectScopedResourceReadMetadata(in, coreopenapi.ResourceProvisioningStatusProvisioned),
Metadata: conversion.ProjectScopedResourceReadMetadata(in, coreapi.ResourceProvisioningStatusProvisioned),
}

if tags := convertTags(in.Spec.Tags); tags != nil {
Expand All @@ -333,7 +338,7 @@ func convertPhysicalNetwork(in *unikornv1.PhysicalNetwork) *openapi.PhysicalNetw
}

func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDIdentitiesIdentityIDPhysicalNetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter, identityID openapi.IdentityIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Create); err != nil {
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Create, organizationID, projectID); err != nil {
errors.HandleError(w, r, err)
return
}
Expand Down Expand Up @@ -380,7 +385,7 @@ func convertExternalNetworks(in providers.ExternalNetworks) openapi.ExternalNetw
}

func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDExternalnetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil {
errors.HandleError(w, r, err)
return
}
Expand Down
Loading
Loading