Skip to content

Commit

Permalink
RBAC Done Properly (#28)
Browse files Browse the repository at this point in the history
Use new and improved RBAC.
  • Loading branch information
spjmurray authored Jul 4, 2024
1 parent 12a6d70 commit 4cc8838
Show file tree
Hide file tree
Showing 16 changed files with 510 additions and 152 deletions.
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

0 comments on commit 4cc8838

Please sign in to comment.