diff --git a/cyclops-ctrl/.dockerignore b/cyclops-ctrl/.dockerignore index ba077a40..ff84dbbb 100644 --- a/cyclops-ctrl/.dockerignore +++ b/cyclops-ctrl/.dockerignore @@ -1 +1,2 @@ bin +.env \ No newline at end of file diff --git a/cyclops-ctrl/.env b/cyclops-ctrl/.env index 78d4024a..6274daf4 100644 --- a/cyclops-ctrl/.env +++ b/cyclops-ctrl/.env @@ -1,4 +1,6 @@ DISABLE_TELEMETRY=true PORT=8888 -WATCH_NAMESPACE=cyclops +CERBOS_URL='localhost:3593' +CYCLOPS_AUTHORIZATION='enabled' CYCLOPS_VERSION=v0.0.0 +WATCH_NAMESPACE=cyclops \ No newline at end of file diff --git a/cyclops-ctrl/cmd/main/main.go b/cyclops-ctrl/cmd/main/main.go index 8d3b7048..8478506e 100644 --- a/cyclops-ctrl/cmd/main/main.go +++ b/cyclops-ctrl/cmd/main/main.go @@ -20,6 +20,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/auth" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/handler" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/modulecontroller" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/prometheus" @@ -81,6 +83,16 @@ func main() { cache.NewInMemoryTemplatesCache(), ) + var cerbosAddr string + flag.StringVar(&cerbosAddr, "cerbos", os.Getenv("CERBOS_URL"), "Address of the Cerbos server") + flag.Parse() + + cerbosClient, err := cerbos.New(cerbosAddr) + if err != nil { + setupLog.Info("unable to connect to cerbos server") + } + setupLog.Info("connected to cerbos server") + monitor, err := prometheus.NewMonitor(setupLog) if err != nil { setupLog.Error(err, "failed to set up prom monitor") @@ -90,7 +102,7 @@ func main() { prometheus.StartCacheMetricsUpdater(&monitor, templatesRepo.ReturnCache(), 10*time.Second, setupLog) - handler, err := handler.New(templatesRepo, k8sClient, renderer, telemetryClient, monitor) + handler, err := handler.New(templatesRepo, k8sClient, renderer, cerbosClient, telemetryClient, monitor) if err != nil { panic(err) } diff --git a/cyclops-ctrl/docker-compose.yaml b/cyclops-ctrl/docker-compose.yaml index 8ce36ba5..6c758d34 100644 --- a/cyclops-ctrl/docker-compose.yaml +++ b/cyclops-ctrl/docker-compose.yaml @@ -1,7 +1,13 @@ -version: "3.0" +version: "3.8" + services: - redis: - container_name: redis - image: redis + cerbos: + container_name: cerbos + image: ghcr.io/cerbos/cerbos:latest ports: - - 6379:6379 + - "3592:3592" + - "3593:3593" + volumes: + - ./internal/cerbos/config:/config + - ./internal/cerbos/policies:/policies + command: server --config=/config/conf.yaml diff --git a/cyclops-ctrl/go.mod b/cyclops-ctrl/go.mod index 7d15f2dd..456ec411 100644 --- a/cyclops-ctrl/go.mod +++ b/cyclops-ctrl/go.mod @@ -6,11 +6,13 @@ toolchain go1.22.5 require ( github.com/Masterminds/semver/v3 v3.2.1 + github.com/cerbos/cerbos-sdk-go v0.2.8 github.com/dgraph-io/ristretto v0.1.1 github.com/gin-gonic/gin v1.9.1 github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.11.0 github.com/go-logr/logr v1.4.1 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 github.com/json-iterator/go v1.1.12 @@ -27,9 +29,11 @@ require ( k8s.io/apimachinery v0.30.1 k8s.io/client-go v0.30.1 sigs.k8s.io/controller-runtime v0.18.4 + sigs.k8s.io/yaml v1.4.0 ) require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1 // indirect dario.cat/mergo v1.0.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/BurntSushi/toml v1.3.2 // indirect @@ -38,8 +42,11 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bufbuild/protovalidate-go v0.6.2 // indirect github.com/bytedance/sonic v1.9.1 // indirect + github.com/cerbos/cerbos/api/genpb v0.36.1-0.20240612095234-af7a526c03b6 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cloudflare/circl v1.3.7 // indirect @@ -47,6 +54,7 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v25.0.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect @@ -61,6 +69,7 @@ require ( github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -75,27 +84,38 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jdxcode/netrc v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.16.0 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -104,15 +124,21 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc6 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/planetscale/vtprotobuf v0.6.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.2.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect @@ -127,21 +153,21 @@ require ( go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/mod v0.15.0 // indirect + golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect + golang.org/x/mod v0.16.0 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.19.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gotest.tools/v3 v3.5.1 // indirect @@ -151,5 +177,4 @@ require ( oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/cyclops-ctrl/go.sum b/cyclops-ctrl/go.sum index 7cce19f6..9a90e448 100644 --- a/cyclops-ctrl/go.sum +++ b/cyclops-ctrl/go.sum @@ -1,7 +1,11 @@ +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1 h1:LEXWFH/xZ5oOWrC3oOtHbUyBdzRWMCPpAQmKC9v05mA= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.1-20240508200655-46a4cf4ba109.1/go.mod h1:XF+P8+RmfdufmIYpGUC+6bF7S+IlmHDEnCrO3OXaUAQ= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -16,6 +20,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -24,6 +30,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -32,6 +40,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bufbuild/protovalidate-go v0.6.2 h1:U/V3CGF0kPlR12v41rjO4DrYZtLcS4ZONLmWN+rJVCQ= +github.com/bufbuild/protovalidate-go v0.6.2/go.mod h1:4BR3rKEJiUiTy+sqsusFn2ladOf0kYmA2Reo6BHSBgQ= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= @@ -42,6 +52,12 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cerbos/cerbos-sdk-go v0.2.8 h1:+pPSsH9yh2EP1y+z+9Md9s5ArdiEuge3T2ZIBnuYLUI= +github.com/cerbos/cerbos-sdk-go v0.2.8/go.mod h1:YfMDWB/AUIEVoufmg4bfhCF0P3YXPrnA2UUxP4SlEEA= +github.com/cerbos/cerbos/api/genpb v0.36.1-0.20240612095234-af7a526c03b6 h1:l+Ug7931K6sNdWTP905LbqKRlScdHTNsEJPr+/wO5Xo= +github.com/cerbos/cerbos/api/genpb v0.36.1-0.20240612095234-af7a526c03b6/go.mod h1:fc7ccSHg92dusTTaaD7gs5/QnIIphO4YT3JeCx7WfL8= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -68,6 +84,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= @@ -90,6 +108,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -101,6 +121,8 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -115,6 +137,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= @@ -163,21 +187,27 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -186,6 +216,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -193,6 +225,10 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -200,12 +236,14 @@ github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jdxcode/netrc v1.0.0 h1:tJR3fyzTcjDi22t30pCdpOT8WJ5gb32zfYE1hFNCOjk= +github.com/jdxcode/netrc v1.0.0/go.mod h1:Zi/ZFkEqFHTm7qkjyNJjaWH4LQA9LQhGJyF0lTYGpxw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -236,6 +274,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= @@ -248,13 +298,19 @@ github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -265,6 +321,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= @@ -273,6 +330,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= @@ -282,6 +343,8 @@ github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFz github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA= +github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posthog/posthog-go v0.0.0-20240315130956-036dfa9f3555 h1:RqJZxk2VAaZYCCk4ZVo7iLqp4a03LWitjE0tNIMyvMU= @@ -306,6 +369,10 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -324,6 +391,8 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -339,8 +408,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -393,17 +462,16 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -416,8 +484,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -458,15 +526,14 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -475,25 +542,28 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -508,7 +578,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= diff --git a/cyclops-ctrl/internal/cerbos/cerbos.go b/cyclops-ctrl/internal/cerbos/cerbos.go new file mode 100644 index 00000000..259afe73 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/cerbos.go @@ -0,0 +1,126 @@ +package cerbos + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + + cerbosSDK "github.com/cerbos/cerbos-sdk-go/cerbos" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos/db" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt" + ctrl "sigs.k8s.io/controller-runtime" +) + +// authCtxKeyType to create a unique key for storing +// and retrieving the authentication context from the request context. +type authCtxKeyType struct{} + +// authCtxKey is a unique value of type authCtxKeyType that is used as the key for +// the authentication context in the request context. +var authCtxKey = authCtxKeyType{} + +type authContext struct { + username string + principal *cerbosSDK.Principal +} + +type CerbosSvc struct { + cerbos *cerbosSDK.GRPCClient +} + +var ( + authLog = ctrl.Log.WithName("action") +) + +func New(cerbosAddr string) (*CerbosSvc, error) { + cerbosInstance, err := cerbosSDK.New(cerbosAddr, cerbosSDK.WithPlaintext()) + if err != nil { + return nil, err + } + return &CerbosSvc{cerbos: cerbosInstance}, nil +} + +// isAllowed is a utility function to check each action against a Cerbos policy. +func (s *CerbosSvc) IsAllowed(ctx context.Context, resource *cerbosSDK.Resource, action string) (bool, error) { + principalCtx := s.principalContext(ctx) + if principalCtx == nil { + return false, errors.New("principal context is nil") + } + allowed, err := principalCtx.IsAllowed(ctx, resource, action) + if err != nil { + return false, err + } + msg := fmt.Sprintf("%v actions is performed on %v.", action, resource) + authLog.Info(msg) + + return allowed, nil +} + +func getAuthContext(ctx context.Context) *authContext { + ac := ctx.Value(authCtxKey) + if ac == nil { + return nil + } + return ac.(*authContext) +} + +// principalContext retrieves the principal stored in the context by the authentication middleware. +func (s *CerbosSvc) principalContext(ctx context.Context) cerbosSDK.PrincipalContext { + actx := getAuthContext(ctx) + if actx == nil { + log.Fatal("getAuthContext is nil") + } + msg := fmt.Sprintf("%v is performing actions...", actx.username) + authLog.Info(msg) + return s.cerbos.WithPrincipal(actx.principal) +} + +// AuthMiddleware is a Gin middleware for authenticating requests and setting the auth context. +func AuthMiddleware(cerbosClient *CerbosSvc) gin.HandlerFunc { + return func(c *gin.Context) { + accessToken, err := c.Cookie("cyclops.token") + if err != nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + return + } + + claims := &Claims{} + token, err := jwt.ParseWithClaims(accessToken, claims, func(token *jwt.Token) (interface{}, error) { + return jwtKey, nil + }) + + if err != nil || !token.Valid { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + return + } + + authCtx, err := buildAuthContext(claims.Subject, c, cerbosClient) + if err != nil { + log.Printf("Failed to authenticate user [%s]: %v", claims.Subject, err) + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + return + } + + requestCtx := c.Request.Context() + ctx := context.WithValue(requestCtx, authCtxKey, authCtx) + c.Request = c.Request.WithContext(ctx) + c.Next() + } +} + +// buildAuthContext verifies the username and creates an authContext. +func buildAuthContext(username string, c *gin.Context, _ *CerbosSvc) (*authContext, error) { + userRecord, err := db.LookupUser(c.Request.Context(), username) + if err != nil { + return nil, err + } + + newPrincipal := cerbosSDK.NewPrincipal(username). + WithRoles(userRecord.Roles...). + WithAttr("ipAddress", c.ClientIP()) + + return &authContext{username: username, principal: newPrincipal}, nil +} diff --git a/cyclops-ctrl/internal/cerbos/config/conf.yaml b/cyclops-ctrl/internal/cerbos/config/conf.yaml new file mode 100644 index 00000000..5c611844 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/config/conf.yaml @@ -0,0 +1,21 @@ +--- +server: + httpListenAddr: ":3592" + grpcListenAddr: ":3593" + #grpcListenAddr: "unix:/tmp/sock/cerbos.grpc" + #httpListenAddr: "unix:/tmp/sock/cerbos.http" + +storage: + driver: "disk" + disk: + directory: /policies + watchForChanges: true + +# audit: +# enabled: false # enable audit logging. +# accessLogsEnabled: true # Log API access attempts +# # decisionLogsEnabled: true # Log policy decisions +# backend: local # Audit backend to use. +# local: # Configuration for the local audit backend +# storagePath: /auditlogs # Path to store the data +# retentionPeriod: 168h # Records older than this will be automatically deleted \ No newline at end of file diff --git a/cyclops-ctrl/internal/cerbos/controller.go b/cyclops-ctrl/internal/cerbos/controller.go new file mode 100644 index 00000000..24ce4d33 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/controller.go @@ -0,0 +1,136 @@ +package cerbos + +import ( + "context" + "net/http" + "strings" + "time" + + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos/db" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt" + "github.com/google/uuid" +) + +var jwtKey = []byte("its_cyclops_secrets") + +type Claims struct { + Username string `json:"username"` + Roles []string `json:"roles"` + jwt.StandardClaims +} + +func Login(cerbosClient *CerbosSvc) gin.HandlerFunc { + return func(c *gin.Context) { + var credentials struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + } + if err := c.ShouldBindJSON(&credentials); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid credentials"}) + return + } + + userRecord, err := db.LookupUser(c.Request.Context(), credentials.Username) + if err != nil { + c.JSON(http.StatusOK, gin.H{"error": "invalid credentials"}) + return + } + + if credentials.Password != userRecord.Password { + c.JSON(http.StatusOK, gin.H{"error": "invalid credentials"}) + return + } + + authCtx, err := buildAuthContext(credentials.Username, c, cerbosClient) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) + return + } + + requestCtx := c.Request.Context() + ctx := context.WithValue(requestCtx, authCtxKey, authCtx) + c.Request = c.Request.WithContext(ctx) + + expirationTime := time.Now().Add(24 * time.Hour) + claims := &Claims{ + Username: userRecord.Username, + Roles: userRecord.Roles, + StandardClaims: jwt.StandardClaims{ + Issuer: "cyclops", + Subject: userRecord.Username, + ExpiresAt: expirationTime.Unix(), + NotBefore: time.Now().Unix(), + IssuedAt: time.Now().Unix(), + Id: uuid.NewString(), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString(jwtKey) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"}) + return + } + + c.SetCookie("cyclops.token", tokenString, int(time.Until(expirationTime).Seconds()), "/", "", false, true) + c.JSON(http.StatusOK, gin.H{"token": tokenString}) + } +} + +func Logout() gin.HandlerFunc { + return func(c *gin.Context) { + expirationTime := time.Now().Add(-time.Hour) + c.SetCookie("cyclops.token", "", int(expirationTime.Unix()), "/", "", false, true) + c.JSON(http.StatusOK, gin.H{"message": "Successfully logged out"}) + } +} + +func GetRole() gin.HandlerFunc { + return func(c *gin.Context) { + // Get the Authorization header + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.JSON(http.StatusUnauthorized, gin.H{"error": "No token"}) + return + } + + // Check if the header starts with "Bearer " + bearerToken := strings.Split(authHeader, " ") + if len(bearerToken) != 2 || strings.ToLower(bearerToken[0]) != "bearer" { + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"}) + return + } + + tokenString := bearerToken[1] + + claims := &Claims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + // Validate the alg is what you expect + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, jwt.ErrSignatureInvalid + } + return jwtKey, nil + }) + + if err != nil { + if err == jwt.ErrSignatureInvalid { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) + return + } + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid token"}) + return + } + + if !token.Valid { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) + return + } + + if len(claims.Roles) > 0 { + c.JSON(http.StatusOK, gin.H{"roles": claims.Roles}) + } else { + c.JSON(http.StatusOK, gin.H{"roles": []string{}}) + } + } +} diff --git a/cyclops-ctrl/internal/cerbos/db/users.go b/cyclops-ctrl/internal/cerbos/db/users.go new file mode 100644 index 00000000..e4d21c42 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/db/users.go @@ -0,0 +1,102 @@ +package db + +import ( + "context" + "errors" + "fmt" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" +) + +type UserRecord struct { + Username string + Password string + Roles []string +} + +type UsersData struct { + Users []UserRecord `yaml:"users"` +} + +type UserConfig struct { + clientset *kubernetes.Clientset +} + +// NewUserConfig creates a new UserConfig with a Kubernetes clientset. +func NewUserConfig() (*UserConfig, error) { + config := ctrl.GetConfigOrDie() + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return &UserConfig{clientset: clientset}, nil +} + +// loadUserConfig fetches the user configuration from a Kubernetes secret based on a label selector. +func (u *UserConfig) loadUserConfig(namespace, userName string) (*UserRecord, error) { + labelSelector := fmt.Sprintf("app.kubernetes.io/part-of=cyclops,app.kubernetes.io/type=user,app.kubernetes.io/name=%v", userName) + secrets, err := u.clientset.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + if err != nil { + return nil, fmt.Errorf("failed to list secrets: %w", err) + } + + if len(secrets.Items) == 0 { + return nil, errors.New("no secrets found matching the label selector") + } + + // Assuming the first secret is the one we need. + secret := secrets.Items[0] + + userRecord, err := mapSecretData(secret.Data) + if err != nil { + return nil, err + } + + return userRecord, nil +} + +// mapSecretData extracts the user record from the secret data. +func mapSecretData(secretData map[string][]byte) (*UserRecord, error) { + username, ok := secretData["username"] + if !ok { + return nil, errors.New("username key not found in secret data") + } + password, ok := secretData["password"] + if !ok { + return nil, errors.New("password key not found in secret data") + } + roles, ok := secretData["roles"] + if !ok { + return nil, errors.New("roles key not found in secret data") + } + + return &UserRecord{ + Username: string(username), + Password: string(password), + Roles: strings.Split(string(roles), ","), + }, nil +} + +// LookupUser retrieves the record for the given username from cerbos-users-config secret. +func LookupUser(ctx context.Context, userName string) (*UserRecord, error) { + userConf, err := NewUserConfig() + if err != nil { + return nil, fmt.Errorf("failed to create user config: %w", err) + } + + userRecord, err := userConf.loadUserConfig("cyclops", userName) + if err != nil { + return nil, fmt.Errorf("failed to load users: %v", err) + } + + if userRecord.Username == userName { + return userRecord, nil + } + + return nil, errors.New("user not found") +} diff --git a/cyclops-ctrl/internal/cerbos/policies/common_roles.yaml b/cyclops-ctrl/internal/cerbos/policies/common_roles.yaml new file mode 100644 index 00000000..b0361242 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/policies/common_roles.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: "api.cerbos.dev/v1" +description: |- + Common dynamic roles used within the app +derivedRoles: + name: common_roles + definitions: + - name: owner + parentRoles: ["user"] + condition: + match: + expr: request.resource.attr.ownerId == request.principal.id \ No newline at end of file diff --git a/cyclops-ctrl/internal/cerbos/policies/resource_module.yaml b/cyclops-ctrl/internal/cerbos/policies/resource_module.yaml new file mode 100644 index 00000000..15650ebb --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/policies/resource_module.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: api.cerbos.dev/v1 +resourcePolicy: + version: default + importDerivedRoles: + - common_roles + resource: module + rules: + - actions: ["*"] + effect: EFFECT_ALLOW + roles: + - admin + + - actions: ["view", "update", "list"] + effect: EFFECT_ALLOW + roles: + - user + + - actions: ["view", "list"] + effect: EFFECT_ALLOW + roles: + - readonly + + - actions: ["list", "view", "create", "delete"] + effect: EFFECT_ALLOW + roles: + - creator + + - actions: ["list", "view", "edit"] + effect: EFFECT_ALLOW + roles: + - editor \ No newline at end of file diff --git a/cyclops-ctrl/internal/cerbos/policies/resource_template_auth_rules.yaml b/cyclops-ctrl/internal/cerbos/policies/resource_template_auth_rules.yaml new file mode 100644 index 00000000..01028976 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/policies/resource_template_auth_rules.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: api.cerbos.dev/v1 +resourcePolicy: + version: default + importDerivedRoles: + - common_roles + resource: templateauthrules + rules: + - actions: ["*"] + effect: EFFECT_ALLOW + roles: + - admin + + - actions: ["view", "update", "list"] + effect: EFFECT_ALLOW + roles: + - user + + - actions: ["view", "list"] + effect: EFFECT_ALLOW + roles: + - readonly + + - actions: ["list", "view", "create", "delete"] + effect: EFFECT_ALLOW + roles: + - creator + + - actions: ["list", "view", "edit"] + effect: EFFECT_ALLOW + roles: + - editor \ No newline at end of file diff --git a/cyclops-ctrl/internal/cerbos/policies/resource_templatestore.yaml b/cyclops-ctrl/internal/cerbos/policies/resource_templatestore.yaml new file mode 100644 index 00000000..7c7426a2 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/policies/resource_templatestore.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: api.cerbos.dev/v1 +resourcePolicy: + version: default + importDerivedRoles: + - common_roles + resource: templatestore + rules: + - actions: ["*"] + effect: EFFECT_ALLOW + roles: + - admin + + - actions: ["view", "update", "list"] + effect: EFFECT_ALLOW + roles: + - user + + - actions: ["view", "list"] + effect: EFFECT_ALLOW + roles: + - readonly + + - actions: ["list", "view", "create", "delete"] + effect: EFFECT_ALLOW + roles: + - creator + + - actions: ["list", "view", "edit"] + effect: EFFECT_ALLOW + roles: + - editor \ No newline at end of file diff --git a/cyclops-ctrl/internal/cerbos/pv-values.yaml b/cyclops-ctrl/internal/cerbos/pv-values.yaml new file mode 100644 index 00000000..1f75973e --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/pv-values.yaml @@ -0,0 +1,17 @@ +volumes: + - name: cerbos-policies + hostPath: + path: /data/cerbos-policies + +volumeMounts: + - name: cerbos-policies + mountPath: /policies + readOnly: true + +cerbos: + config: + storage: + driver: "disk" + disk: + directory: /policies + watchForChanges: true \ No newline at end of file diff --git a/cyclops-ctrl/internal/controller/common.go b/cyclops-ctrl/internal/controller/common.go new file mode 100644 index 00000000..e69359a9 --- /dev/null +++ b/cyclops-ctrl/internal/controller/common.go @@ -0,0 +1,17 @@ +package controller + +// Action[Type]: list of all supported action type +const ( + ActionCreate string = "create" + ActionDelete string = "delete" + ActionUpdate string = "update" + ActionList string = "list" + ActionEdit string = "edit" +) + +// Resource[Type]: list of all supported resources type +const ( + ResourceModule string = "module" + ResourceTemplateStore string = "templatestore" + ResourceTemplateAuthRule string = "templateauthrule" +) diff --git a/cyclops-ctrl/internal/controller/modules.go b/cyclops-ctrl/internal/controller/modules.go index 3993d399..31994cec 100644 --- a/cyclops-ctrl/internal/controller/modules.go +++ b/cyclops-ctrl/internal/controller/modules.go @@ -3,16 +3,18 @@ package controller import ( "fmt" "io" + "log" "net/http" "os" "strings" "time" - "sigs.k8s.io/yaml" - + cerbosSDK "github.com/cerbos/cerbos-sdk-go/cerbos" "github.com/gin-gonic/gin" + "sigs.k8s.io/yaml" "github.com/cyclops-ui/cyclops/cyclops-ctrl/api/v1alpha1" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/mapper" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/models/dto" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/prometheus" @@ -29,6 +31,7 @@ type Modules struct { renderer *render.Renderer telemetryClient telemetry.Client monitor prometheus.Monitor + cerbos *cerbos.CerbosSvc } func NewModulesController( @@ -37,6 +40,7 @@ func NewModulesController( renderer *render.Renderer, telemetryClient telemetry.Client, monitor prometheus.Monitor, + cerbosSvc *cerbos.CerbosSvc, ) *Modules { return &Modules{ kubernetesClient: kubernetes, @@ -44,12 +48,23 @@ func NewModulesController( renderer: renderer, telemetryClient: telemetryClient, monitor: monitor, + cerbos: cerbosSvc, } } func (m *Modules) GetModule(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, ResourceModule, ctx.Param("name"), ActionList) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionList, ResourceModule, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + module, err := m.kubernetesClient.GetModule(ctx.Param("name")) if err != nil { fmt.Println(err) @@ -96,6 +111,16 @@ func (m *Modules) GetRawModuleManifest(ctx *gin.Context) { func (m *Modules) ListModules(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, ResourceModule, "*", ActionList) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionList, ResourceModule, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + modules, err := m.kubernetesClient.ListModules() if err != nil { fmt.Println(err) @@ -122,6 +147,16 @@ func (m *Modules) ListModules(ctx *gin.Context) { func (m *Modules) DeleteModule(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, ResourceModule, ctx.Param("name"), ActionDelete) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionDelete, ResourceModule, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + err := m.kubernetesClient.DeleteModule(ctx.Param("name")) if err != nil { fmt.Println(err) @@ -136,6 +171,16 @@ func (m *Modules) DeleteModule(ctx *gin.Context) { func (m *Modules) GetModuleHistory(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, ResourceModule, ctx.Param("name"), ActionList) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionList, ResourceModule, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + module, err := m.kubernetesClient.GetModule(ctx.Param("name")) if err != nil { fmt.Println(err) @@ -196,6 +241,16 @@ func (m *Modules) Manifest(ctx *gin.Context) { func (m *Modules) CurrentManifest(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, ResourceModule, ctx.Param("name"), ActionList) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionList, ResourceModule, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + module, err := m.kubernetesClient.GetModule(ctx.Param("name")) if err != nil { fmt.Println(err) @@ -231,6 +286,16 @@ func (m *Modules) CurrentManifest(ctx *gin.Context) { func (m *Modules) DeleteModuleResource(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, ResourceModule, "", ActionDelete) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + string(ActionDelete), ResourceModule, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + var request dto.DeleteResource if err := ctx.BindJSON(&request); err != nil { fmt.Println(err) @@ -258,6 +323,16 @@ func (m *Modules) CreateModule(ctx *gin.Context) { return } + allowed := m.checkPermission(ctx, ResourceModule, request.Name, ActionCreate) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionCreate, ResourceModule, request.Name, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + module, err := mapper.RequestToModule(request) if err != nil { fmt.Println(err) @@ -288,6 +363,16 @@ func (m *Modules) UpdateModule(ctx *gin.Context) { return } + allowed := m.checkPermission(ctx, ResourceModule, request.Name, ActionEdit) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + ActionEdit, ResourceModule, request.Name, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + curr, err := m.kubernetesClient.GetModule(request.Name) if err != nil { fmt.Println(err) @@ -773,6 +858,22 @@ func getTargetGeneration(generation string, module *v1alpha1.Module) (*v1alpha1. }, true } +func (m *Modules) checkPermission(ctx *gin.Context, kind, resourceName, action string) bool { + if os.Getenv("CYCLOPS_AUTHORIZATION") == "disabled" { + return true + } + resource := cerbosSDK.NewResource(kind, "new"). + WithAttr("name", resourceName). + WithAttr("action", action) + + allowed, err := m.cerbos.IsAllowed(ctx.Request.Context(), resource, action) + if err != nil { + log.Println("Error checking permissions", err.Error()) + return false + } + return allowed +} + func trimLogLine(logLine string) string { parts := strings.SplitN(logLine, " ", 2) if len(parts) > 1 { diff --git a/cyclops-ctrl/internal/controller/templates.go b/cyclops-ctrl/internal/controller/templates.go index 95923179..6c96f65c 100644 --- a/cyclops-ctrl/internal/controller/templates.go +++ b/cyclops-ctrl/internal/controller/templates.go @@ -2,13 +2,18 @@ package controller import ( "fmt" + "log" "net/http" + "os" "strconv" "strings" + cerbosSDK "github.com/cerbos/cerbos-sdk-go/cerbos" "github.com/gin-gonic/gin" json "github.com/json-iterator/go" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/mapper" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/models/dto" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/telemetry" @@ -19,17 +24,20 @@ import ( type Templates struct { templatesRepo *template.Repo kubernetesClient *k8sclient.KubernetesClient + cerbos *cerbos.CerbosSvc telemetryClient telemetry.Client } func NewTemplatesController( templatesRepo *template.Repo, kubernetes *k8sclient.KubernetesClient, + cerbosSvc *cerbos.CerbosSvc, telemetryClient telemetry.Client, ) *Templates { return &Templates{ templatesRepo: templatesRepo, kubernetesClient: kubernetes, + cerbos: cerbosSvc, telemetryClient: telemetryClient, } } @@ -101,6 +109,16 @@ func (c *Templates) GetTemplateInitialValues(ctx *gin.Context) { func (c *Templates) ListTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := c.checkPermission(ctx, ResourceTemplateStore, "*", ActionList) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + ActionList, ResourceTemplateStore, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + store, err := c.kubernetesClient.ListTemplateStore() if err != nil { ctx.JSON(http.StatusInternalServerError, dto.NewError("Error fetching templates store", err.Error())) @@ -115,6 +133,16 @@ func (c *Templates) ListTemplatesStore(ctx *gin.Context) { func (c *Templates) CreateTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := c.checkPermission(ctx, ResourceTemplateStore, "", ActionCreate) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + ActionCreate, ResourceTemplateStore, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + var templateStore *dto.TemplateStore if err := ctx.ShouldBind(&templateStore); err != nil { fmt.Println("error binding request", templateStore) @@ -158,6 +186,16 @@ func (c *Templates) CreateTemplatesStore(ctx *gin.Context) { func (c *Templates) EditTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := c.checkPermission(ctx, ResourceTemplateStore, ctx.Param("name"), ActionEdit) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + ActionEdit, ResourceTemplateStore, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + var templateStore *dto.TemplateStore if err := ctx.ShouldBind(&templateStore); err != nil { fmt.Println("error binding request", templateStore) @@ -203,6 +241,16 @@ func (c *Templates) EditTemplatesStore(ctx *gin.Context) { func (c *Templates) DeleteTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := c.checkPermission(ctx, ResourceTemplateStore, ctx.Param("name"), ActionDelete) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + ActionDelete, ResourceTemplateStore, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + templateRefName := ctx.Param("name") if err := c.kubernetesClient.DeleteTemplateStore(templateRefName); err != nil { @@ -212,3 +260,19 @@ func (c *Templates) DeleteTemplatesStore(ctx *gin.Context) { ctx.Status(http.StatusOK) } + +func (c *Templates) checkPermission(ctx *gin.Context, kind, resourceName, action string) bool { + if os.Getenv("CYCLOPS_AUTHORIZATION") == "disabled" { + return true + } + resource := cerbosSDK.NewResource(kind, "new"). + WithAttr("name", resourceName). + WithAttr("action", action) + + allowed, err := c.cerbos.IsAllowed(ctx.Request.Context(), resource, action) + if err != nil { + log.Println("Error checking permissions", err.Error()) + return false + } + return allowed +} diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 89c59091..34a08aa3 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -1,16 +1,19 @@ package handler import ( - "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/controller/sse" - "github.com/gin-gonic/gin" "net/http" + "os" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/controller/sse" + + cerbos "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/controller" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/prometheus" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/telemetry" templaterepo "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/template" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/template/render" "github.com/cyclops-ui/cyclops/cyclops-ctrl/pkg/cluster/k8sclient" + "github.com/gin-gonic/gin" ) type Handler struct { @@ -19,6 +22,7 @@ type Handler struct { templatesRepo *templaterepo.Repo k8sClient *k8sclient.KubernetesClient renderer *render.Renderer + cerbosClient *cerbos.CerbosSvc telemetryClient telemetry.Client monitor prometheus.Monitor @@ -28,6 +32,7 @@ func New( templatesRepo *templaterepo.Repo, kubernetesClient *k8sclient.KubernetesClient, renderer *render.Renderer, + cerbosSvc *cerbos.CerbosSvc, telemetryClient telemetry.Client, monitor prometheus.Monitor, ) (*Handler, error) { @@ -35,6 +40,7 @@ func New( templatesRepo: templatesRepo, k8sClient: kubernetesClient, renderer: renderer, + cerbosClient: cerbosSvc, telemetryClient: telemetryClient, monitor: monitor, router: gin.New(), @@ -44,10 +50,12 @@ func New( func (h *Handler) Start() error { gin.SetMode(gin.DebugMode) - templatesController := controller.NewTemplatesController(h.templatesRepo, h.k8sClient, h.telemetryClient) - modulesController := controller.NewModulesController(h.templatesRepo, h.k8sClient, h.renderer, h.telemetryClient, h.monitor) + templatesController := controller.NewTemplatesController(h.templatesRepo, h.k8sClient, h.cerbosClient, h.telemetryClient) + modulesController := controller.NewModulesController(h.templatesRepo, h.k8sClient, h.renderer, h.telemetryClient, h.monitor, h.cerbosClient) clusterController := controller.NewClusterController(h.k8sClient) + // _ = os.Getenv("CYCLOPS_AUTHORIZATION") + h.router = gin.New() server := sse.NewServer(h.k8sClient) @@ -57,6 +65,16 @@ func (h *Handler) Start() error { h.router.GET("/ping", h.pong()) + // authentication + h.router.POST("/login", cerbos.Login(h.cerbosClient)) + + if os.Getenv("CYCLOPS_AUTHORIZATION") == "enabled" { + h.router.Use(cerbos.AuthMiddleware(h.cerbosClient)) + } + + h.router.POST("/logout", cerbos.Logout()) + h.router.GET("/getrole", cerbos.GetRole()) + // templates h.router.GET("/templates", templatesController.GetTemplate) h.router.GET("/templates/initial", templatesController.GetTemplateInitialValues) diff --git a/cyclops-ui/.dockerignore b/cyclops-ui/.dockerignore index 4b904442..57dd66a6 100644 --- a/cyclops-ui/.dockerignore +++ b/cyclops-ui/.dockerignore @@ -1 +1,2 @@ ./node_modules +*.env \ No newline at end of file diff --git a/cyclops-ui/.env b/cyclops-ui/.env index 4494f1b2..249947f5 100644 --- a/cyclops-ui/.env +++ b/cyclops-ui/.env @@ -1,3 +1,4 @@ NODE_ENV=production +REACT_APP_CYCLOPS_AUTHORIZATION=enabled REACT_APP_CYCLOPS_CTRL_HOST=http://localhost:8888 REACT_APP_ENABLE_STREAMING=true diff --git a/cyclops-ui/env.js b/cyclops-ui/env.js index ac6f30fc..cdd143c6 100644 --- a/cyclops-ui/env.js +++ b/cyclops-ui/env.js @@ -2,6 +2,7 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "${NODE_ENV}", REACT_APP_CYCLOPS_CTRL_HOST: "${REACT_APP_CYCLOPS_CTRL_HOST}", + REACT_APP_CYCLOPS_AUTHORIZATION: "${REACT_APP_CYCLOPS_AUTHORIZATION}", REACT_APP_VERSION: "${REACT_APP_VERSION}", REACT_APP_ENABLE_STREAMING: "${REACT_APP_ENABLE_STREAMING}", }; diff --git a/cyclops-ui/package.json b/cyclops-ui/package.json index fb61c632..d2388a6c 100644 --- a/cyclops-ui/package.json +++ b/cyclops-ui/package.json @@ -26,6 +26,8 @@ "draft-js": "^0.11.7", "fetch-jsonp": "^1.2.1", "http-proxy-middleware": "^2.0.6", + "js-cookie": "^3.0.5", + "jwt-decode": "^4.0.0", "react": "^18.0.0", "react-ace": "^10.1.0", "react-ace-editor": "^0.0.3", diff --git a/cyclops-ui/src/App.tsx b/cyclops-ui/src/App.tsx index 37182d76..946e3467 100644 --- a/cyclops-ui/src/App.tsx +++ b/cyclops-ui/src/App.tsx @@ -2,11 +2,29 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; import routes from "./routes"; import Page404 from "./components/pages/Page404"; import AppLayout from "./components/layouts/AppLayout"; +import { useAuth } from "./context/AuthContext"; +import Login from "./components/pages/Login/Login"; +import { useEffect, useState } from "react"; export default function App() { + const { isAuthenticated, checkAuthentication } = useAuth(); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const initAuth = async () => { + await checkAuthentication(); + setIsLoading(false); + }; + initAuth(); + }, [checkAuthentication]); + + if (isLoading) { + return
Loading...
; // Or a more sophisticated loading component + } + const router = createBrowserRouter([ { - element: , + element: isAuthenticated ? : , errorElement: , children: routes, }, diff --git a/cyclops-ui/src/components/layouts/AppLayout.tsx b/cyclops-ui/src/components/layouts/AppLayout.tsx index a541e00c..acce673c 100644 --- a/cyclops-ui/src/components/layouts/AppLayout.tsx +++ b/cyclops-ui/src/components/layouts/AppLayout.tsx @@ -1,11 +1,29 @@ +import React from "react"; import { Outlet } from "react-router-dom"; import SideNav from "./Sidebar"; import { Suspense } from "react"; -import Sider from "antd/es/layout/Sider"; -import { Content, Header } from "antd/es/layout/layout"; -import { ConfigProvider, Layout } from "antd"; +import { Layout, ConfigProvider, Menu, Dropdown, Avatar, Space } from "antd"; +import { UserOutlined, LogoutOutlined } from "@ant-design/icons"; +import { useAuth } from "../../context/AuthContext"; + +const { Sider, Content, Header } = Layout; export default function AppLayout() { + // Replace these with actual user data + const { userName, userRole, logout } = useAuth(); + + const handleLogout = () => { + logout(); + }; + + const dropdownMenu = ( + + }> + Logout + + + ); + return ( + > + + {userName} + ({userRole}) + + } + /> + + + { const location = useLocation().pathname.split("/")[1]; + const { logout } = useAuth(); const sidebarItems: MenuProps["items"] = [ { @@ -42,9 +45,7 @@ const SideNav = () => { }; return ( -
+ ); }; diff --git a/cyclops-ui/src/components/pages/Login/Login.tsx b/cyclops-ui/src/components/pages/Login/Login.tsx new file mode 100644 index 00000000..7567d508 --- /dev/null +++ b/cyclops-ui/src/components/pages/Login/Login.tsx @@ -0,0 +1,156 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { Alert, Button, ConfigProvider, Form } from "antd"; +import axios from "axios"; +import { useAuth } from "../../../context/AuthContext"; +import Cookies from "js-cookie"; +import { + UserOutlined, + EyeInvisibleOutlined, + EyeTwoTone, +} from "@ant-design/icons"; +import { Input } from "antd"; +import styles from "./styles.module.css"; +import { jwtDecode } from "jwt-decode"; + +interface LoginResponse { + error?: string; + token?: string; +} + +interface LoginRequest { + username: string; + password: string; +} + +interface DecodedToken { + sub: string; + exp: number; + iat: number; +} + +interface RoleResponse { + roles: string[]; +} + +const Login = () => { + const navigate = useNavigate(); + const { login } = useAuth(); + const [error, setError] = useState({ + message: "", + description: "", + }); + + const handleSubmit = async (request: LoginRequest) => { + try { + const response = await axios.post("/api/login", request); + if (response.data?.token) { + const decodedToken = jwtDecode(response.data.token); + Cookies.set("_isAuthenticated", "true"); + Cookies.set("token", response.data.token); + const roleResponse = await axios.get("/api/getrole", { + headers: { + Authorization: `Bearer ${response.data.token}`, + }, + }); + const userName = decodedToken.sub; + const userRole = roleResponse.data.roles[0]; + login(userName, userRole); + navigate("/"); + } else { + setError({ + message: "Authentication Failed", + description: `${response.data.error}`, + }); + Cookies.set("_isAuthenticated", "false"); + } + } catch (err) { + console.error(err); + setError({ + message: "Login Error", + description: "An error occurred during login. Please try again.", + }); + } + }; + + return ( +
+
+
+ +
+
+
+
+ +
+

+ +

+ {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ marginBottom: "20px" }} + /> + )} + + } + /> + + + + visible ? : + } + /> + + + +
+
+
+
+ ); +}; + +export default Login; diff --git a/cyclops-ui/src/components/pages/Login/cyclops-logo.png b/cyclops-ui/src/components/pages/Login/cyclops-logo.png new file mode 100644 index 00000000..13077cd6 Binary files /dev/null and b/cyclops-ui/src/components/pages/Login/cyclops-logo.png differ diff --git a/cyclops-ui/src/components/pages/Login/styles.module.css b/cyclops-ui/src/components/pages/Login/styles.module.css new file mode 100644 index 00000000..4f6f3eba --- /dev/null +++ b/cyclops-ui/src/components/pages/Login/styles.module.css @@ -0,0 +1,71 @@ +.login_page { + display: flex; +} + +.left_banner { + background-color: rgb(6, 25, 61); + background-image: url("https://cyclops-ui.com/assets/images/yaml_background-cc1699351922ead2cae5da1dbb75cbd8.png"); + background-position: 0 -10rem; + background-repeat: no-repeat; + height: 100vh; + width: 120%; +} + +.right_banner { + height: 100vh; + width: 80%; +} + +.cyclops_image_container { + display: flex; + flex-direction: column; + align-items: center; +} + +.cyclops_brand_image { + width: 50%; + position: fixed; + bottom: 0; +} + +.login_header { + text-align: center; +} + +.cyclops_login_header_login { + width: 80%; + margin-bottom: 16px; +} + +.login_form { + margin-top: 25vh; +} + +.login_container { + width: 80%; + padding: 0 5%; + margin: 0 10%; +} + +.field_container { + margin: 1rem 0; + width: 100%; +} + +.submit_button { + border: none; + width: 100%; +} + +.submit_buttom:hover { + background-color: rgb(249, 250, 251); +} + +.submit_buttom:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.submit_buttom:focus-visible { + box-shadow: none; +} diff --git a/cyclops-ui/src/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx new file mode 100644 index 00000000..5f7c065d --- /dev/null +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -0,0 +1,122 @@ +import React, { + createContext, + useContext, + useState, + useEffect, + ReactNode, +} from "react"; +import Cookies from "js-cookie"; +import { jwtDecode } from "jwt-decode"; +import axios from "axios"; + +interface AuthContextType { + isAuthenticated: boolean; + userName: string | null; + userRole: string | null; + login: (userName: string, userRole: string) => void; + logout: () => void; + checkAuthentication: () => Promise; +} + +interface DecodedToken { + sub: string; + exp: number; + iat: number; +} + +interface RoleResponse { + roles: string[]; +} + +const AuthContext = createContext(undefined); + +export const AuthProvider: React.FC<{ children: ReactNode }> = ({ + children, +}) => { + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [userName, setuserName] = useState(null); + const [userRole, setuserRole] = useState(null); + + const checkAuthentication = async () => { + const isAuthorizationEnabled = + process.env.REACT_APP_CYCLOPS_AUTHORIZATION === "enabled"; + const token = Cookies.get("token"); + + if (!isAuthorizationEnabled) { + setIsAuthenticated(true); + } else if (token) { + try { + const decodedToken = jwtDecode(token); + if (decodedToken.exp * 1000 > Date.now()) { + setIsAuthenticated(true); + setuserName(decodedToken.sub); + const getuserRole = async () => { + try { + const roleResponse = await axios.get( + "/api/getrole", + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ); + setuserRole(roleResponse.data.roles[0]); + } catch (err) { + console.log(err); + } + }; + getuserRole(); + } else { + // Token expired + logout(); + } + } catch (error) { + console.error("Error decoding token:", error); + logout(); + } + } else { + setIsAuthenticated(false); + } + }; + + useEffect(() => { + checkAuthentication(); + }, []); + + const login = (newuserName: string, newuserRole: string) => { + setIsAuthenticated(true); + setuserName(newuserName); + setuserRole(newuserRole); + }; + + const logout = () => { + Cookies.remove("token"); + Cookies.set("_isAuthenticated", "false"); + setIsAuthenticated(false); + setuserName(null); + window.location.reload(); + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +}; diff --git a/cyclops-ui/src/index.tsx b/cyclops-ui/src/index.tsx index 46c6a2e7..f492b1ff 100644 --- a/cyclops-ui/src/index.tsx +++ b/cyclops-ui/src/index.tsx @@ -2,6 +2,7 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "antd/dist/reset.css"; import App from "./App"; +import { AuthProvider } from "./context/AuthContext"; import reportWebVitals from "./reportWebVitals"; const container = document.getElementById("root"); @@ -9,7 +10,9 @@ const root = createRoot(container!); root.render( - + + + , ); diff --git a/cyclops-ui/yarn.lock b/cyclops-ui/yarn.lock index 746889e5..5b1eac15 100644 --- a/cyclops-ui/yarn.lock +++ b/cyclops-ui/yarn.lock @@ -9449,6 +9449,11 @@ js-cookie@^2.2.1: resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + js-logger@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/js-logger/-/js-logger-1.6.1.tgz#8f09671b515e4a6f31dced8fdb8923432e2c60af" @@ -9619,6 +9624,11 @@ jsonpointer@^5.0.0: object.assign "^4.1.4" object.values "^1.1.6" +jwt-decode@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b" + integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== + kdbush@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0" @@ -13436,16 +13446,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13537,7 +13538,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -13551,13 +13552,6 @@ strip-ansi@^3.0.0: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -14885,7 +14879,7 @@ workbox-window@6.6.1: "@types/trusted-types" "^2.0.2" workbox-core "6.6.1" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -14903,15 +14897,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index 7a383d17..c25e4cc2 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -351,6 +351,8 @@ spec: value: "true" - name: NODE_ENV value: production + - name: REACT_APP_CYCLOPS_AUTHORIZATION + value: disabled - name: NODE_OPTIONS value: --openssl-legacy-provider restartPolicy: Always @@ -412,6 +414,10 @@ spec: env: - name: PORT value: "8080" + - name: CYCLOPS_AUTHORIZATION + value: disabled + - name: CERBOS_URL + value: localhost:3592 livenessProbe: httpGet: path: /healthz @@ -424,6 +430,33 @@ spec: port: 8082 initialDelaySeconds: 5 periodSeconds: 10 + - name: cyclops-cerbos + image: ghcr.io/cerbos/cerbos:latest + imagePullPolicy: IfNotPresent + args: + - "server" + - "--config=/config/config.yaml" + - "--log-level=INFO" + volumeMounts: + - name: sock + mountPath: /sock + - name: config + mountPath: /config + readOnly: true + - name: policies + mountPath: /policies + volumes: + - name: sock + emptyDir: {} + - name: config + configMap: + name: cyclops-cerbos-sidecar-config + - name: certs + secret: + secretName: cyclops-cerbos-sidecar + - name: policies + configMap: + name: cyclops-cerbos-sidecar-policies --- apiVersion: v1 kind: Service @@ -463,3 +496,145 @@ spec: policyTypes: - Ingress - Egress +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cyclops-cerbos-sidecar-config + namespace: cyclops + labels: + app.kubernetes.io/name: cyclops-cerbos-sidecar + app.kubernetes.io/component: cerbos + app.kubernetes.io/version: "0.0.1" +data: + "config.yaml": |- + server: + httpListenAddr: ":3592" + grpcListenAddr: ":3593" + storage: + driver: disk + disk: + directory: /policies + watchForChanges: true +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cyclops-cerbos-sidecar-policies + namespace: cyclops + labels: + app.kubernetes.io/name: cyclops-cerbos-sidecar + app.kubernetes.io/component: cerbos + app.kubernetes.io/version: "0.0.1" +data: + "common_roles.yaml": |- + apiVersion: "api.cerbos.dev/v1" + description: |- + Common dynamic roles used within the app + derivedRoles: + name: common_roles + definitions: + - name: owner + parentRoles: ["user"] + condition: + match: + expr: request.resource.attr.ownerId == request.principal.id + + "resource_module.yaml": |- + apiVersion: api.cerbos.dev/v1 + resourcePolicy: + version: default + importDerivedRoles: + - common_roles + resource: module + rules: + - actions: ["*"] + effect: EFFECT_ALLOW + roles: + - admin + + - actions: ["view", "update", "list"] + effect: EFFECT_ALLOW + roles: + - user + + - actions: ["view", "list"] + effect: EFFECT_ALLOW + roles: + - readonly + + - actions: ["list", "view", "create", "delete"] + effect: EFFECT_ALLOW + roles: + - creator + + - actions: ["list", "view", "edit"] + effect: EFFECT_ALLOW + roles: + - editor + + "resource_template_auth_rules.yaml": |- + apiVersion: api.cerbos.dev/v1 + resourcePolicy: + version: default + importDerivedRoles: + - common_roles + resource: templateauthrules + rules: + - actions: ["*"] + effect: EFFECT_ALLOW + roles: + - admin + + - actions: ["view", "update", "list"] + effect: EFFECT_ALLOW + roles: + - user + + - actions: ["view", "list"] + effect: EFFECT_ALLOW + roles: + - readonly + + - actions: ["list", "view", "create", "delete"] + effect: EFFECT_ALLOW + roles: + - creator + + - actions: ["list", "view", "edit"] + effect: EFFECT_ALLOW + roles: + - editor + + "resource_templatestore.yaml": |- + apiVersion: api.cerbos.dev/v1 + resourcePolicy: + version: default + importDerivedRoles: + - common_roles + resource: templatestore + rules: + - actions: ["*"] + effect: EFFECT_ALLOW + roles: + - admin + + - actions: ["view", "update", "list"] + effect: EFFECT_ALLOW + roles: + - user + + - actions: ["view", "list"] + effect: EFFECT_ALLOW + roles: + - readonly + + - actions: ["list", "view", "create", "delete"] + effect: EFFECT_ALLOW + roles: + - creator + + - actions: ["list", "view", "edit"] + effect: EFFECT_ALLOW + roles: + - editor