Skip to content

Commit

Permalink
Add Standardized RBAC (#14)
Browse files Browse the repository at this point in the history
Make everything organization scoped, so we can apply RBAC rules.
  • Loading branch information
spjmurray authored Jun 14, 2024
1 parent 2ba0ae6 commit bc3f575
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 459 deletions.
4 changes: 2 additions & 2 deletions charts/region/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Helm chart for deploying Unikorn's Region Controller

type: application

version: v0.1.7
appVersion: v0.1.7
version: v0.1.8
appVersion: v0.1.8

icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png
12 changes: 6 additions & 6 deletions charts/region/templates/region-controller/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ spec:
{{ printf "- --cors-max-age=%s" $cors.maxAge | nindent 8 }}
{{- end }}
{{- end }}
{{- with $oidc := .Values.region.oidc }}
- --oidc-issuer={{ $oidc.issuer }}
{{- if $oidc.issuerCASecretNamespace }}
{{ printf "- --oidc-issuer-ca-secret-namespace=%s" $oidc.issuerCASecretNamespace | nindent 8 }}
{{- with $identity := .Values.region.identity }}
- --identity-host={{ $identity.host }}
{{- if $identity.caSecretNamespace }}
{{ printf "- --identity-ca-secret-namespace=%s" $identity.caSecretNamespace | nindent 8 }}
{{- end }}
{{- if $oidc.issuerCASecretName }}
{{ printf "- --oidc-issuer-ca-secret-name=%s" $oidc.issuerCASecretName | nindent 8 }}
{{- if $identity.caSecretName }}
{{ printf "- --identity-ca-secret-name=%s" $identity.caSecretName | nindent 8 }}
{{- end }}
{{- end }}
{{- if .Values.region.otlpEndpoint }}
Expand Down
8 changes: 4 additions & 4 deletions charts/region/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ region:
# # How long to cache the CORS preflight for, mostly useless as browsers override this.
# maxAge: 86400

oidc:
identity:
# OIDC issuer used to discover OIDC configuration and verify access tokens.
issuer: https://identity.unikorn-cloud.org
host: https://identity.unikorn-cloud.org

# CA certificate to use to verify connections to the issuer, used in development only.
# issuerCASecretNamespace: ~
# issuerCASecretName: ~
# caSecretNamespace: ~
# caSecretName: ~

# Sets the OTLP endpoint for shipping spans.
# otlpEndpoint: jaeger-collector.default:4318
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56
github.com/oapi-codegen/runtime v1.1.1
github.com/spf13/pflag v1.0.5
github.com/unikorn-cloud/core v0.1.47
github.com/unikorn-cloud/core v0.1.49
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 Expand Up @@ -57,6 +57,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/unikorn-cloud/identity v0.2.11 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.27.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unikorn-cloud/core v0.1.47 h1:zZ3kWkGMan5v32jv3+BJ7YKYT08JwOMJmJQsn43nsTU=
github.com/unikorn-cloud/core v0.1.47/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
github.com/unikorn-cloud/core v0.1.49 h1:ahAxrzvBnBICi+qN/AmTqKRJHpxl958gKVfBO3lz4G8=
github.com/unikorn-cloud/core v0.1.49/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/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
75 changes: 60 additions & 15 deletions pkg/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@ 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"
"github.com/unikorn-cloud/core/pkg/server/conversion"
"github.com/unikorn-cloud/core/pkg/server/errors"
"github.com/unikorn-cloud/core/pkg/server/middleware/openapi/oidc"
coreutil "github.com/unikorn-cloud/core/pkg/util"
"github.com/unikorn-cloud/identity/pkg/authorization"
identityclient "github.com/unikorn-cloud/identity/pkg/client"
unikornv1 "github.com/unikorn-cloud/region/pkg/apis/unikorn/v1alpha1"
"github.com/unikorn-cloud/region/pkg/handler/region"
"github.com/unikorn-cloud/region/pkg/openapi"
Expand All @@ -50,15 +54,15 @@ type Handler struct {
// options allows behaviour to be defined on the CLI.
options *Options

// authorizerOptions allows access to the identity service for RBAC callbacks.
authorizerOptions *oidc.Options
// identity is an identity client for RBAC access.
identity *identityclient.Client
}

func New(client client.Client, namespace string, options *Options, authorizerOptions *oidc.Options) (*Handler, error) {
func New(client client.Client, namespace string, options *Options, identity *identityclient.Client) (*Handler, error) {
h := &Handler{
client: client,
options: options,
authorizerOptions: authorizerOptions,
client: client,
options: options,
identity: identity,
}

return h, nil
Expand All @@ -73,7 +77,31 @@ func (h *Handler) setUncacheable(w http.ResponseWriter) {
w.Header().Add("Cache-Control", "no-cache")
}

func (h *Handler) GetApiV1Regions(w http.ResponseWriter, r *http.Request) {
//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) GetApiV1OrganizationsOrganizationIDRegions(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
errors.HandleError(w, r, err)
return
}

result, err := region.NewClient(h.client, h.namespace).List(r.Context())
if err != nil {
errors.HandleError(w, r, err)
Expand Down Expand Up @@ -119,7 +147,12 @@ func convertFlavor(in providers.Flavor) openapi.Flavor {
return out
}

func (h *Handler) GetApiV1RegionsRegionIDFlavors(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter) {
func (h *Handler) GetApiV1OrganizationsOrganizationIDRegionsRegionIDFlavors(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
errors.HandleError(w, r, err)
return
}

provider, err := region.NewClient(h.client, h.namespace).Provider(r.Context(), regionID)
if err != nil {
errors.HandleError(w, r, err)
Expand Down Expand Up @@ -175,7 +208,12 @@ func convertImage(in providers.Image) openapi.Image {
return out
}

func (h *Handler) GetApiV1RegionsRegionIDImages(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter) {
func (h *Handler) GetApiV1OrganizationsOrganizationIDRegionsRegionIDImages(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
errors.HandleError(w, r, err)
return
}

provider, err := region.NewClient(h.client, h.namespace).Provider(r.Context(), regionID)
if err != nil {
errors.HandleError(w, r, err)
Expand Down Expand Up @@ -234,7 +272,12 @@ func generateClusterInfo(in *openapi.IdentityWrite) *providers.ClusterInfo {
return out
}

func (h *Handler) PostApiV1RegionsRegionIDIdentities(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter) {
func (h *Handler) PostApiV1OrganizationsOrganizationIDRegionsRegionIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Create); err != nil {
errors.HandleError(w, r, err)
return
}

request := &openapi.IdentityWrite{}

if err := util.ReadJSONBody(r, request); err != nil {
Expand All @@ -258,9 +301,6 @@ func (h *Handler) PostApiV1RegionsRegionIDIdentities(w http.ResponseWriter, r *h
util.WriteJSONResponse(w, r, http.StatusCreated, convertCloudConfig(identity, cloudconfig))
}

func (h *Handler) DeleteApiV1RegionsRegionIDIdentitiesIdentityID(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter, identityID openapi.IdentityIDParameter) {
}

func convertExternalNetwork(in providers.ExternalNetwork) openapi.ExternalNetwork {
out := openapi.ExternalNetwork{
Id: in.ID,
Expand All @@ -280,7 +320,12 @@ func convertExternalNetworks(in providers.ExternalNetworks) openapi.ExternalNetw
return out
}

func (h *Handler) GetApiV1RegionsRegionIDExternalnetworks(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter) {
func (h *Handler) GetApiV1OrganizationsOrganizationIDRegionsRegionIDExternalnetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, regionID openapi.RegionIDParameter) {
if err := h.checkRBAC(r.Context(), organizationID, "infrastructure", constants.Read); err != nil {
errors.HandleError(w, r, err)
return
}

provider, err := region.NewClient(h.client, h.namespace).Provider(r.Context(), regionID)
if err != nil {
errors.HandleError(w, r, err)
Expand Down
Loading

0 comments on commit bc3f575

Please sign in to comment.