Skip to content

Commit 4cc8838

Browse files
authored
RBAC Done Properly (#28)
Use new and improved RBAC.
1 parent 12a6d70 commit 4cc8838

File tree

16 files changed

+510
-152
lines changed

16 files changed

+510
-152
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ GOBIN := $(if $(shell go env GOBIN),$(shell go env GOBIN),$(GOPATH)/bin)
5959
FLAGS=-trimpath -ldflags '-X $(MODULE)/pkg/constants.Version=$(VERSION) -X $(MODULE)/pkg/constants.Revision=$(REVISION)'
6060

6161
# Defines the linter version.
62-
LINT_VERSION=v1.57.1
62+
LINT_VERSION=v1.59.1
6363

6464
# Defines the version of the CRD generation tools to use.
6565
CONTROLLER_TOOLS_VERSION=v0.14.0

charts/region/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn's Region Controller
44

55
type: application
66

7-
version: v0.1.18
8-
appVersion: v0.1.18
7+
version: v0.1.19
8+
appVersion: v0.1.19
99

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

charts/region/crds/region.unikorn-cloud.org_identities.yaml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ spec:
6262
to "openstack".
6363
properties:
6464
projectID:
65-
description: ProjectIS is the ID of the project created for the
65+
description: ProjectID is the ID of the project created for the
6666
identity.
6767
type: string
6868
userID:
@@ -77,6 +77,24 @@ spec:
7777
enum:
7878
- openstack
7979
type: string
80+
tags:
81+
description: |-
82+
Tags are an abitrary list of key/value pairs that a client
83+
may populate to store metadata for the resource.
84+
items:
85+
description: Tag is an arbirary key/value.
86+
properties:
87+
name:
88+
description: Name of the tag.
89+
type: string
90+
value:
91+
description: Value of the tag.
92+
type: string
93+
required:
94+
- name
95+
- value
96+
type: object
97+
type: array
8098
required:
8199
- provider
82100
type: object
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.14.0
7+
name: physicalnetworks.region.unikorn-cloud.org
8+
spec:
9+
group: region.unikorn-cloud.org
10+
names:
11+
categories:
12+
- unikorn
13+
kind: PhysicalNetwork
14+
listKind: PhysicalNetworkList
15+
plural: physicalnetworks
16+
singular: physicalnetwork
17+
scope: Namespaced
18+
versions:
19+
- additionalPrinterColumns:
20+
- jsonPath: .status.conditions[?(@.type=="Available")].reason
21+
name: status
22+
type: string
23+
- jsonPath: .metadata.creationTimestamp
24+
name: age
25+
type: date
26+
name: v1alpha1
27+
schema:
28+
openAPIV3Schema:
29+
description: PhysicalNetwork defines a physical network beloning to an identity.
30+
properties:
31+
apiVersion:
32+
description: |-
33+
APIVersion defines the versioned schema of this representation of an object.
34+
Servers should convert recognized schemas to the latest internal value, and
35+
may reject unrecognized values.
36+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
37+
type: string
38+
kind:
39+
description: |-
40+
Kind is a string value representing the REST resource this object represents.
41+
Servers may infer this from the endpoint the client submits requests to.
42+
Cannot be updated.
43+
In CamelCase.
44+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
45+
type: string
46+
metadata:
47+
type: object
48+
spec:
49+
properties:
50+
providerNetwork:
51+
description: |-
52+
ProviderNetwork is the provider network for port allocation of
53+
virtual machines.
54+
properties:
55+
id:
56+
description: ID is the network ID.
57+
type: string
58+
vlanID:
59+
description: VlanID is the ID if the VLAN for IPAM.
60+
type: integer
61+
required:
62+
- id
63+
- vlanID
64+
type: object
65+
tags:
66+
description: |-
67+
Tags are an abitrary list of key/value pairs that a client
68+
may populate to store metadata for the resource.
69+
items:
70+
description: Tag is an arbirary key/value.
71+
properties:
72+
name:
73+
description: Name of the tag.
74+
type: string
75+
value:
76+
description: Value of the tag.
77+
type: string
78+
required:
79+
- name
80+
- value
81+
type: object
82+
type: array
83+
type: object
84+
status:
85+
type: object
86+
required:
87+
- spec
88+
- status
89+
type: object
90+
served: true
91+
storage: true
92+
subresources: {}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ require (
1010
github.com/oapi-codegen/runtime v1.1.1
1111
github.com/spf13/pflag v1.0.5
1212
github.com/stretchr/testify v1.9.0
13-
github.com/unikorn-cloud/core v0.1.55
14-
github.com/unikorn-cloud/identity v0.2.11
13+
github.com/unikorn-cloud/core v0.1.57
14+
github.com/unikorn-cloud/identity v0.2.20
1515
go.opentelemetry.io/otel v1.27.0
1616
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
1717
go.opentelemetry.io/otel/sdk v1.27.0

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
136136
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
137137
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
138138
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
139-
github.com/unikorn-cloud/core v0.1.55 h1:Oy5r3UBTNWb0qFDcmehLrgBwMx9xCo9x2nOEzNZoYUU=
140-
github.com/unikorn-cloud/core v0.1.55/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
141-
github.com/unikorn-cloud/identity v0.2.11 h1:q6mkJ3qTRjwhlvLS9Jv0I4wlJhnsbJZHu2rbNdnXBYk=
142-
github.com/unikorn-cloud/identity v0.2.11/go.mod h1:4KHNdHiIKpKERD0slunDDXhdC59M7eiN+Y1wSfHbQwQ=
139+
github.com/unikorn-cloud/core v0.1.57 h1:/5LjDH/z2C+NZSbfRUNBgP2IRwe/7OfnR7OgDtJAREs=
140+
github.com/unikorn-cloud/core v0.1.57/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
141+
github.com/unikorn-cloud/identity v0.2.20 h1:djsq9+F7siK3sUpGpaEM5LnCbCRtL0BIvbkOEAz5YSU=
142+
github.com/unikorn-cloud/identity v0.2.20/go.mod h1:xeFcIJ4yZ7wRQCW8Fs1rL/NvP04LBOiv+Y1KQF3etl4=
143143
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
144144
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
145145
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=

pkg/handler/handler.go

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,28 @@ package handler
2020

2121
import (
2222
"cmp"
23-
"context"
2423
"encoding/base64"
2524
"fmt"
2625
"net/http"
2726
"slices"
2827
"time"
2928

30-
"github.com/unikorn-cloud/core/pkg/authorization/constants"
31-
"github.com/unikorn-cloud/core/pkg/authorization/userinfo"
32-
coreopenapi "github.com/unikorn-cloud/core/pkg/openapi"
29+
coreapi "github.com/unikorn-cloud/core/pkg/openapi"
3330
"github.com/unikorn-cloud/core/pkg/server/conversion"
3431
"github.com/unikorn-cloud/core/pkg/server/errors"
3532
coreutil "github.com/unikorn-cloud/core/pkg/util"
36-
"github.com/unikorn-cloud/identity/pkg/authorization"
3733
identityclient "github.com/unikorn-cloud/identity/pkg/client"
34+
identityapi "github.com/unikorn-cloud/identity/pkg/openapi"
35+
"github.com/unikorn-cloud/identity/pkg/rbac"
3836
unikornv1 "github.com/unikorn-cloud/region/pkg/apis/unikorn/v1alpha1"
37+
"github.com/unikorn-cloud/region/pkg/constants"
3938
"github.com/unikorn-cloud/region/pkg/handler/region"
4039
"github.com/unikorn-cloud/region/pkg/openapi"
4140
"github.com/unikorn-cloud/region/pkg/providers"
4241
"github.com/unikorn-cloud/region/pkg/server/util"
4342

43+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
44+
4445
"sigs.k8s.io/controller-runtime/pkg/client"
4546
)
4647

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

80-
//nolint:unparam
81-
func (h *Handler) checkRBAC(ctx context.Context, organizationID, scope string, permission constants.Permission) error {
82-
identity, err := h.identity.Client(ctx)
83-
if err != nil {
84-
return err
85-
}
86-
87-
authorizer, err := userinfo.NewAuthorizer(ctx, authorization.NewIdentityACLGetter(identity, organizationID))
88-
if err != nil {
89-
return errors.HTTPForbidden("operation is not allowed by rbac").WithError(err)
90-
}
91-
92-
if err := authorizer.Allow(ctx, scope, permission); err != nil {
93-
return errors.HTTPForbidden("operation is not allowed by rbac").WithError(err)
94-
}
95-
96-
return nil
97-
}
98-
9981
func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegions(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter) {
100-
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
82+
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil {
10183
errors.HandleError(w, r, err)
10284
return
10385
}
@@ -125,7 +107,7 @@ func convertGpuVendor(in providers.GPUVendor) openapi.GpuVendor {
125107

126108
func convertFlavor(in providers.Flavor) openapi.Flavor {
127109
out := openapi.Flavor{
128-
Metadata: coreopenapi.StaticResourceMetadata{
110+
Metadata: coreapi.StaticResourceMetadata{
129111
Id: in.ID,
130112
Name: in.Name,
131113
},
@@ -150,7 +132,7 @@ func convertFlavor(in providers.Flavor) openapi.Flavor {
150132
}
151133

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

194176
func convertImage(in providers.Image) openapi.Image {
195177
out := openapi.Image{
196-
Metadata: coreopenapi.StaticResourceMetadata{
178+
Metadata: coreapi.StaticResourceMetadata{
197179
Id: in.ID,
198180
Name: in.Name,
199181
CreationTime: in.Created,
@@ -211,7 +193,7 @@ func convertImage(in providers.Image) openapi.Image {
211193
}
212194

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

251+
func regionScopedResourceReadMetadata(in metav1.Object, status coreapi.ResourceProvisioningStatus) coreapi.RegionScopedResourceMetadata {
252+
labels := in.GetLabels()
253+
254+
temp := conversion.ProjectScopedResourceReadMetadata(in, status)
255+
256+
out := coreapi.RegionScopedResourceMetadata{
257+
Id: temp.Id,
258+
Name: temp.Name,
259+
Description: temp.Description,
260+
CreatedBy: temp.CreatedBy,
261+
CreationTime: temp.CreationTime,
262+
ProvisioningStatus: temp.ProvisioningStatus,
263+
OrganizationId: temp.OrganizationId,
264+
ProjectId: temp.ProjectId,
265+
RegionId: labels[constants.RegionLabel],
266+
}
267+
268+
return out
269+
}
270+
269271
func convertIdentity(identity *unikornv1.Identity, in *providers.CloudConfig) *openapi.IdentityRead {
270272
out := &openapi.IdentityRead{
271-
Metadata: conversion.ProjectScopedResourceReadMetadata(identity, coreopenapi.ResourceProvisioningStatusProvisioned),
273+
Metadata: regionScopedResourceReadMetadata(identity, coreapi.ResourceProvisioningStatusProvisioned),
272274
}
273275

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

296+
func (h *Handler) GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter) {
297+
}
298+
294299
func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) {
295-
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Create); err != nil {
300+
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Create, organizationID, projectID); err != nil {
296301
errors.HandleError(w, r, err)
297302
return
298303
}
@@ -322,7 +327,7 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRe
322327

323328
func convertPhysicalNetwork(in *unikornv1.PhysicalNetwork) *openapi.PhysicalNetworkRead {
324329
out := &openapi.PhysicalNetworkRead{
325-
Metadata: conversion.ProjectScopedResourceReadMetadata(in, coreopenapi.ResourceProvisioningStatusProvisioned),
330+
Metadata: conversion.ProjectScopedResourceReadMetadata(in, coreapi.ResourceProvisioningStatusProvisioned),
326331
}
327332

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

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

382387
func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDRegionsRegionIDExternalnetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, regionID openapi.RegionIDParameter) {
383-
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
388+
if err := rbac.AllowProjectScope(r.Context(), "infrastructure", identityapi.Read, organizationID, projectID); err != nil {
384389
errors.HandleError(w, r, err)
385390
return
386391
}

0 commit comments

Comments
 (0)