From bcb5393a34ecf933014a7fb2d25c75b649839388 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:04:10 +0530 Subject: [PATCH 01/44] ft(ui): login component need enhancement --- cyclops-ui/src/components/pages/Login.tsx | 62 +++++++++++++++++++++++ cyclops-ui/src/routes/PathConstants.tsx | 1 + cyclops-ui/src/routes/index.tsx | 2 + 3 files changed, 65 insertions(+) create mode 100644 cyclops-ui/src/components/pages/Login.tsx diff --git a/cyclops-ui/src/components/pages/Login.tsx b/cyclops-ui/src/components/pages/Login.tsx new file mode 100644 index 00000000..17b13349 --- /dev/null +++ b/cyclops-ui/src/components/pages/Login.tsx @@ -0,0 +1,62 @@ +import React, { useState } from "react"; +import axios from "axios"; + +const Login = () => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + + const handleUsernameChange = (e: React.ChangeEvent) => { + setUsername(e.target.value); + }; + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + await axios + .post("/api/login", { + username, + password, + }) + .then((response) => { + // response + }) + .catch((err) => console.error(err)); + + setUsername(""); + setPassword(""); + }; + + return ( +
+

Login

+
+
+ + +
+
+ + +
+ +
+
+ ); +}; + +export default Login; diff --git a/cyclops-ui/src/routes/PathConstants.tsx b/cyclops-ui/src/routes/PathConstants.tsx index e272b1ad..85c5eada 100644 --- a/cyclops-ui/src/routes/PathConstants.tsx +++ b/cyclops-ui/src/routes/PathConstants.tsx @@ -8,6 +8,7 @@ const PathConstants = { NODES: "/nodes", NODE_GET: "/nodes/:nodeName", TEMPLATES: "/templates", + LOGIN: "/login", }; export default PathConstants; diff --git a/cyclops-ui/src/routes/index.tsx b/cyclops-ui/src/routes/index.tsx index 1b35f866..e1ddd4b3 100644 --- a/cyclops-ui/src/routes/index.tsx +++ b/cyclops-ui/src/routes/index.tsx @@ -14,6 +14,7 @@ const NodeDetails = React.lazy(() => import("../components/pages/NodeDetails")); const Templates = React.lazy( () => import("../components/pages/TemplateStore/TemplateStore"), ); +const Login = React.lazy(() => import("../components/pages/Login")); const routes = [ { path: PathConstants.HOME, element: }, @@ -25,6 +26,7 @@ const routes = [ { path: PathConstants.NODES, element: }, { path: PathConstants.NODE_GET, element: }, { path: PathConstants.TEMPLATES, element: }, + { path: PathConstants.LOGIN, element: }, ]; export default routes; From 449260bb2b2900873abdf24cffaf8a6cfff231d7 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:05:12 +0530 Subject: [PATCH 02/44] ft(ctrl): cerbos resource policies --- .../cerbos/policies/common_roles.yaml | 12 +++++++ .../cerbos/policies/resource_module.yaml | 32 +++++++++++++++++++ .../resource_template_auth_rules.yaml | 32 +++++++++++++++++++ .../policies/resource_templatestore.yaml | 32 +++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 cyclops-ctrl/internal/cerbos/policies/common_roles.yaml create mode 100644 cyclops-ctrl/internal/cerbos/policies/resource_module.yaml create mode 100644 cyclops-ctrl/internal/cerbos/policies/resource_template_auth_rules.yaml create mode 100644 cyclops-ctrl/internal/cerbos/policies/resource_templatestore.yaml 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 From 3ef0321c102b979093238c4ef4d83d9b1d53dea5 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:05:35 +0530 Subject: [PATCH 03/44] ft(ctrl): login controller --- cyclops-ctrl/internal/cerbos/controller.go | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 cyclops-ctrl/internal/cerbos/controller.go diff --git a/cyclops-ctrl/internal/cerbos/controller.go b/cyclops-ctrl/internal/cerbos/controller.go new file mode 100644 index 00000000..01a1b49d --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/controller.go @@ -0,0 +1,74 @@ +package cerbos + +import ( + "context" + "net/http" + "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 { + 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 request"}) + return + } + + userRecord, err := db.LookupUser(c.Request.Context(), credentials.Username) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "user does not exist."}) + return + } + + if credentials.Password != userRecord.Password { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid credentials"}) + return + } + + authCtx, err := buildAuthContext(credentials.Username, c, cerbosClient) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + 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{ + StandardClaims: jwt.StandardClaims{ + Issuer: "cyclops", + Subject: credentials.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(time.Now()).Seconds()), "/", "", false, true) + c.JSON(http.StatusOK, gin.H{"token": tokenString}) + } +} From 9c7a611f99fe5a8c6196f95fa670367a8a86bfd6 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:06:00 +0530 Subject: [PATCH 04/44] ft(ctrl): cerbos resource authorization checks added --- cyclops-ctrl/internal/controller/common.go | 30 ++++++ cyclops-ctrl/internal/controller/modules.go | 98 +++++++++++++++++++ cyclops-ctrl/internal/controller/templates.go | 58 +++++++++++ 3 files changed, 186 insertions(+) create mode 100644 cyclops-ctrl/internal/controller/common.go diff --git a/cyclops-ctrl/internal/controller/common.go b/cyclops-ctrl/internal/controller/common.go new file mode 100644 index 00000000..96e7dcd7 --- /dev/null +++ b/cyclops-ctrl/internal/controller/common.go @@ -0,0 +1,30 @@ +package controller + +// ActionType: list of all supported action type +type ActionType struct { + create string + delete string + update string + list string + edit string +} + +var Action = ActionType{ + create: "create", + delete: "delete", + update: "update", + list: "list", + edit: "edit", +} + +type ResourceType struct { + module string + templatestore string + templateauthrule string +} + +var Resource = ResourceType{ + module: "module", + templatestore: "templatestore", + templateauthrule: "templateauthrule", +} diff --git a/cyclops-ctrl/internal/controller/modules.go b/cyclops-ctrl/internal/controller/modules.go index 942190b3..f00bd26c 100644 --- a/cyclops-ctrl/internal/controller/modules.go +++ b/cyclops-ctrl/internal/controller/modules.go @@ -6,9 +6,11 @@ import ( "os" "strings" + cerbosSDK "github.com/cerbos/cerbos-sdk-go/cerbos" "github.com/gin-gonic/gin" "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/cluster/k8sclient" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/mapper" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/models/dto" @@ -24,6 +26,7 @@ type Modules struct { renderer *render.Renderer telemetryClient telemetry.Client monitor prometheus.Monitor + cerbos *cerbos.CerbosSvc } func NewModulesController( @@ -32,6 +35,7 @@ func NewModulesController( renderer *render.Renderer, telemetryClient telemetry.Client, monitor prometheus.Monitor, + cerbosSvc *cerbos.CerbosSvc, ) *Modules { return &Modules{ kubernetesClient: kubernetes, @@ -39,12 +43,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, Resource.module, ctx.Param("name"), Action.list) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.list, Resource.module, 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) @@ -65,6 +80,16 @@ func (m *Modules) GetModule(ctx *gin.Context) { func (m *Modules) ListModules(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") + allowed := m.checkPermission(ctx, Resource.module, "*", Action.list) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.list, Resource.module, ctx.Param("name"), + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + modules, err := m.kubernetesClient.ListModules() if err != nil { fmt.Println(err) @@ -91,6 +116,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, Resource.module, ctx.Param("name"), Action.delete) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.delete, Resource.module, 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) @@ -105,6 +140,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, Resource.module, ctx.Param("name"), Action.list) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.list, Resource.module, 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) @@ -161,6 +206,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, Resource.module, ctx.Param("name"), Action.list) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.list, Resource.module, 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) @@ -195,6 +250,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, Resource.module, "", Action.delete) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.delete, Resource.module, 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) @@ -222,6 +287,16 @@ func (m *Modules) CreateModule(ctx *gin.Context) { return } + allowed := m.checkPermission(ctx, Resource.module, request.Name, Action.create) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.create, Resource.module, request.Name, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + module, err := mapper.RequestToModule(request) if err != nil { fmt.Println(err) @@ -252,6 +327,16 @@ func (m *Modules) UpdateModule(ctx *gin.Context) { return } + allowed := m.checkPermission(ctx, Resource.module, request.Name, Action.edit) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s named %s", + Action.edit, Resource.module, 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) @@ -622,3 +707,16 @@ func getTargetGeneration(generation string, module *v1alpha1.Module) (*v1alpha1. Status: module.Status, }, true } + +func (m *Modules) checkPermission(ctx *gin.Context, kind, resourceName, action string) bool { + resource := cerbosSDK.NewResource(kind, "new"). + WithAttr("name", resourceName). + WithAttr("action", action) + + allowed, err := m.cerbos.IsAllowed(ctx.Request.Context(), resource, action) + if err != nil { + ctx.JSON(http.StatusInternalServerError, dto.NewError("Error checking permissions", err.Error())) + return false + } + return allowed +} diff --git a/cyclops-ctrl/internal/controller/templates.go b/cyclops-ctrl/internal/controller/templates.go index b2f96250..2c49844a 100644 --- a/cyclops-ctrl/internal/controller/templates.go +++ b/cyclops-ctrl/internal/controller/templates.go @@ -6,8 +6,10 @@ import ( "strconv" "strings" + cerbosSDK "github.com/cerbos/cerbos-sdk-go/cerbos" "github.com/gin-gonic/gin" + "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cluster/k8sclient" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/mapper" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/models/dto" @@ -17,15 +19,18 @@ import ( type Templates struct { templatesRepo *template.Repo kubernetesClient *k8sclient.KubernetesClient + cerbos *cerbos.CerbosSvc } func NewTemplatesController( templatesRepo *template.Repo, kubernetes *k8sclient.KubernetesClient, + cerbosSvc *cerbos.CerbosSvc, ) *Templates { return &Templates{ templatesRepo: templatesRepo, kubernetesClient: kubernetes, + cerbos: cerbosSvc, } } @@ -89,6 +94,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, Resource.templatestore, "*", Action.list) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + Action.list, Resource.templatestore, + ) + 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())) @@ -103,6 +118,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, Resource.templatestore, "", Action.create) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + Action.create, Resource.templatestore, + ) + 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) @@ -139,6 +164,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, Resource.templatestore, ctx.Param("name"), Action.edit) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + Action.edit, Resource.templatestore, + ) + 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) @@ -177,6 +212,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, Resource.templatestore, ctx.Param("name"), Action.delete) + if !allowed { + errorMessage := fmt.Sprintf( + "User does not have permission to perform '%s' action on %s", + Action.delete, Resource.templatestore, + ) + ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) + return + } + templateRefName := ctx.Param("name") if err := c.kubernetesClient.DeleteTemplateStore(templateRefName); err != nil { @@ -186,3 +231,16 @@ func (c *Templates) DeleteTemplatesStore(ctx *gin.Context) { ctx.Status(http.StatusOK) } + +func (c *Templates) checkPermission(ctx *gin.Context, kind, resourceName, action string) bool { + resource := cerbosSDK.NewResource(kind, "new"). + WithAttr("name", resourceName). + WithAttr("action", action) + + allowed, err := c.cerbos.IsAllowed(ctx.Request.Context(), resource, action) + if err != nil { + ctx.JSON(http.StatusInternalServerError, dto.NewError("Error checking permissions", err.Error())) + return false + } + return allowed +} From 78087e2f058b6488568e9038010ef9feaf9ef21e Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:06:57 +0530 Subject: [PATCH 05/44] ft(ctrl): cerbos integration added --- cyclops-ctrl/.env | 1 + cyclops-ctrl/cmd/main/main.go | 13 +- cyclops-ctrl/internal/cerbos/cerbos.go | 126 ++++++++++++++++++ cyclops-ctrl/internal/cerbos/config/conf.yaml | 21 +++ cyclops-ctrl/internal/cerbos/db/users.go | 103 ++++++++++++++ cyclops-ctrl/internal/cerbos/pv-values.yaml | 17 +++ cyclops-ctrl/internal/handler/handler.go | 13 +- 7 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 cyclops-ctrl/internal/cerbos/cerbos.go create mode 100644 cyclops-ctrl/internal/cerbos/config/conf.yaml create mode 100644 cyclops-ctrl/internal/cerbos/db/users.go create mode 100644 cyclops-ctrl/internal/cerbos/pv-values.yaml diff --git a/cyclops-ctrl/.env b/cyclops-ctrl/.env index 3dc5b89b..e1310c2f 100644 --- a/cyclops-ctrl/.env +++ b/cyclops-ctrl/.env @@ -1,2 +1,3 @@ DISABLE_TELEMETRY=true PORT=8888 +CERBOS_URL='localhost:3593' \ No newline at end of file diff --git a/cyclops-ctrl/cmd/main/main.go b/cyclops-ctrl/cmd/main/main.go index 036b5f6e..c6d7de6a 100644 --- a/cyclops-ctrl/cmd/main/main.go +++ b/cyclops-ctrl/cmd/main/main.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "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/cluster/k8sclient" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/handler" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/modulecontroller" @@ -73,6 +74,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") @@ -80,7 +91,7 @@ func main() { renderer := render.NewRenderer(k8sClient) - 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/internal/cerbos/cerbos.go b/cyclops-ctrl/internal/cerbos/cerbos.go new file mode 100644 index 00000000..24806273 --- /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 access"}) + 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/db/users.go b/cyclops-ctrl/internal/cerbos/db/users.go new file mode 100644 index 00000000..1b085bc2 --- /dev/null +++ b/cyclops-ctrl/internal/cerbos/db/users.go @@ -0,0 +1,103 @@ +package db + +import ( + "context" + "errors" + "fmt" + "log" + "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 { + log.Fatalf("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/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/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 862d15aa..e2d6b2fd 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -5,6 +5,7 @@ import ( "github.com/gin-gonic/gin" + cerbos "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cerbos" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/cluster/k8sclient" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/controller" "github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/prometheus" @@ -19,6 +20,7 @@ type Handler struct { templatesRepo *templaterepo.Repo k8sClient *k8sclient.KubernetesClient renderer *render.Renderer + cerbosClient *cerbos.CerbosSvc telemetryClient telemetry.Client monitor prometheus.Monitor @@ -28,6 +30,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 +38,7 @@ func New( templatesRepo: templatesRepo, k8sClient: kubernetesClient, renderer: renderer, + cerbosClient: cerbosSvc, telemetryClient: telemetryClient, monitor: monitor, router: gin.New(), @@ -44,14 +48,19 @@ func New( func (h *Handler) Start() error { gin.SetMode(gin.DebugMode) - templatesController := controller.NewTemplatesController(h.templatesRepo, h.k8sClient) - modulesController := controller.NewModulesController(h.templatesRepo, h.k8sClient, h.renderer, h.telemetryClient, h.monitor) + templatesController := controller.NewTemplatesController(h.templatesRepo, h.k8sClient, h.cerbosClient) + modulesController := controller.NewModulesController(h.templatesRepo, h.k8sClient, h.renderer, h.telemetryClient, h.monitor, h.cerbosClient) clusterController := controller.NewClusterController(h.k8sClient) h.router = gin.New() h.router.GET("/ping", h.pong()) + // authentication + h.router.POST("/login", cerbos.Login(h.cerbosClient)) + + h.router.Use(cerbos.AuthMiddleware(h.cerbosClient)) + // templates h.router.GET("/templates", templatesController.GetTemplate) h.router.GET("/templates/initial", templatesController.GetTemplateInitialValues) From 8bcd5d54bf86df0682d6177c8e1b0181eae57945 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:07:19 +0530 Subject: [PATCH 06/44] added cerbos container for local development --- cyclops-ctrl/docker-compose.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cyclops-ctrl/docker-compose.yaml b/cyclops-ctrl/docker-compose.yaml index 021d0490..c6f8df47 100644 --- a/cyclops-ctrl/docker-compose.yaml +++ b/cyclops-ctrl/docker-compose.yaml @@ -1,7 +1,19 @@ -version: "3.0" +version: "3.8" + services: redis: container_name: redis image: redis ports: - - 6379:6379 \ No newline at end of file + - 6379:6379 + + cerbos: + container_name: cerbos + image: ghcr.io/cerbos/cerbos:latest + ports: + - "3592:3592" + - "3593:3593" + volumes: + - ./internal/cerbos/config:/config + - ./internal/cerbos/policies:/policies + command: server --config=/config/conf.yaml \ No newline at end of file From 0d65d4c9409eda3ae343dca1cd73f09c57b634bd Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 22 Jun 2024 21:07:30 +0530 Subject: [PATCH 07/44] updated dependencies --- cyclops-ctrl/go.mod | 59 +++++++++++++------ cyclops-ctrl/go.sum | 139 +++++++++++++++++++++++++++++++------------- 2 files changed, 141 insertions(+), 57 deletions(-) diff --git a/cyclops-ctrl/go.mod b/cyclops-ctrl/go.mod index a9c5b0c2..c105d993 100644 --- a/cyclops-ctrl/go.mod +++ b/cyclops-ctrl/go.mod @@ -4,11 +4,13 @@ go 1.21 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.3.0 + 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,17 +29,21 @@ require ( ) 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/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect 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 @@ -45,6 +51,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/docker/cli v24.0.6+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker v24.0.9+incompatible // indirect @@ -59,6 +66,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 @@ -73,22 +81,32 @@ 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.3 // 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 @@ -104,15 +122,21 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc5 // 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.10.1 // 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 +151,22 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.21.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.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.15.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.19.0 // indirect gomodules.xyz/jsonpatch/v2 v2.3.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 gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/cyclops-ctrl/go.sum b/cyclops-ctrl/go.sum index 8a718454..950ae669 100644 --- a/cyclops-ctrl/go.sum +++ b/cyclops-ctrl/go.sum @@ -1,9 +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-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +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= @@ -18,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= @@ -26,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/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -36,6 +42,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= @@ -46,6 +54,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= @@ -74,6 +88,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= @@ -107,6 +123,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.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= @@ -121,6 +139,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= @@ -170,19 +190,23 @@ 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.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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= @@ -195,6 +219,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= @@ -202,6 +228,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= @@ -209,12 +239,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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= @@ -246,6 +278,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= @@ -258,6 +302,8 @@ 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= @@ -279,6 +325,7 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P 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.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= @@ -287,6 +334,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-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +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= @@ -297,6 +348,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/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= @@ -321,6 +374,10 @@ github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+Pymzi github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= 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= @@ -339,6 +396,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= @@ -354,8 +413,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= @@ -412,20 +471,21 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +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-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= +golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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.4.2/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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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= @@ -437,10 +497,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +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.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= @@ -449,8 +509,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -475,25 +535,24 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +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= @@ -504,28 +563,29 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +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.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= gomodules.xyz/jsonpatch/v2 v2.3.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/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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -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/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-20180628173108-788fd7840127/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= @@ -540,7 +600,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= From 70dcf422dcc332cc7fc024cf3024a12cb76b6ef5 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 23 Jun 2024 15:51:38 +0530 Subject: [PATCH 08/44] ft(ui): login pages added --- cyclops-ui/src/components/pages/Login.tsx | 62 --------- .../src/components/pages/Login/Login.tsx | 121 ++++++++++++++++++ .../components/pages/Login/styles.module.css | 84 ++++++++++++ 3 files changed, 205 insertions(+), 62 deletions(-) delete mode 100644 cyclops-ui/src/components/pages/Login.tsx create mode 100644 cyclops-ui/src/components/pages/Login/Login.tsx create mode 100644 cyclops-ui/src/components/pages/Login/styles.module.css diff --git a/cyclops-ui/src/components/pages/Login.tsx b/cyclops-ui/src/components/pages/Login.tsx deleted file mode 100644 index 17b13349..00000000 --- a/cyclops-ui/src/components/pages/Login.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState } from "react"; -import axios from "axios"; - -const Login = () => { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - - const handleUsernameChange = (e: React.ChangeEvent) => { - setUsername(e.target.value); - }; - - const handlePasswordChange = (e: React.ChangeEvent) => { - setPassword(e.target.value); - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - await axios - .post("/api/login", { - username, - password, - }) - .then((response) => { - // response - }) - .catch((err) => console.error(err)); - - setUsername(""); - setPassword(""); - }; - - return ( -
-

Login

-
-
- - -
-
- - -
- -
-
- ); -}; - -export default Login; 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..4eb0a91e --- /dev/null +++ b/cyclops-ui/src/components/pages/Login/Login.tsx @@ -0,0 +1,121 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import axios from "axios"; +import { useAuth } from "../../../context/AuthContext"; +import Cookies from "js-cookie"; +import { + UserOutlined, + EyeInvisibleOutlined, + EyeTwoTone, +} from "@ant-design/icons"; +import { + Button, + Col, + Divider, + Row, + Select, + Typography, + Input, + Card, + Alert, + Empty, +} from "antd"; +import styles from "./styles.module.css"; + +interface LoginResponse { + token: string; +} + +const Login = () => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const naviage = useNavigate(); + const { login } = useAuth(); + + const handleUsernameChange = (e: React.ChangeEvent) => { + setUsername(e.target.value); + }; + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + await axios + .post("/api/login", { + username, + password, + }) + .then((response: any) => { + console.log("token", response.data.token); + console.log( + "_isAuthenticated: ", + Cookies.set("_isAuthenticated", "true"), + ); + // response + }) + .catch((err) => console.error(err)); + // + login(); + setUsername(""); + setPassword(""); + return naviage("/"); + }; + + return ( +
+
+
+ +
+
+
+
+

+ +

+
+ } + /> +
+
+ + visible ? : + } + /> +
+ +
+
+
+ ); +}; + +export default Login; 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..74650e06 --- /dev/null +++ b/cyclops-ui/src/components/pages/Login/styles.module.css @@ -0,0 +1,84 @@ +.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-size: cover; + background-position: 0 -10rem; + height: 100vh; + width: 70vw; +} + +.cyclops_image_container { + display: flex; + flex-direction: column; + align-items: center; +} + +.cyclops_brand_image { + height: 25rem; + position: fixed; + bottom: 0; + /* width: 10rem; */ +} + +.login_header { + text-align: center; +} + +.cyclops_login_header_login { + height: 7rem; + margin-bottom: 4rem; +} + +.login_form { + margin-top: 25vh; +} + +.login_container { + width: 30vw; + padding: 0 5%; +} + +.field_container { + margin: 1rem 0; +} + +.submit_buttom { + background-color: #ffffff; + border: 1px solid rgb(209, 213, 219); + border-radius: 0.5rem; + box-sizing: border-box; + color: #111827; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 0.875rem; + font-weight: 600; + line-height: 1.25rem; + padding: 0.75rem 1rem; + text-align: center; + text-decoration: none #d1d5db solid; + text-decoration-thickness: auto; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + cursor: pointer; + user-select: none; + width: 100%; + -webkit-user-select: none; + touch-action: manipulation; +} + +.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; +} From 7c35131fae62c4250a3585e70ff95216ec41d5f4 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 23 Jun 2024 15:55:29 +0530 Subject: [PATCH 09/44] ft(ui): login cleanup --- cyclops-ui/package.json | 1 + .../src/components/pages/Login/Login.tsx | 25 +++---------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/cyclops-ui/package.json b/cyclops-ui/package.json index d0296025..31f2401e 100644 --- a/cyclops-ui/package.json +++ b/cyclops-ui/package.json @@ -25,6 +25,7 @@ "draft-js": "^0.11.7", "fetch-jsonp": "^1.2.1", "http-proxy-middleware": "^2.0.6", + "js-cookie": "^3.0.5", "react": "^18.0.0", "react-ace": "^10.1.0", "react-ace-editor": "^0.0.3", diff --git a/cyclops-ui/src/components/pages/Login/Login.tsx b/cyclops-ui/src/components/pages/Login/Login.tsx index 4eb0a91e..eaa7b7db 100644 --- a/cyclops-ui/src/components/pages/Login/Login.tsx +++ b/cyclops-ui/src/components/pages/Login/Login.tsx @@ -8,24 +8,9 @@ import { EyeInvisibleOutlined, EyeTwoTone, } from "@ant-design/icons"; -import { - Button, - Col, - Divider, - Row, - Select, - Typography, - Input, - Card, - Alert, - Empty, -} from "antd"; +import { Input } from "antd"; import styles from "./styles.module.css"; -interface LoginResponse { - token: string; -} - const Login = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); @@ -48,12 +33,9 @@ const Login = () => { password, }) .then((response: any) => { - console.log("token", response.data.token); - console.log( - "_isAuthenticated: ", - Cookies.set("_isAuthenticated", "true"), - ); // response + // console.log("token", response.data.token); + Cookies.set("_isAuthenticated", "true"); }) .catch((err) => console.error(err)); // @@ -101,7 +83,6 @@ const Login = () => { From 7a34d6680af86fe7d39014117e4d7236790c70bb Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 23 Jun 2024 15:56:05 +0530 Subject: [PATCH 10/44] ft(ui): auth context provider added --- cyclops-ui/src/App.tsx | 12 +++++- cyclops-ui/src/context/AuthContext.tsx | 53 ++++++++++++++++++++++++++ cyclops-ui/src/index.tsx | 5 ++- cyclops-ui/src/routes/index.tsx | 2 - 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 cyclops-ui/src/context/AuthContext.tsx diff --git a/cyclops-ui/src/App.tsx b/cyclops-ui/src/App.tsx index 37182d76..8909ef68 100644 --- a/cyclops-ui/src/App.tsx +++ b/cyclops-ui/src/App.tsx @@ -1,12 +1,20 @@ -import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { + createBrowserRouter, + RouterProvider, + Navigate, +} 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"; export default function App() { + const { isAuthenticated } = useAuth(); + const router = createBrowserRouter([ { - element: , + element: isAuthenticated ? : , errorElement: , children: routes, }, diff --git a/cyclops-ui/src/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx new file mode 100644 index 00000000..8fbfc955 --- /dev/null +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -0,0 +1,53 @@ +import React, { + createContext, + useContext, + useState, + useEffect, + ReactNode, +} from "react"; +import Cookies from "js-cookie"; +import axios from "axios"; + +interface AuthContextType { + isAuthenticated: boolean; + login: () => void; + logout: () => void; +} + +const AuthContext = createContext(undefined); + +export const AuthProvider: React.FC<{ children: ReactNode }> = ({ + children, +}) => { + const [isAuthenticated, setIsAuthenticated] = useState(false); + + useEffect(() => { + // Check if the user is authenticated + if (Cookies.get("_isAuthenticated") === "true") { + setIsAuthenticated(true); + } + }, []); + + const login = async () => { + setIsAuthenticated(true); + }; + + const logout = () => { + Cookies.set("_isAuthenticated", "false"); + setIsAuthenticated(false); + }; + + 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/src/routes/index.tsx b/cyclops-ui/src/routes/index.tsx index e1ddd4b3..1b35f866 100644 --- a/cyclops-ui/src/routes/index.tsx +++ b/cyclops-ui/src/routes/index.tsx @@ -14,7 +14,6 @@ const NodeDetails = React.lazy(() => import("../components/pages/NodeDetails")); const Templates = React.lazy( () => import("../components/pages/TemplateStore/TemplateStore"), ); -const Login = React.lazy(() => import("../components/pages/Login")); const routes = [ { path: PathConstants.HOME, element: }, @@ -26,7 +25,6 @@ const routes = [ { path: PathConstants.NODES, element: }, { path: PathConstants.NODE_GET, element: }, { path: PathConstants.TEMPLATES, element: }, - { path: PathConstants.LOGIN, element: }, ]; export default routes; From 64a648e238dc074aef286902cef2ada7a414f757 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 23 Jun 2024 16:00:49 +0530 Subject: [PATCH 11/44] ft(ui): removed login path constant --- cyclops-ui/src/routes/PathConstants.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/cyclops-ui/src/routes/PathConstants.tsx b/cyclops-ui/src/routes/PathConstants.tsx index 85c5eada..e272b1ad 100644 --- a/cyclops-ui/src/routes/PathConstants.tsx +++ b/cyclops-ui/src/routes/PathConstants.tsx @@ -8,7 +8,6 @@ const PathConstants = { NODES: "/nodes", NODE_GET: "/nodes/:nodeName", TEMPLATES: "/templates", - LOGIN: "/login", }; export default PathConstants; From 0f086be1fd82a4fdaa9bebf96e464a3054041bb7 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Mon, 15 Jul 2024 10:13:08 +0530 Subject: [PATCH 12/44] disable authorization feature added --- cyclops-ctrl/.env | 3 ++- cyclops-ctrl/internal/controller/modules.go | 3 +++ cyclops-ctrl/internal/handler/handler.go | 5 ++++- cyclops-ui/.env | 1 + cyclops-ui/env.js | 1 + cyclops-ui/public/env-config.js | 1 + cyclops-ui/public/runtime-env.js | 1 + cyclops-ui/src/context/AuthContext.tsx | 10 +++++++++- 8 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cyclops-ctrl/.env b/cyclops-ctrl/.env index e1310c2f..ae060c7d 100644 --- a/cyclops-ctrl/.env +++ b/cyclops-ctrl/.env @@ -1,3 +1,4 @@ DISABLE_TELEMETRY=true PORT=8888 -CERBOS_URL='localhost:3593' \ No newline at end of file +CERBOS_URL='localhost:3593' +ENABLE_AUTHORIZATION='false' \ No newline at end of file diff --git a/cyclops-ctrl/internal/controller/modules.go b/cyclops-ctrl/internal/controller/modules.go index f00bd26c..69409eb1 100644 --- a/cyclops-ctrl/internal/controller/modules.go +++ b/cyclops-ctrl/internal/controller/modules.go @@ -709,6 +709,9 @@ func getTargetGeneration(generation string, module *v1alpha1.Module) (*v1alpha1. } func (m *Modules) checkPermission(ctx *gin.Context, kind, resourceName, action string) bool { + if os.Getenv("ENABLE_AUTHORIZATION") == "false" { + return true + } resource := cerbosSDK.NewResource(kind, "new"). WithAttr("name", resourceName). WithAttr("action", action) diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index e2d6b2fd..880dca9c 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -2,6 +2,7 @@ package handler import ( "net/http" + "os" "github.com/gin-gonic/gin" @@ -59,7 +60,9 @@ func (h *Handler) Start() error { // authentication h.router.POST("/login", cerbos.Login(h.cerbosClient)) - h.router.Use(cerbos.AuthMiddleware(h.cerbosClient)) + if os.Getenv("ENABLE_AUTHORIZATION") == "true" { + h.router.Use(cerbos.AuthMiddleware(h.cerbosClient)) + } // templates h.router.GET("/templates", templatesController.GetTemplate) diff --git a/cyclops-ui/.env b/cyclops-ui/.env index cbde1cca..fffc394c 100644 --- a/cyclops-ui/.env +++ b/cyclops-ui/.env @@ -1 +1,2 @@ NODE_ENV=production +REACT_APP_CYCLOPS_AUTH=disable \ No newline at end of file diff --git a/cyclops-ui/env.js b/cyclops-ui/env.js index 04a85aa0..0886fccf 100644 --- a/cyclops-ui/env.js +++ b/cyclops-ui/env.js @@ -2,5 +2,6 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "${NODE_ENV}", REACT_APP_CYCLOPS_CTRL_HOST: "${REACT_APP_CYCLOPS_CTRL_HOST}", + REACT_APP_CYCLOPS_AUTH: "${REACT_APP_CYCLOPS_AUTH}", REACT_APP_VERSION: "${REACT_APP_VERSION}", }; diff --git a/cyclops-ui/public/env-config.js b/cyclops-ui/public/env-config.js index c50c99da..beb4cc6c 100644 --- a/cyclops-ui/public/env-config.js +++ b/cyclops-ui/public/env-config.js @@ -1,5 +1,6 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", + REACT_APP_CYCLOPS_AUTH: "disable", REACT_APP_VERSION: "v0.0.0", }; diff --git a/cyclops-ui/public/runtime-env.js b/cyclops-ui/public/runtime-env.js index bcf2dd02..f5618ce6 100644 --- a/cyclops-ui/public/runtime-env.js +++ b/cyclops-ui/public/runtime-env.js @@ -1,4 +1,5 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", + REACT_APP_CYCLOPS_AUTH: "disable", }; diff --git a/cyclops-ui/src/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx index 8fbfc955..8bd5d09e 100644 --- a/cyclops-ui/src/context/AuthContext.tsx +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -22,8 +22,16 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ const [isAuthenticated, setIsAuthenticated] = useState(false); useEffect(() => { + // If authentication is disable pass the authentication check + if (process.env.REACT_APP_CYCLOPS_AUTH === "disable") { + setIsAuthenticated(true); + } + // Check if the user is authenticated - if (Cookies.get("_isAuthenticated") === "true") { + if ( + Cookies.get("_isAuthenticated") === "true" && + process.env.REACT_APP_CYCLOPS_AUTH === "enable" + ) { setIsAuthenticated(true); } }, []); From 32f55589502ebdd3e5c9fe844495c1b8c1a01137 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Mon, 15 Jul 2024 10:17:40 +0530 Subject: [PATCH 13/44] removed unwanted module imports --- cyclops-ui/src/App.tsx | 6 +----- cyclops-ui/src/context/AuthContext.tsx | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/cyclops-ui/src/App.tsx b/cyclops-ui/src/App.tsx index 8909ef68..2b29cb01 100644 --- a/cyclops-ui/src/App.tsx +++ b/cyclops-ui/src/App.tsx @@ -1,8 +1,4 @@ -import { - createBrowserRouter, - RouterProvider, - Navigate, -} from "react-router-dom"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; import routes from "./routes"; import Page404 from "./components/pages/Page404"; import AppLayout from "./components/layouts/AppLayout"; diff --git a/cyclops-ui/src/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx index 8bd5d09e..f00ad612 100644 --- a/cyclops-ui/src/context/AuthContext.tsx +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -6,7 +6,6 @@ import React, { ReactNode, } from "react"; import Cookies from "js-cookie"; -import axios from "axios"; interface AuthContextType { isAuthenticated: boolean; From 128c3757c349a777ed1aceb603917ce45c2d80d5 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Tue, 23 Jul 2024 16:22:38 +0530 Subject: [PATCH 14/44] consistent env var for authorization --- cyclops-ctrl/.env | 2 +- cyclops-ui/.env | 2 +- cyclops-ui/env.js | 2 +- cyclops-ui/public/env-config.js | 2 +- cyclops-ui/public/runtime-env.js | 2 +- cyclops-ui/src/context/AuthContext.tsx | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cyclops-ctrl/.env b/cyclops-ctrl/.env index ae060c7d..46434839 100644 --- a/cyclops-ctrl/.env +++ b/cyclops-ctrl/.env @@ -1,4 +1,4 @@ DISABLE_TELEMETRY=true PORT=8888 CERBOS_URL='localhost:3593' -ENABLE_AUTHORIZATION='false' \ No newline at end of file +CYCLOPS_AUTHORIZATION='enabled' \ No newline at end of file diff --git a/cyclops-ui/.env b/cyclops-ui/.env index fffc394c..3f1fe27a 100644 --- a/cyclops-ui/.env +++ b/cyclops-ui/.env @@ -1,2 +1,2 @@ NODE_ENV=production -REACT_APP_CYCLOPS_AUTH=disable \ No newline at end of file +REACT_APP_CYCLOPS_AUTHORIZATION=enabled \ No newline at end of file diff --git a/cyclops-ui/env.js b/cyclops-ui/env.js index 0886fccf..87a5ed3d 100644 --- a/cyclops-ui/env.js +++ b/cyclops-ui/env.js @@ -2,6 +2,6 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "${NODE_ENV}", REACT_APP_CYCLOPS_CTRL_HOST: "${REACT_APP_CYCLOPS_CTRL_HOST}", - REACT_APP_CYCLOPS_AUTH: "${REACT_APP_CYCLOPS_AUTH}", + REACT_APP_CYCLOPS_AUTHORIZATION: "${REACT_APP_CYCLOPS_AUTHORIZATION}", REACT_APP_VERSION: "${REACT_APP_VERSION}", }; diff --git a/cyclops-ui/public/env-config.js b/cyclops-ui/public/env-config.js index beb4cc6c..24f6fae7 100644 --- a/cyclops-ui/public/env-config.js +++ b/cyclops-ui/public/env-config.js @@ -1,6 +1,6 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", - REACT_APP_CYCLOPS_AUTH: "disable", + REACT_APP_CYCLOPS_AUTHORIZATION: "enabled", REACT_APP_VERSION: "v0.0.0", }; diff --git a/cyclops-ui/public/runtime-env.js b/cyclops-ui/public/runtime-env.js index f5618ce6..4f215c15 100644 --- a/cyclops-ui/public/runtime-env.js +++ b/cyclops-ui/public/runtime-env.js @@ -1,5 +1,5 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", - REACT_APP_CYCLOPS_AUTH: "disable", + REACT_APP_CYCLOPS_AUTHORIZATION: "enabled", }; diff --git a/cyclops-ui/src/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx index f00ad612..67d24590 100644 --- a/cyclops-ui/src/context/AuthContext.tsx +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -22,14 +22,14 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ useEffect(() => { // If authentication is disable pass the authentication check - if (process.env.REACT_APP_CYCLOPS_AUTH === "disable") { + if (process.env.REACT_APP_CYCLOPS_AUTHORIZATION === "disabled") { setIsAuthenticated(true); } // Check if the user is authenticated if ( Cookies.get("_isAuthenticated") === "true" && - process.env.REACT_APP_CYCLOPS_AUTH === "enable" + process.env.REACT_APP_CYCLOPS_AUTHORIZATION === "enabled" ) { setIsAuthenticated(true); } From 0cec1f37c1ab485d55c704f0f588371ca613e480 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Tue, 23 Jul 2024 16:23:13 +0530 Subject: [PATCH 15/44] fixed the login error handling --- cyclops-ctrl/internal/cerbos/controller.go | 4 +- cyclops-ctrl/internal/cerbos/db/users.go | 3 +- cyclops-ctrl/internal/controller/modules.go | 2 +- cyclops-ctrl/internal/handler/handler.go | 2 +- .../src/components/pages/Login/Login.tsx | 63 +++++++++++++++---- 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/cyclops-ctrl/internal/cerbos/controller.go b/cyclops-ctrl/internal/cerbos/controller.go index 01a1b49d..1e4eb99b 100644 --- a/cyclops-ctrl/internal/cerbos/controller.go +++ b/cyclops-ctrl/internal/cerbos/controller.go @@ -31,12 +31,12 @@ func Login(cerbosClient *CerbosSvc) gin.HandlerFunc { userRecord, err := db.LookupUser(c.Request.Context(), credentials.Username) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "user does not exist."}) + c.JSON(http.StatusOK, gin.H{"error": "user does not exist."}) return } if credentials.Password != userRecord.Password { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid credentials"}) + c.JSON(http.StatusOK, gin.H{"error": "invalid credentials"}) return } diff --git a/cyclops-ctrl/internal/cerbos/db/users.go b/cyclops-ctrl/internal/cerbos/db/users.go index 1b085bc2..e4d21c42 100644 --- a/cyclops-ctrl/internal/cerbos/db/users.go +++ b/cyclops-ctrl/internal/cerbos/db/users.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "log" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -92,7 +91,7 @@ func LookupUser(ctx context.Context, userName string) (*UserRecord, error) { userRecord, err := userConf.loadUserConfig("cyclops", userName) if err != nil { - log.Fatalf("Failed to load users: %v", err) + return nil, fmt.Errorf("failed to load users: %v", err) } if userRecord.Username == userName { diff --git a/cyclops-ctrl/internal/controller/modules.go b/cyclops-ctrl/internal/controller/modules.go index 69409eb1..4151ddf5 100644 --- a/cyclops-ctrl/internal/controller/modules.go +++ b/cyclops-ctrl/internal/controller/modules.go @@ -709,7 +709,7 @@ func getTargetGeneration(generation string, module *v1alpha1.Module) (*v1alpha1. } func (m *Modules) checkPermission(ctx *gin.Context, kind, resourceName, action string) bool { - if os.Getenv("ENABLE_AUTHORIZATION") == "false" { + if os.Getenv("CYCLOPS_AUTHORIZATION") == "disabled" { return true } resource := cerbosSDK.NewResource(kind, "new"). diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 880dca9c..8c4e664e 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -60,7 +60,7 @@ func (h *Handler) Start() error { // authentication h.router.POST("/login", cerbos.Login(h.cerbosClient)) - if os.Getenv("ENABLE_AUTHORIZATION") == "true" { + if os.Getenv("CYCLOPS_AUTHORIZATION") == "enabled" { h.router.Use(cerbos.AuthMiddleware(h.cerbosClient)) } diff --git a/cyclops-ui/src/components/pages/Login/Login.tsx b/cyclops-ui/src/components/pages/Login/Login.tsx index eaa7b7db..9518d350 100644 --- a/cyclops-ui/src/components/pages/Login/Login.tsx +++ b/cyclops-ui/src/components/pages/Login/Login.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; +import { Alert } from "antd"; import axios from "axios"; import { useAuth } from "../../../context/AuthContext"; import Cookies from "js-cookie"; @@ -11,38 +12,59 @@ import { import { Input } from "antd"; import styles from "./styles.module.css"; +interface LoginResponse { + error?: string; + token?: string; +} + const Login = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); - const naviage = useNavigate(); + const navigate = useNavigate(); const { login } = useAuth(); + const [error, setError] = useState({ + message: "", + description: "", + }); const handleUsernameChange = (e: React.ChangeEvent) => { setUsername(e.target.value); }; const handlePasswordChange = (e: React.ChangeEvent) => { + e.preventDefault(); setPassword(e.target.value); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - await axios - .post("/api/login", { + + try { + const response = await axios.post("/api/login", { username, password, - }) - .then((response: any) => { - // response - // console.log("token", response.data.token); + }); + + if (response.data?.token) { + console.log("token", response.data.token); Cookies.set("_isAuthenticated", "true"); - }) - .catch((err) => console.error(err)); + setUsername(""); + setPassword(""); + login(); + navigate("/"); + } else { + // console.log("Error:", ); + setError({ + message: "Authentication Failed", + description: `${response.data.error}`, + }); + Cookies.set("_isAuthenticated", "false"); + } + } catch (err) { + console.error(err); + } // - login(); - setUsername(""); - setPassword(""); - return naviage("/"); + // return navigate("/"); }; return ( @@ -69,6 +91,21 @@ const Login = () => { alt="" /> + {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ marginBottom: "20px" }} + /> + )}
Date: Fri, 26 Jul 2024 12:01:21 +0530 Subject: [PATCH 16/44] cyclops installation - testing rbac image and added cerbos to deployment (logic need to be changed) --- install/cyclops-install.yaml | 177 ++++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 2 deletions(-) diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index 38149dab..9db509d4 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -317,7 +317,7 @@ spec: spec: containers: - name: cyclops-ui - image: cyclopsui/cyclops-ui:v0.7.1 + image: siddhantprateek/cyclops-ui:v0.7.2-rbac ports: - containerPort: 80 env: @@ -325,6 +325,8 @@ spec: value: http://cyclops-ctrl.cyclops:8080 - name: NODE_ENV value: production + - name: REACT_APP_CYCLOPS_AUTHORIZATION + value: disabled - name: NODE_OPTIONS value: --openssl-legacy-provider restartPolicy: Always @@ -380,12 +382,14 @@ spec: serviceAccountName: cyclops-ctrl containers: - name: cyclops-ctrl - image: cyclopsui/cyclops-ctrl:v0.7.1 + image: siddhantprateek/cyclops-ctrl:v0.7.2-rbac ports: - containerPort: 8080 env: - name: PORT value: "8080" + - name: CYCLOPS_AUTHORIZATION + value: disabled livenessProbe: httpGet: path: /healthz @@ -398,6 +402,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 @@ -437,3 +468,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: + grpcListenAddr: "unix:/sock/cerbos.sock" + httpListenAddr: "unix:/sock/cerbos.http" + 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 From 6c011a08284db3a80343668471ad68d5f7acb948 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Fri, 26 Jul 2024 21:47:31 +0530 Subject: [PATCH 17/44] disable authorization on both ctrl and ui --- cyclops-ctrl/.env | 2 +- cyclops-ui/.env | 2 +- cyclops-ui/public/env-config.js | 2 +- cyclops-ui/public/runtime-env.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cyclops-ctrl/.env b/cyclops-ctrl/.env index 46434839..fcb73a2f 100644 --- a/cyclops-ctrl/.env +++ b/cyclops-ctrl/.env @@ -1,4 +1,4 @@ DISABLE_TELEMETRY=true PORT=8888 CERBOS_URL='localhost:3593' -CYCLOPS_AUTHORIZATION='enabled' \ No newline at end of file +CYCLOPS_AUTHORIZATION='disabled' \ No newline at end of file diff --git a/cyclops-ui/.env b/cyclops-ui/.env index 3f1fe27a..530cc0ec 100644 --- a/cyclops-ui/.env +++ b/cyclops-ui/.env @@ -1,2 +1,2 @@ NODE_ENV=production -REACT_APP_CYCLOPS_AUTHORIZATION=enabled \ No newline at end of file +REACT_APP_CYCLOPS_AUTHORIZATION=disabled \ No newline at end of file diff --git a/cyclops-ui/public/env-config.js b/cyclops-ui/public/env-config.js index 24f6fae7..ead9621a 100644 --- a/cyclops-ui/public/env-config.js +++ b/cyclops-ui/public/env-config.js @@ -1,6 +1,6 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", - REACT_APP_CYCLOPS_AUTHORIZATION: "enabled", + REACT_APP_CYCLOPS_AUTHORIZATION: "disabled", REACT_APP_VERSION: "v0.0.0", }; diff --git a/cyclops-ui/public/runtime-env.js b/cyclops-ui/public/runtime-env.js index 4f215c15..d85f3610 100644 --- a/cyclops-ui/public/runtime-env.js +++ b/cyclops-ui/public/runtime-env.js @@ -1,5 +1,5 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", - REACT_APP_CYCLOPS_AUTHORIZATION: "enabled", + REACT_APP_CYCLOPS_AUTHORIZATION: "disabled", }; From 456857176f40294dd5a57ce0f492453cdd9e506e Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Fri, 26 Jul 2024 21:48:09 +0530 Subject: [PATCH 18/44] disable config in install config and added cerbos to depl --- install/cyclops-install.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index 9db509d4..a04905f4 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -317,7 +317,7 @@ spec: spec: containers: - name: cyclops-ui - image: siddhantprateek/cyclops-ui:v0.7.2-rbac + image: siddhantprateek/cyclops-ui:v0.7.2-rbac-ds ports: - containerPort: 80 env: @@ -382,7 +382,7 @@ spec: serviceAccountName: cyclops-ctrl containers: - name: cyclops-ctrl - image: siddhantprateek/cyclops-ctrl:v0.7.2-rbac + image: siddhantprateek/cyclops-ctrl:v0.7.2-rbac-ds ports: - containerPort: 8080 env: From c1e2044e29f454dc239eb9b44113777ef40ebb04 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 27 Jul 2024 14:08:33 +0530 Subject: [PATCH 19/44] checkPermission disable for template added - remove unreliability --- cyclops-ctrl/.dockerignore | 1 + cyclops-ctrl/internal/controller/templates.go | 4 ++++ cyclops-ctrl/internal/handler/handler.go | 2 ++ 3 files changed, 7 insertions(+) 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/internal/controller/templates.go b/cyclops-ctrl/internal/controller/templates.go index 2c49844a..b353b010 100644 --- a/cyclops-ctrl/internal/controller/templates.go +++ b/cyclops-ctrl/internal/controller/templates.go @@ -3,6 +3,7 @@ package controller import ( "fmt" "net/http" + "os" "strconv" "strings" @@ -233,6 +234,9 @@ func (c *Templates) DeleteTemplatesStore(ctx *gin.Context) { } 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) diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 8c4e664e..898257f1 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -53,6 +53,8 @@ func (h *Handler) Start() error { 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() h.router.GET("/ping", h.pong()) From d999b46d0524aca7cd6e0c3838b508aa58c8e3b4 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 27 Jul 2024 14:08:59 +0530 Subject: [PATCH 20/44] updated cyclops installation --- install/cyclops-install.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index a04905f4..d8ae1167 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -382,7 +382,7 @@ spec: serviceAccountName: cyclops-ctrl containers: - name: cyclops-ctrl - image: siddhantprateek/cyclops-ctrl:v0.7.2-rbac-ds + image: siddhantprateek/cyclops-ctrl:v0.7.4-rbac-ds ports: - containerPort: 8080 env: From d3bc18de538998575f1f74d24a0a75cc6039e93a Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 27 Jul 2024 22:12:09 +0530 Subject: [PATCH 21/44] removed authorization variable from runtime --- cyclops-ui/.env | 2 +- cyclops-ui/public/env-config.js | 1 - cyclops-ui/public/runtime-env.js | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cyclops-ui/.env b/cyclops-ui/.env index 530cc0ec..3f1fe27a 100644 --- a/cyclops-ui/.env +++ b/cyclops-ui/.env @@ -1,2 +1,2 @@ NODE_ENV=production -REACT_APP_CYCLOPS_AUTHORIZATION=disabled \ No newline at end of file +REACT_APP_CYCLOPS_AUTHORIZATION=enabled \ No newline at end of file diff --git a/cyclops-ui/public/env-config.js b/cyclops-ui/public/env-config.js index ead9621a..c50c99da 100644 --- a/cyclops-ui/public/env-config.js +++ b/cyclops-ui/public/env-config.js @@ -1,6 +1,5 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", - REACT_APP_CYCLOPS_AUTHORIZATION: "disabled", REACT_APP_VERSION: "v0.0.0", }; diff --git a/cyclops-ui/public/runtime-env.js b/cyclops-ui/public/runtime-env.js index d85f3610..bcf2dd02 100644 --- a/cyclops-ui/public/runtime-env.js +++ b/cyclops-ui/public/runtime-env.js @@ -1,5 +1,4 @@ window.__RUNTIME_CONFIG__ = { NODE_ENV: "development", REACT_APP_CYCLOPS_CTRL_HOST: "http://localhost:8888", - REACT_APP_CYCLOPS_AUTHORIZATION: "disabled", }; From e8fec4b509b34498c4229a01dc61b5472bbab9e7 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 27 Jul 2024 22:13:24 +0530 Subject: [PATCH 22/44] logging to check permission --- cyclops-ctrl/internal/controller/modules.go | 3 ++- cyclops-ctrl/internal/controller/templates.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cyclops-ctrl/internal/controller/modules.go b/cyclops-ctrl/internal/controller/modules.go index 4151ddf5..df09f5ec 100644 --- a/cyclops-ctrl/internal/controller/modules.go +++ b/cyclops-ctrl/internal/controller/modules.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "log" "net/http" "os" "strings" @@ -718,7 +719,7 @@ func (m *Modules) checkPermission(ctx *gin.Context, kind, resourceName, action s allowed, err := m.cerbos.IsAllowed(ctx.Request.Context(), resource, action) if err != nil { - ctx.JSON(http.StatusInternalServerError, dto.NewError("Error checking permissions", err.Error())) + log.Println("Error checking permissions", err.Error()) return false } return allowed diff --git a/cyclops-ctrl/internal/controller/templates.go b/cyclops-ctrl/internal/controller/templates.go index b353b010..7de9db54 100644 --- a/cyclops-ctrl/internal/controller/templates.go +++ b/cyclops-ctrl/internal/controller/templates.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "log" "net/http" "os" "strconv" @@ -243,7 +244,7 @@ func (c *Templates) checkPermission(ctx *gin.Context, kind, resourceName, action allowed, err := c.cerbos.IsAllowed(ctx.Request.Context(), resource, action) if err != nil { - ctx.JSON(http.StatusInternalServerError, dto.NewError("Error checking permissions", err.Error())) + log.Println("Error checking permissions", err.Error()) return false } return allowed From a6be27f61cc2f966abc81cf4bfa42abb58384527 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 27 Jul 2024 22:14:00 +0530 Subject: [PATCH 23/44] stable docker image and installation config --- install/cyclops-install.yaml | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index d8ae1167..c51cab67 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -317,7 +317,7 @@ spec: spec: containers: - name: cyclops-ui - image: siddhantprateek/cyclops-ui:v0.7.2-rbac-ds + image: siddhantprateek/cyclops-ui:v0.7.7-rbac-ds ports: - containerPort: 80 env: @@ -326,7 +326,7 @@ spec: - name: NODE_ENV value: production - name: REACT_APP_CYCLOPS_AUTHORIZATION - value: disabled + value: enabled - name: NODE_OPTIONS value: --openssl-legacy-provider restartPolicy: Always @@ -382,14 +382,16 @@ spec: serviceAccountName: cyclops-ctrl containers: - name: cyclops-ctrl - image: siddhantprateek/cyclops-ctrl:v0.7.4-rbac-ds + image: siddhantprateek/cyclops-ctrl:v0.7.5-rbac-ds ports: - containerPort: 8080 env: - name: PORT value: "8080" - name: CYCLOPS_AUTHORIZATION - value: disabled + value: enabled + - name: CERBOS_URL + value: cyclops-cerbos.cyclops:3592 livenessProbe: httpGet: path: /healthz @@ -432,6 +434,19 @@ spec: --- apiVersion: v1 kind: Service +metadata: + name: cyclops-cerbos + namespace: cyclops +spec: + selector: + app: cyclops-ctrl + ports: + - protocol: TCP + port: 3592 + targetPort: 3592 +--- +apiVersion: v1 +kind: Service metadata: name: cyclops-ctrl namespace: cyclops @@ -481,8 +496,8 @@ metadata: data: "config.yaml": |- server: - grpcListenAddr: "unix:/sock/cerbos.sock" - httpListenAddr: "unix:/sock/cerbos.http" + httpListenAddr: ":3592" + grpcListenAddr: ":3593" storage: driver: disk disk: From b66b3117060fd59af9f8a922ce8a11452c6794aa Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 27 Jul 2024 22:17:34 +0530 Subject: [PATCH 24/44] removed dedicated service for cerbos pod --- install/cyclops-install.yaml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index c51cab67..3939eedf 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -391,7 +391,7 @@ spec: - name: CYCLOPS_AUTHORIZATION value: enabled - name: CERBOS_URL - value: cyclops-cerbos.cyclops:3592 + value: localhost:3592 livenessProbe: httpGet: path: /healthz @@ -434,19 +434,6 @@ spec: --- apiVersion: v1 kind: Service -metadata: - name: cyclops-cerbos - namespace: cyclops -spec: - selector: - app: cyclops-ctrl - ports: - - protocol: TCP - port: 3592 - targetPort: 3592 ---- -apiVersion: v1 -kind: Service metadata: name: cyclops-ctrl namespace: cyclops From acdf9171f9c51ba88e16302cdb87cf42de8898e4 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 11 Aug 2024 15:47:23 +0530 Subject: [PATCH 25/44] Added logout button --- cyclops-ctrl/.env | 2 +- cyclops-ui/src/components/layouts/Sidebar.tsx | 58 ++++++++++--------- cyclops-ui/src/context/AuthContext.tsx | 1 + 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/cyclops-ctrl/.env b/cyclops-ctrl/.env index fcb73a2f..46434839 100644 --- a/cyclops-ctrl/.env +++ b/cyclops-ctrl/.env @@ -1,4 +1,4 @@ DISABLE_TELEMETRY=true PORT=8888 CERBOS_URL='localhost:3593' -CYCLOPS_AUTHORIZATION='disabled' \ No newline at end of file +CYCLOPS_AUTHORIZATION='enabled' \ No newline at end of file diff --git a/cyclops-ui/src/components/layouts/Sidebar.tsx b/cyclops-ui/src/components/layouts/Sidebar.tsx index e4e662f6..be18f59e 100644 --- a/cyclops-ui/src/components/layouts/Sidebar.tsx +++ b/cyclops-ui/src/components/layouts/Sidebar.tsx @@ -4,16 +4,19 @@ import { AppstoreAddOutlined, HddOutlined, BugFilled, + LogoutOutlined, SnippetsOutlined, GithubFilled, } from "@ant-design/icons"; import { useLocation } from "react-router"; import PathConstants from "../../routes/PathConstants"; import { Link } from "react-router-dom"; +import { useAuth } from "../../context/AuthContext"; import styles from "./styles.module.css"; const SideNav = () => { 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/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx index 67d24590..0786a395 100644 --- a/cyclops-ui/src/context/AuthContext.tsx +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -42,6 +42,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ const logout = () => { Cookies.set("_isAuthenticated", "false"); setIsAuthenticated(false); + window.location.reload(); }; return ( From 12fa5e87317a6d81766d7f6c7c987da733f0aac0 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 11 Aug 2024 16:04:55 +0530 Subject: [PATCH 26/44] converted resources and actions to enum --- cyclops-ctrl/internal/controller/common.go | 41 +++++++------------ cyclops-ctrl/internal/controller/modules.go | 33 ++++++++------- cyclops-ctrl/internal/controller/templates.go | 16 ++++---- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/cyclops-ctrl/internal/controller/common.go b/cyclops-ctrl/internal/controller/common.go index 96e7dcd7..e69359a9 100644 --- a/cyclops-ctrl/internal/controller/common.go +++ b/cyclops-ctrl/internal/controller/common.go @@ -1,30 +1,17 @@ package controller -// ActionType: list of all supported action type -type ActionType struct { - create string - delete string - update string - list string - edit string -} +// Action[Type]: list of all supported action type +const ( + ActionCreate string = "create" + ActionDelete string = "delete" + ActionUpdate string = "update" + ActionList string = "list" + ActionEdit string = "edit" +) -var Action = ActionType{ - create: "create", - delete: "delete", - update: "update", - list: "list", - edit: "edit", -} - -type ResourceType struct { - module string - templatestore string - templateauthrule string -} - -var Resource = ResourceType{ - module: "module", - templatestore: "templatestore", - templateauthrule: "templateauthrule", -} +// 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 eea7e639..01fb1f93 100644 --- a/cyclops-ctrl/internal/controller/modules.go +++ b/cyclops-ctrl/internal/controller/modules.go @@ -52,11 +52,11 @@ func NewModulesController( func (m *Modules) GetModule(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := m.checkPermission(ctx, Resource.module, ctx.Param("name"), Action.list) + 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", - Action.list, Resource.module, ctx.Param("name"), + ActionList, ResourceModule, ctx.Param("name"), ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -82,11 +82,11 @@ func (m *Modules) GetModule(ctx *gin.Context) { func (m *Modules) ListModules(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := m.checkPermission(ctx, Resource.module, "*", Action.list) + allowed := m.checkPermission(ctx, ResourceModule, "*", ActionList) if !allowed { errorMessage := fmt.Sprintf( "User does not have permission to perform '%s' action on %s named %s", - Action.list, Resource.module, ctx.Param("name"), + ActionList, ResourceModule, ctx.Param("name"), ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -118,11 +118,11 @@ func (m *Modules) ListModules(ctx *gin.Context) { func (m *Modules) DeleteModule(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := m.checkPermission(ctx, Resource.module, ctx.Param("name"), Action.delete) + 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", - Action.delete, Resource.module, ctx.Param("name"), + ActionDelete, ResourceModule, ctx.Param("name"), ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -142,11 +142,11 @@ func (m *Modules) DeleteModule(ctx *gin.Context) { func (m *Modules) GetModuleHistory(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := m.checkPermission(ctx, Resource.module, ctx.Param("name"), Action.list) + 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", - Action.list, Resource.module, ctx.Param("name"), + ActionList, ResourceModule, ctx.Param("name"), ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -183,7 +183,6 @@ func (m *Modules) Manifest(ctx *gin.Context) { return } - manifest, err := m.renderer.HelmTemplate(v1alpha1.Module{ ObjectMeta: metav1.ObjectMeta{ Name: ctx.Param("name"), @@ -212,11 +211,11 @@ func (m *Modules) Manifest(ctx *gin.Context) { func (m *Modules) CurrentManifest(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := m.checkPermission(ctx, Resource.module, ctx.Param("name"), Action.list) + 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", - Action.list, Resource.module, ctx.Param("name"), + ActionList, ResourceModule, ctx.Param("name"), ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -256,11 +255,11 @@ func (m *Modules) CurrentManifest(ctx *gin.Context) { func (m *Modules) DeleteModuleResource(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := m.checkPermission(ctx, Resource.module, "", Action.delete) + allowed := m.checkPermission(ctx, ResourceModule, "", ActionDelete) if !allowed { errorMessage := fmt.Sprintf( "User does not have permission to perform '%s' action on %s named %s", - Action.delete, Resource.module, ctx.Param("name"), + string(ActionDelete), ResourceModule, ctx.Param("name"), ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -293,11 +292,11 @@ func (m *Modules) CreateModule(ctx *gin.Context) { return } - allowed := m.checkPermission(ctx, Resource.module, request.Name, Action.create) + 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", - Action.create, Resource.module, request.Name, + ActionCreate, ResourceModule, request.Name, ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -333,11 +332,11 @@ func (m *Modules) UpdateModule(ctx *gin.Context) { return } - allowed := m.checkPermission(ctx, Resource.module, request.Name, Action.edit) + 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", - Action.edit, Resource.module, request.Name, + ActionEdit, ResourceModule, request.Name, ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return diff --git a/cyclops-ctrl/internal/controller/templates.go b/cyclops-ctrl/internal/controller/templates.go index 706dd201..aa732ebe 100644 --- a/cyclops-ctrl/internal/controller/templates.go +++ b/cyclops-ctrl/internal/controller/templates.go @@ -104,11 +104,11 @@ func (c *Templates) GetTemplateInitialValues(ctx *gin.Context) { func (c *Templates) ListTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := c.checkPermission(ctx, Resource.templatestore, "*", Action.list) + allowed := c.checkPermission(ctx, ResourceTemplateStore, "*", ActionList) if !allowed { errorMessage := fmt.Sprintf( "User does not have permission to perform '%s' action on %s", - Action.list, Resource.templatestore, + ActionList, ResourceTemplateStore, ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -128,11 +128,11 @@ func (c *Templates) ListTemplatesStore(ctx *gin.Context) { func (c *Templates) CreateTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := c.checkPermission(ctx, Resource.templatestore, "", Action.create) + allowed := c.checkPermission(ctx, ResourceTemplateStore, "", ActionCreate) if !allowed { errorMessage := fmt.Sprintf( "User does not have permission to perform '%s' action on %s", - Action.create, Resource.templatestore, + ActionCreate, ResourceTemplateStore, ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -174,11 +174,11 @@ func (c *Templates) CreateTemplatesStore(ctx *gin.Context) { func (c *Templates) EditTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := c.checkPermission(ctx, Resource.templatestore, ctx.Param("name"), Action.edit) + 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", - Action.edit, Resource.templatestore, + ActionEdit, ResourceTemplateStore, ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return @@ -222,11 +222,11 @@ func (c *Templates) EditTemplatesStore(ctx *gin.Context) { func (c *Templates) DeleteTemplatesStore(ctx *gin.Context) { ctx.Header("Access-Control-Allow-Origin", "*") - allowed := c.checkPermission(ctx, Resource.templatestore, ctx.Param("name"), Action.delete) + 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", - Action.delete, Resource.templatestore, + ActionDelete, ResourceTemplateStore, ) ctx.JSON(http.StatusForbidden, dto.NewError("Permission Denied", errorMessage)) return From 8a34d0f328be0131f32db669e7b9ba33759bd5df Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 11 Aug 2024 21:13:15 +0530 Subject: [PATCH 27/44] replaced with runtime env configuration --- cyclops-ui/.dockerignore | 1 + cyclops-ui/src/context/AuthContext.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) 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/src/context/AuthContext.tsx b/cyclops-ui/src/context/AuthContext.tsx index 0786a395..d9b5b28e 100644 --- a/cyclops-ui/src/context/AuthContext.tsx +++ b/cyclops-ui/src/context/AuthContext.tsx @@ -22,14 +22,16 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ useEffect(() => { // If authentication is disable pass the authentication check - if (process.env.REACT_APP_CYCLOPS_AUTHORIZATION === "disabled") { + if ( + window.__RUNTIME_CONFIG__.REACT_APP_CYCLOPS_AUTHORIZATION === "disabled" + ) { setIsAuthenticated(true); } // Check if the user is authenticated if ( Cookies.get("_isAuthenticated") === "true" && - process.env.REACT_APP_CYCLOPS_AUTHORIZATION === "enabled" + window.__RUNTIME_CONFIG__.REACT_APP_CYCLOPS_AUTHORIZATION === "enabled" ) { setIsAuthenticated(true); } From a3b0bcb0ae66a58f0912e7b25483882e4e10330d Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sun, 11 Aug 2024 21:28:27 +0530 Subject: [PATCH 28/44] updated go dependencies --- cyclops-ctrl/go.mod | 24 +++++++++++----------- cyclops-ctrl/go.sum | 49 +++++++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/cyclops-ctrl/go.mod b/cyclops-ctrl/go.mod index 5cf32856..5920ae9d 100644 --- a/cyclops-ctrl/go.mod +++ b/cyclops-ctrl/go.mod @@ -11,8 +11,8 @@ require ( 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/golang-jwt/jwt v3.2.2+incompatible 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 @@ -35,7 +35,6 @@ 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/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect @@ -115,6 +114,7 @@ require ( 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 @@ -127,9 +127,9 @@ require ( 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/prometheus/procfs v0.12.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 @@ -152,21 +152,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 diff --git a/cyclops-ctrl/go.sum b/cyclops-ctrl/go.sum index 0cdcae21..4cf52bca 100644 --- a/cyclops-ctrl/go.sum +++ b/cyclops-ctrl/go.sum @@ -4,6 +4,8 @@ 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= @@ -106,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= @@ -203,6 +207,7 @@ github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u 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= @@ -239,7 +244,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl 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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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= @@ -299,10 +303,14 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR 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= @@ -313,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= @@ -321,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= @@ -449,14 +462,14 @@ 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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -471,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= @@ -529,22 +542,24 @@ 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= From 4ec2f4d2ab291d77cc0de2f81800e87413db18ae Mon Sep 17 00:00:00 2001 From: petar-cvit Date: Wed, 14 Aug 2024 15:51:16 +0200 Subject: [PATCH 29/44] login page revamp --- .../src/components/pages/Login/Login.tsx | 145 +++++++++--------- .../pages/Login/cyclops-simplistic.png | Bin 0 -> 54302 bytes .../components/pages/Login/styles.module.css | 42 ++--- 3 files changed, 84 insertions(+), 103 deletions(-) create mode 100644 cyclops-ui/src/components/pages/Login/cyclops-simplistic.png diff --git a/cyclops-ui/src/components/pages/Login/Login.tsx b/cyclops-ui/src/components/pages/Login/Login.tsx index 9518d350..946454e9 100644 --- a/cyclops-ui/src/components/pages/Login/Login.tsx +++ b/cyclops-ui/src/components/pages/Login/Login.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Alert } from "antd"; +import { Alert, Button, ConfigProvider, Form } from "antd"; import axios from "axios"; import { useAuth } from "../../../context/AuthContext"; import Cookies from "js-cookie"; @@ -17,9 +17,12 @@ interface LoginResponse { token?: string; } +interface LoginRequest { + username: string; + password: string; +} + const Login = () => { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); const navigate = useNavigate(); const { login } = useAuth(); const [error, setError] = useState({ @@ -27,33 +30,16 @@ const Login = () => { description: "", }); - const handleUsernameChange = (e: React.ChangeEvent) => { - setUsername(e.target.value); - }; - - const handlePasswordChange = (e: React.ChangeEvent) => { - e.preventDefault(); - setPassword(e.target.value); - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - + const handleSubmit = async (request: LoginRequest) => { try { - const response = await axios.post("/api/login", { - username, - password, - }); + const response = await axios.post("/api/login", request); if (response.data?.token) { console.log("token", response.data.token); Cookies.set("_isAuthenticated", "true"); - setUsername(""); - setPassword(""); login(); navigate("/"); } else { - // console.log("Error:", ); setError({ message: "Authentication Failed", description: `${response.data.error}`, @@ -63,8 +49,6 @@ const Login = () => { } catch (err) { console.error(err); } - // - // return navigate("/"); }; return ( @@ -80,57 +64,68 @@ const Login = () => { />
-
-
-

- -

- {error.message.length !== 0 && ( - { - setError({ - message: "", - description: "", - }); - }} - style={{ marginBottom: "20px" }} - /> - )} -
- } - /> -
-
- - visible ? : - } - /> -
- - +
+
+ +
+

+ +

+ {error.message.length !== 0 && ( + { + setError({ + message: "", + description: "", + }); + }} + style={{ marginBottom: "20px" }} + /> + )} + + } + /> + + + + visible ? : + } + /> + + + +
+
); diff --git a/cyclops-ui/src/components/pages/Login/cyclops-simplistic.png b/cyclops-ui/src/components/pages/Login/cyclops-simplistic.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc1d74db6a1e9accae8f04678a3b30028aa0223 GIT binary patch literal 54302 zcmYg&2RPO5|NfDgSvG|f85tp2#|RY>;vk!l?7e3Z5fM6)UFH!&*0BlMd+%(=%HICZ zn?Aqq-*t6$UC#UcoM*ho{kmWGBUnZ0F$p0(Aq)m1k(ZNIg~9OLU@)w70zB{=gUApM z@ZV)SIW0#RjOz*X56jiV=_w3$112x~0O1zDGHzg}worL?)Y&_{@J8VBi_Ws4r#^mM z>j5}J0z)h~4&B-*1g`XTNtakThwe+C-Uf&QE6{?xaAaj zw%n^ajk#LMRjity-;H+>SaE2fm3QtqTl?tLJ{Nzj>2c0FhEvxWEaLnJK`m3`FKsU! zPrtyna@~e~v)<>X*!EIn-<_exCH-F82agF_ zcpKzp^#6MhrXqy{|E-{%EMU;E-ogIuqu~O4yeOdUwCpa%*k?FD;*YX|S?~3K+ zm@W;A zj#+HUCq$^t@B`(T!MFcDgGET!sIf-o8%C`dX4H;8;iP^*_V1%)t1BD(@f0hv4g6Mb zd$_Zl`wpuZ|2HiJ?@~)NC+0y;jJT-HN#h9!IsiR`9^;qe5{l!2&W;_vZ3=@jh84rcRh}2~T3Ap|62{ zPIr~{Y5Y>;8{ZhS6g{!nLQ|I#%85pY;x5hqH*-o7EM;7KQJIkj39(N%`rCW&fn&}X zY=B;8H@GHLv}91_`F_xJc2#bHIYHRI4Z{yUK{0~2?9eWM3S_Vm9}q&F67o4t=c}`G zJUiL5Y}_{2kNH^Q$mBcgd>K4{#d9ON(u8Y>GfOi-?2}b&YA!Ib1!rdHTc97`8&e!= z&Ch?_rXzBZA7L)1!1-hylIo~r_@7a`~!Ua z5iCA1Hvv@>X?-y&%EFjHA_Q*m$WYuklOQp*iX0r% z7h3$(E6`3~oEr3)$|-ne%E@wtN}(X7x~~fEtWWdb`|P$ja8_^<&;f?0n(7a}DR}t% zKfj>ulXTaUDw1n_Vr4-`!)`gloIl3b2!ShF`1jGCa6=eFB_jBsh#myRae3aD@kRqj90Q7%+Xy9rP90 ztr+V0fF0{hHPj8WRMmBJk}PcVT4m+spstJ4Ox`DO3%Nl@G$0@H-VXn5A+U)LbVXHp z&A_gS!}!u4xqPNTGR#NMRvLBxVWk)MaH=*Q%rh9Q#kQrMnNAm$Y)!qEqFl04lcAs* zW2!Y6^0($X(lQOQ}0JHVD# zJ?SfFkuMo@cz-&WQz92>Q@KMO#w6rIP;GzacWZ%Hw@>o)Zy3wcx`B62UiGQ=q}Nth z)&8-7d{3*VXrP*h|5o9n$WXo{x#ehvT_=)-f5$$hg9FC}haShWx`QU&r)MTp35@4S zIOtH8>4++PCrdAAzG*V$y(wrIbRLvl-F=Rv;Cv2`%~03CDjXG26|I1&Ke(==F>~se5dC5d`BMbHHT8F zoXkmdWWto~n&BVKY_YTpH2!Q|TQ)D@%FZaIx^OB4qp=opP!PdY#}FOt9RQb_78w`K zDezCIF!iyYY3l5bAceuC`oH>{(MIM^(GC}e1QmU}uVU!53BxrB zz1&Lbg10)L_0uhQEosZ|<-}m}-)Qf!B3Vy^`&!7D*@Z9Jkswk zvb=0PO3ymR@lnd~Cpg4x@p9L!^-U`A6Gg=o73<<&f5_@Q5_q^SMny^0MB&#lAU>M{ z!)wOe^mCXcU%?sc(3u$fYRop#`*+3{(c6jSyh5v2$NA9)Sciv0y=a1%s}laC$alF^ zzR@h43($~^y0St1#txs#f|r@BD~T^%-5=+CnRyGmuR7QCoG{U`2+d9~U|&APy1Bcf zLb2Lq2nB+pRlhOS{2ILaAp$=FY&w7A_LGs6>$OUgb9Y9H1yRjWw~;?3h{sp71d7b| zcuo$s4K^=(phNkld(GN>M1Ek+EV-YGq47laj{|0iUs<$bq1b zaeUq*Vcs71Z7f<{wEa`}!uS#zKYxj&`Zs_H4xER;bxL3r>_Z3G2UYvs;a~+1gWai9 z7>?K}zt7lnu(ACHod&=Ay9JbcSdA6?Xa&*(<}R`XB`%gSULC`}ij*o1Ga2f^h=f?{ z*c#i=-x4tS`9de1xKu{twqtT61b;#HvcrL6H#{uGK9he77X+D$(w{HAgSPh@*U<`> ztxb25^4VL%j3=o`8!?wZ;ty|z*<|3bGQ1eRfAMxRfm;uYac$`et1e8EP;S`?-3Obz z+bGML5Tzi@6nt-aN-PN4srJtjIkQEzI)!HKebB~l@vb1)5g3;i`0zM9hn0uqd^p2j z1a8-rN@viiWK*})Ok6jwY$pFnb6)*fHhy+YB<4I-qoZAFXcK%#rp(=Ihtt{F)$%Du zyQCaD(uLL-z4#CAdK@B z)1{H6fgDM0%5EUJp3NuCEOWV{0}MeU_oI27e09rvTg3D8f?ar~XbcP1-e& zlOQ$B-x$i*==21@rusjD+qnrKvUsT7;2fBhr*NLiIVnTClhPTFDCkn3zb~ba;-9jw zOW?`Fz*>F*z(~7`Ca3{G2q@vRQoXg@ZPi9hyb zg=$SneZn}sQBJm^HU7eq5JlNmv3@|~(o^LB<_m~BF0Ly*y`8_t))H@K1+k7^e5*(n z3R*gPmj5{cqRek4BIs$uGwM)2`!cR?bif*b2QL2Y&wgS+>BW16HV4G5Uf_Zplqd$f zk`>m`uW5P2=K~ogqX^wt8s-W_gkbV68^ZuJ@dBi>EvJoipZqdVBx*Os{WS4jw||Su zQxI3Pa@W?Z6pD-Jj77uvbT8nNUk2(nbcpt4E7P##B#y`-mFtOeqqb&JS0G#JuQDzN zW{}29tPYuh=*GY)7@<9?Zw_vD;YjdjRL}`nMYdYm!E%wH#K|0dn2Z+ojC0~JzTB>o zsR8VU25I7NmN@nU3PLNzCZm)fTA%-r9h{qxQQ682>_*0YHd@>e;8iS35i8hAjSufBFJDrHqN8g#qdH5^XLn7h9+3r+i(ODrts->;sWKr|2^=FQqI`?~Qb;2$ zMT`PLb-Hs7bOQVK#tH_MQ!h4CgP}d)wzaV@_sru$XzuuV*bY?{0&f1O+WbUqc_OIre2Efl3pN#OL+W=v~MV>@| z5NrQeG_b!tG{L?<-^z5eKjQ0hhLE88iig1a6|M%vV^HZYw%O8hZ?0%0;?87Ak6xs(IXJ@VZn+2VPL0kUaPhCP5Q%qK&;7 zd1q{RJYQj^DQqW|C%Fqs%itYu?FyDHo_}wtP*xE@=UKk8vxY@!Aj3}`>Y<0zGHE!i zbmQ(w6x#0v9RNJwSZ~f=m>}!3zmN&W^O}bZI>Xe{+uc{OatT~ja=?j|Ni&MxxCl74 z2&WpTN@Qse=fTd@uwM`)C^d(oAL2tqMlCJHUNX!1u}sNOUE2`8R_WR@L4hN+l+Q$_ zD}p`L+7o_tEqNqWI#DI_{?`A_@CxVssgJEIb}&|&Gzuo+ z3?|{Rx7cmPtlZ}xdHw}{;Rq9GCUMbxegriFmP6tAksynzHYUCTn0rFp0};jcbUU5B z#u)tHM!oq{B#-XmMWd`zDDVt4uT5a7Nc`z22+mY({Cp6(o6dA=49hAjqNzdOSh4ii z-1+0Mq|8jeCOWAP@4a399O`}G>TR@YY)2zwowmj6H^#88gW>yXT}jKw`ehbGMW+u~%IDgkr zj`Q}E{qFvAfyH20Mc+Kvw|C3ktQ?DuBWVba-1!i9WzS}vu?|43C+MJciZ7+Ltl0R*`P;yr-WK`_Wenf#+|b+>!5qsX zYs%by%|&$E+dyP8*E-C9_&(;9f|HclrAJfSv4w{l-@bK)^yy+m)U7;dU%AKr3N22-W3U^AL2hvNZ1 zyvqWW!V8%@-g^2pZ|WTH2iPi0QWN%b(p#$mANYnkW~N$%>zY>&zgZtH))&SmXs#;#?wwJ;-~p2Pqn&GJR_1D*?2`GNZyVRr zBQ3UQ%x>)~nL4Rgoi_SF`6BKs=kuHPdTh$hSqo!L9;^MDffc{B8PNhZ;cj)mz-FoRd#NBsGk)Ekf&F{wis`^F zPA^&`JWqe_*~3qBo1d~hPQrwWEEnF(EcMC%CbjcWJ>xdf45A>|5fwVBR7$YP5d}2D z+58w8FV!w^dF{iETOJ#=tWGX-(;$J5A?M=ELd{=DUWCJluzapo3_adCm9g%rhwt}B zPVPlkG2y~Wd2iZJtW&wvXZWNQ4F$UX!rt@WMMXt7$JN-V7MWTZyvE*9*{gY&}<{lkE`4W891`|NjJxHaKt)*%N;<3|K$fwvO z>vO{Fn*-Pd)BqsNZbTK{(;35v!YCCjvhE{_OZoA(Tz`V7&^)~=^Z1_UE}8Rss3#tg zD|zIuytbj%0Bvi*ybiKo-)8!QiR&>aRb4Hv-!gx_GD0zY+6GMG=VH?q^xDpD$}QIH zq8?C47dej>V+A$rP@HeQ$uk$MaNS*3n9ARIR^tb|k(nxupIwGM-;8YlprPe)=RM*_ zAsB?eoR2>(Nc1VvRDDb}5LA<*MP&55kdeRlfh!jkouMYH_EqIE-zfp8>;(w94GwRh zrPxWne*QHo^DUfH_IQ2^Bpx^Yp7gCbEvCGoHLi;sNM!;wQeBl8&nB_UA&WTkQqmp4 zzUJFhyK<8)C#%+EW2X;huadGJC1U3)(dAslg0a_57%K7FR2Jo@+*fZAn5t zXuLzgdAlv~LNUn)PLn+e%5DOyXwFQgc-J%NB_bAQii z+}xH>E_x3XHMS@Yf#6@x)+5&$tilt#BbT{b2{mUL>%m58Aq5Ro<#s0W<>l7q7JXig#6_}F|koJkSwZJ1`ZnOYT9Qa+foecKTmN10P zKwK6e%QMQlsCgp(xYyM8SV{HA0%Gu+KF#X~4ZXI&MMlW2w=$zQ?LjgS%zK{oI7a-t zLQIvD7;6#bs23PnK}YYPmB0fnu&%t?S-8(ewtW2dJ!@Xxm{o9b8F&99IY2FdAC^xK zYHr1_`oZ?~Edl=jizX-)Xq25RxTgc+W89k-*<;r{jymC$Pftc?<<%$KDF{LbI`ps;ZPnrj+0RGLbV$U5gQ8LioHgQ`-{-gb$WT7P z$it##Q0QeXrwx+_V=TO1=;h`na7Qk4wJvknn^ScTf6I8ZJ85)t>(t=%40S&2xV`uX504Haus!JfzaunK|BVPB#-)DLW-Uc6At8lr1YgC&k{|vod#_O;v&e8^M+3Koy zPioN47&vEx7(}9lAU}Cnj8d1B@5vMP7O8mw&h|M{oo%VC?MQ#3H-(D(j`L~VLo&{- zfdT7LellFs>i-7jbO#x*EdP67I8m|{uI%HajQIAEBNB4kvA%Qw4BS=2H9HJSGwo#j zNg3S)wsHef9|K(QA}^t*fGb#S`poMYHsz()B93Fbqf1faMHIZ=>Jcoh1)_5W zCZiB`n7pmrGaB^4O*KYyAlF+O5J88`(opOM(HkKTU_xFPN1B%+jOx~!Ggkj^TD1&- zi~jF;5gxWoqF=!Yj@2b7v-Ib)#O=lV`u|8>YdfMoano0S3kUGKzbA;P8D0^}bCH`+ z%FUn(2oHOe#YiUAU~Y%BlQrMLph9KocYgN-RiM&lCnTI?K?Lqx%>@)67*yxr2N&jx zsg}{OC-aUPH*+U2-u{Z47{+IR$zv`m6SzhTX<}v%5_Snj^9+z-w;MnINDbUJ&i1L& z1_;HLkTd`KbcZ9-RPa;j_1Wfw61oVxeUaHRKx5vw{;)hjx3@Sa&M>Tm;Wm;#Uwkrg zGz);31JBh$nl7nyLjW~u@FC*ktHUsmJBeu8b~2-TTCT3a0a0j~g{kcVdFs>lt9kqC zxA2Rgscv^%YNaO@S!tywo^la%`!f&>@W15Zq<$?lJ7_P(6SmA{kkLnE5Yx(J0Hy z_S!!_{S06VRqd~SQQ9Cq|G{Ar$!fAccn@o<6{_3CM zy`ct*@ytQo{aBqnB?X1|LJiSfV1F;Wda-N%b_BlPLvv5DNjb4+WmvNBD^q7H^$H7% zxRI6yNvG!dPTimqnX-R5t;{iR4BKEuyM^5O;$sZ(A+Xl@XB=tvz%@>`Q}D;vZjSwZ zZN=Pad`HLWw5|aRi<8kAuMRK+z;RL_k2s34GMm3orEe|xeDC5^ZP;D#m4E)OX7clW zSjkfpj>`9B`&gQbKi_7L3Ar)$_i9PR0^nD7YT`wnu;hBGAxMF{sza#=Ad2nH#1B>( zt5FCuEsef{@0P4w7X&@R&zEz6*X89T{`;LHtbP5vbVT>Vv?poy&{vXz(cWfiqd|sgoqh9$gJ;&%3v;eU$qKk}KvJM+42-xTTFAdD3s6@Hf>(EvFl9 zGcq#(ET8u;05zb!`QlHt=Tn_;agMlRpnII)wuHI_Yo--=w7a9);oFotp5QK~H^0)X zzwkJq4@{`P^|I4^E2-l$VuY#+^ooR}Zx|~|t7v)T$+9+z=iYBTmF(kIr2D zL&s+ZGXv$1x}?GCGrPmFc$n%koC_ix02;rv=#M|0~&AkSn54w8Lp#*Zi%O zao}fph6NxLShHpTxZLjXt;(ce`(O$zOKd0wLnrl&&Uo2`*ZboaajkD3+vB4hIuoN; za(^MNAOr>2u_%+aM0i=_=YCrCU@u$xTwlcYQ+1TEyx&af(qi3;^jqo7*#id6ryE@Bka$b?Rv)b$KH9 zjx;9UuRE6+gf}#v?ahc>K<^7fWijD39qn)=mr4P1$E_C>3tCNLe(A8Gx!7xQ~j7ZW`G4*L9 z5Va7VZ(m0&`~N7*=0_RXI;yRKL%HL;0RU1P7+v>mfuNr6=DE z0m!r@ZFpA;*+2V0gq@=Cvyv$n>rcQr7ow@WFP#1B}`v+0E=f6&g>oHS)(gW%X-Lw5BNvFBZIo(#( z`FdA{mnOzg!wWV7i2jG5?%2vrX137J=XaE@_ikHxR26?hS9f^-b8f1-q3U23&+s7U z?bT$K9(^Grcbb5C>6qMbkz(bF7y)i$r##-r}{L{g8A{U0Q#}FIOkdCG6%9%GnW%U+_H}+Y%k1< zQ44MS%g;uviUDb;H$HxgP?RDV<1tqxv~9IA7~5IyG+wYbyhOi?jpb*|k^=Gk;~tot z!@*IT!30;uuAGty)t}9bkgaGMczYY;&4K>}`_cqhJd@dZj#`V>MLh}JRIGcDo0lyE zuzUaO4)=f;QN9Oj-0o+?h#TDU>q#EGgJz{(-Zzi5`zDKhh$^p#iJiiij*o)7!Sfpvtyda*eyt{Yc z_BZ=ll{6mwS$8FNa;Y>RgmygiWLfPeLV44evVUR)c$E>~lT#IEVqqSqMJ$#ub%N!s z4p&+}ujV~1AN(nZ78%5^lNe?Yt4e%Ln!F#sw|RSkEw45Km_n~vTUYgjOL_XZxH_OP zxU{`xpzN4%vSGXvfAst@sVk|eVUhCggsFYC;>eAu2ZC~Of5m4{=v0|IRw>p>^HY`c z-z;s>@GLW(?LRv28!uE>Gup6oU)?nfh#yq6xY_h$Fa8PHgRSvVmhm7aZ?73&no^Ma zB@#-fg%0q<-c$2-`;(pg{4SHA`>)+vYnW{>XUe!u$v?HEZkpZ<<7{#StIqEnyaHG% z)*Siq53=;m$NNm;-!p{^xY~CEOtn_^`}{XeLW4Pt`CC+CEIg08u2ya*@y;JQL`5*H z{0h>#4U7mrjY* zG6pamVz;9k9D1NGe@2pb3g|2^l*hkwOo!Z1sQ@U+Rmh(nZ%g`hi zEaV1r*2}&_34^vVE?na_!8Gn^)!*KaTOK0QhS8wd^+b@jeCA7kY;F+0sfStx3>@e~ zd*+gPULG!N9X;VMzuw<|nP&6ZyDbl{))$w8>W9Vh|DGf}^RTE#37>Mo7*jjxl=lv+O zx8L7MCgC^p@SQ7T64M_Fyv9>^MGi)3lE{h_g!&KW<9NoO%yHpf*F;*n0jv)I`Qpbk zvQPbvd0h}YaB{Rqw(Gu4F-##={zOgP<}IO{$HP5RzaBRyppz;EK=#r?`+>?~zU3f! zmFF!NIzu@*)C>{jSSfdZG3aEBxLUc2pMi4NV|SRrznyE6?WkU_!JFA0{lyEU?94$y zN|_N=J)32gu;!V;zO3WjHkRnYFyLwrsx(2*qdR;pY398OTU_6bWhNirdr2LMA;ca8 zZc{WJH=Ggf0X>i*o;WggwH?uW@nMdjZ`xaAa$24Z_Nr$zaJungKYs1q?O_IT{}10Z zUR?R&0Q{tKa_2R=e4?|Yo{hOYo2|w5pvR%g_}8a7QAWc4WuZ~mSAZ~P{odjUfxyR2 zYqdz*JMZ)TuHu6l+pY$76Gr(A3U8pN`s{KjUjpFZY@j;KTIbsY6#@r9Tvg*){Mew9 zeP=tGx6{Q4o%tH=M5cMt*3-L#IsyC&b+l@qi#%FE{%FL|ht$%^dQM_QLy}RTNRob3 z?m)Z^+1|mB1wgy3RkV~0As}il?+8uw(n==mU;;8pI-7aDzyct%aiw>-K?W6`Aq0!2x_N;o(!Wp!VMH9 z-1^5+ZM-T;pm<$Sf0{|$f%*2>+&?SpBn#9V=kdGM0FK0H?#~n9V=0?5PyLPYdRh7S zW|;QQR=>KCF@GF<_d(Q(rITe@X9U~SgEWU+J$n-JC%j-G;`>wNlf)`y4G^~)Gbaq26GpZes@y^R9HYcZx`_w-%W@KM8QsQLA5ki^U<=LE5&1OH$Kn znThWei4OQe_XqC(q;@6jnrWeOFNbuvv6MlNplpi<=&I05x5wR^0v`A_ft>p1fLsJrV*P-nk(WdW zVkzgE-eq}{kkVp&+f;gYr@S)2bndMm{--*+OdHJ)or?dGNVH8om+Dc-T=(n!_!Ud) zNyj_Uy;R+(>k9d4qGQ_H8g8et-*urZySP#Aani5p)t#o(!Ow=B;R#rH53e0m4>ckM zDs($zM7Ohj4U$%)BW)P&uAH}>HnXSHxADBroHjqRRE0f{hR>(&N<}zufPsGkvBNqceYyp) zTYPM!z3FiFo8ib?5EinY_{lMttTIeOaCIY@T=mLT@`!s8x}w4S7^ zO|s@hT!q~w&e5k)X`IiP1R67Msx{pzhkk2Eu}RXJZ*K9XcMDdd!OnF>Xhp*w{i3@e z&K6_G2KuO=^M$F=IXX}kzXZ|FkT?VU>JBB!SuE`V=tWOns=0BlW64XsUq-9}-1897 z?~sm0w>%IEfkL^Opp+Zsz>@b2vn0OsuG7~L<> z!*B*-h4T?0-Ayl2e%V>{+edt3MO~ihj(?r~+`0ODpfwK47UV@wIx#g;xAn3c41oe= z53#47Z0s`4Mq519U(|=Mi;F`E?wQKFiX4W*Bc`3vioyPI%n?$6O6f0iT-e?g$hXS)NoVX4PpSl?3*|G8r<+)BHMI={`wIz*W}41^ z8%`0Vy-8d!7?QuMm(rupyD(cIL{;1IyX~{YQ-J^JxroQ7EAl7;Urxi7J!cCVS|+^n zP~J^0iE+0;#XeA+(``i&mY#{j-1!5l!iQL5ggjyF`9O}yF4VDUbu!T8W7rW6N2PBR zIIVXXy*6@86k9EbSug7ZCHVbP+`FnBp*=ihPODr%x(2NAfwIl>CYt4&1UQc{%_Nv@ z(DMEXM0q7@>2k~V$R4aIJ0#ZX$5vw7=83rJn%5-+X+$faS>v=eR%TULoo4HAj`ySW zw_#pPSlkGK3n5D-hFgJF^kG0sZv5XA9#bzylD8JlE5AG~4u@5hmp8LPBXoKE+vD*| z%|OYKk@Y-We2g?PU78vTR)5cTLk;yIFUHIfGhyUy5gJ`ctn;}N8x|TZpirRJLu$&^ zOHSBtv0kMKop+UoEsrMDMSEScYYBcgju)V+aHAoimMj@Hw>+W+BwkgmnzWfbU;9?p zC6i7Sat+b%=Q1mVp2v*-lkET!Vs1y(P-d}*l~{evZUM|o&~rc$OtT#5nwG(8$B;CN z-n&+ujWeVo5a<$gFc-&xk>SLDkoLM0?_+!zO2_!=ncFbh70!+e^TUeAddp+#)bqq; zP)XBONPtzmYr8OQd3mkl{wuU1tM)Yp_Q#+vXSS4xrALq@UbhQ$nULm2MQg^^e}wwLS+#0#5IQoM)1&cje0X9tu3!!1Cc{OQFJ^Zc$Lma9sk3yo+GM?jNYN zZrs%drqePIs}&t#Cd>d=Ke>se;DQ|MJX|L+cJ8zj?7}Ucs;P3(lT1XQpd;xWe z51yPj=#mWS`ln~b{`E?iRku&v5_c%G0ey{DMk7Wj!s%iUZJF>20Be=*@Ux0>sL=oX z>zSy()6$cG6#KFOv2rnj8w%#51Fwod6ut?9*I#zqpCU?*Z&PS|(|?#g9$rXlS3PHV z+~X1_r<>u44^u(L3r>?G^5Nduhqe) ziG;3hiO7klYXeJQfbliXSHA^Pp@7^G(@Gx&o8o(8rXzgP2*Lt%a(NZIkLFWw#>HT; zJQ0Jh_}{C4RFpkj=t$@dUFrprB8M;FTtKjivA+8%Xv>yxUv)&n2s>9ND~)Aogo8KB zaKyCNrg>Q5`!7~?5@W@%mD~i=*uAugg%kdif=D3z)c_}RllarDB4TOJ3;05jw`Ztb zlMDzM?NplqM1N^zR;P@vP)UjW88N}^?Wt%=_GgO{uE{N=w8?-P1q(%Pgy?yO1j`A9UE&8(Psd89jK}_$w5<+X8&ZNG@Y2;=abVHI1uj63ixu zf1o+YN(@3K+xnI%d(ls$qQI06N0-Nk`JnP$XB2NKswAEza2F-&HbuJD%y7NFH$_AY zi++tVNN+!onq0460QeS!lOIra2MlK`T(Jflwjn#xScE6)qSf1e1-iVW8-D3DHPa`bNT}Ix@dhUF-PIGr3RMBIOF>JG|f95Kr4e!B;#Gi z8%{Ch`jXN2Bq&B$8jIzyW9k zo`H=<0f#l2c=+2t1erqt>CYi;6_^SnID%$Xl9qX?AWtg*ULRp?Pzj=`69{r#%^F5{ zmt{PUH%0BGn|<`=fCA*|L`*&a{5E)#6-SfB4F! zDR4iWb7;RgONR$O_`%%(vX2FD?q%EpwK|ox8~Yc=DWk5RJ+?a;>r$e8DX~K)Hs}vyG!`uLMOhsKp8N^x71{*u zS$UGr>+IgY3DTZE*fsN-) zQ6Lp6ZPfGw65y}h_e)3%_w6KX=dW$tBiW(B_QS8!rRa$?FIE`$= z`4HlnAgoZhbL52=r+JgNhhYfzcV8)K?TlbO%gZ> zgPUbFuRa^3+{jsKLJF01*=xLpq=*1(m@?YjZTyK&NO~&40PhzyBuw7K0S%oY%uVa$HoCJ|i1sUi6FuvD!G@3Q;8;+q2F(93reN@dujoo~!GClzw( zR97}$c18Is>$5!@7M$!FC?1Y`a`%O#LI0m!cLCAp%MTB5;L|CTxG=`-M!;?cU6EX= zjxu$>kEaHVd;0!{OsSf;Ljatl!x4v_s3UpSud^jUtluo6))GzTJR?5gtOZm|%Xfcc zQqyN~NnSfvuJ0XtgS1oiJiS^wZFi;_{+-$CS%d(TRgI(nHsHZpbcZmPo-`k9blc7c zGD$=iz^}n-?sSlYK;lE-ic;g84VI-BSl7PaHz(lt!mf(UiDO2r78oe`8iGSQaV3;* z_2$@Xhbkq})??=$&?zQ2@4KbIw27jo{0(BwIMO0+!D)fi;sMf_#5O${es=i>H{8wtDr-U#SAGjh`O!frTSkXE{bscH=v81f(I;CMf&7YB9L88t25v_f@c3k_XKrv^Ae1m#Ekmb zZ!d0ea;US0>>e<{O3&@gQN6!}+J#J~y;-4^b^lmSThs%sVRCEZVEG`5#&V^pDe)?F zjzP)&Q6EotlJ7Ck@0~ni>5eS$ne44C{v+cn$vkM z!G7D@dq8_k;A$uH!TT3dWSmRA_VV;hY?JHS@{O@t!@Q_WeAY4tyaRlv^<;k5i>~)x zN9kkU^(8y;hdtD-1r{J=F|6#FlAP;Q`KzX2Qf~=7^`tKMr%sk`eYgzG%l9x@Z0ib0 z+kRgi@Sfney)n~Z;Rh1ALF=$>hlC}fBnKS}3lL;S#`9a_-l!C-#<`Yr@s*X(qsa=n zjomg9>c0TZlhDJIl~ifQm_d0RI_?Jgr_~Skl;-|*E%lYHMp%SLtonyUjf)2s84}LFp;-mxkBkEthcvP z%^f1bh6{Z&flMObsx{wP0S#b$dP?q#s5?`a$>wOVq+&0FoyixuwlaPt_?`zsemcI| zj&CcB2Xr^XHFsdJ<94mJn~6SLlqfJcyq%TZ&!aa~JS8xx*!6S7n+>13!)jF3XxjkV zfjR=QLH-U}=r-Q?xgtp31u!&_Q*Wi;h^~YV8}L&mj66YU&lm2jqL}~`A87kw)?oOG z$Dj-a#5|A&%IVK9KG3IS5eGfxp@Q$Z)9^>Rei_V7-Y}Il$j&bbE(tY;F|C7GHB)e= z6oYRHn)Hu#>9EuCs`HbyHfRw*kz)PGOUjAyTPYqF+X19@vLJLf2jv4`Sdi{+iG1n( z?>$1AV~K~?&#V9)U{*r*`~}5ghGNW%)jfJWz+OpYYxn_P!RP3Y0^tXy80#bk`H`Dh z9R=U_~+d6vrNu=EWp)rvCJHwIKjV z@0&Ni|DWFqAJU#HNa(LQ;q4LaG67OdHRd(52cVlFwNOm88`mgD5bIlDW2DX^_l1T< z-Mk!)@KuUXDMa~`MThBeHk+sqZ$umqo#s<`$UtXwKYq(pk&O%9T z&wWV}WSytELgDxo!v3BxKV~AFM|bEMx*BdBSY`pe8rspf&>O#%_Jv#lR|EZ?A;*AE zN)PZ;H=daMUb7C1zsii~o$=yMrKA;a7f8IOliAcxy%9hJjA-hU3D@Et@4(4`UnOur z{r+cHf`9E`VLp<&o;(A0pRZ~*PAMa?cnwuX3yCkes}LUF;Xy{pzE%YCNNY(zhBDC` ze~HdthhiqUY>_L3oAP4rd5EC?AJs2CwqueDugN9h=PDJ3DFn=9;;W*6;u6XTjwwKJeof%Txrt+Ag3Y|8#ZMN6j`g|dz^*Ld{&TV zV}5aBFb4p*bd!R;q-KPj=#za_?$ zV<1qqvRZ6B?gVKT=guUvJ2zj#Ta^mLEr}z=tfL%9Ed79a{OKK!hXV+`ySm@;vHi%i z|Dyd=p=)J>tf9X_$0FYS-3fqzxN~05KBP1009`)xopEobu4&;Zd}HLAyToL#^Tiq* z;NbxZt5eYMH_#;=AIx*lR~pI z6-W&+j5Nj|5w=Euwfox>!Uf;cBTSnN z6r!XpX%q@m0aht$ADf+kGp47w|Zy*C_1gZ6TqGpnYIqJb$TrwvH-DGNDS(BiC z(Ng{>&iAcyh(Rotzzw#q4OC0eHPb6tslY3%yM^m-p{)sx4}V zZSu`)x$W_O_L!WKWrM9MVZ?u0fGW%NqlF#f7Kd%1B63RIicpFdM*5WMd382+kqjvW zfE^&VwvO95&D!h1*SrHPhBGE@*iRbN>7354l}W?R6he)Cl*V@_FZ1{>HGx}1M&*A3fNQ8SUW)8YMI;22!wK+J?z? z{x&UacL$jX;By5+aSG~ne-SOx@68z3EAlM?NH^#3V7U$JJxAl`bXA`I*DWt0L4V^2 zM#w621nBetmHTNHon$5{H0~6&R$=_crt09f^b#@T)u5BlS#Z<^wE5-$Y9U|Bl?RCryC& zgIk_m-p0koDL~NjvYNNrQhN|-)g0;&NndJrRo&E9{{y-GOKY;ym+gsyz28DwnxIl- zw?4ezam`py*w;r^pqd7V|HRfS)SP$J1XtIx)T`|e01Z;so*s@m-dkGR(*`%gmYiB6 zv8`TCG)L;^wX#1S$VOq50Eie8k9`v=Ru(61`m> z=Xy|p-nV(Di!_HR9^8mG1m<@W1w!1-C80QDAj7BxVHY$Sw%TNXXaq)B7khcq_R||A za+&^EZVQH{K%2deA<6Ou1;%5+BBJaVr(Hk!~3nWIGI_t##%n?PWwq4_9n#u9QU zRCO;Rb@lEZu{h7?Cq1h^N~m0(7n8)+VDSX{$jaU2J3z&L;x1{c*4hN=l5#uSI-5yd zTV9TF3QQU48Nz(jW}LCo?xO4Vra%wVmYir+(H|w6pFt2vhxDJ|p5)>L6J_s^DF1%TD_0J0wjHL)KI_NcAW)9fuI#QPWXz1}1QtPDr!MWQ#4-;959 zuc1}DkS zv?P)+-Sd;@djB@~lOKUSJtan+8fG`j(@Y;eG{_5lu{y%WKPmQRZm3~q_q57pT+}w? zZx@gezB;>>7w*PXnIiXeEMmp7ucb)?lR?Iv^=0rK3&bzz`|f;t!D~L0C$>7EbEnYX z>|yJZ61xm#RW)X8z6E-!PU>y0!@m{ZnLB|W@2nJxIe6QwMKP=p4;anHw)r*f_zS}j zKjN7t?mVv+Is$SYsF0g3z_S}}e+PphpcDYC!%H0YQCs7t@wXMMjAl~jKW`6Ttm4qx zFR&FYIY(^?Ocg%oTy%#zOD0P)RDv=Romh)D!`L*HX<39mQ#fhhQdN##b;?(Lrh36& zh8a3a?A%#q!U^^rlgiXTJI&Vs5a3;%Rk$#jwG6 zO4)2#03!WNxkso#C?JL(iGI?1V=0L>?9=ucpzeHm^$t~`{C3!1oi_-enmSeBORb(C zU3zdK#Ggr^M)GRr_BxhTEq$)njx=omcZ>_z6t_K<+b`6fpb5smhj^mNr#Cbye-aMe z5B)(3WB&rM;}H*IwQn~JW|)b}@mv5NqFu4iR#ulFAs_w(mCu!(TzFdm^#6doI@?ss zIKFAcQEVMxW5xG0XR;s}aBFbjMu9j8W*|Qu7^v&tH697BXxvdO^*4H#J_*1!q`QT4 zrrm>@ap4BGnn1W(TbEE@kjHwqQc?94=>17tYudpdO#!;$1Pme}H6oR5dv$6L1|^8Y zLywc)hM2&u2&#s;O8&))3?y#J-d>Sq(nk7c^7sFg{a`-esvjGNXU{A}!Q|c|-qtKI>lz!$J|&-dx9Hm0P1U20f5Sz4)bD5+uO}+j>WTnK!G$ zcz|7@%MsLN<6<+31&sIFjitDETy=lOmTw4|tta?gb2_T@T-a|`gU-aiJ$~XQ=yeSo zztFFaugMVSUUBbmKlb6#JM1A%_uWheTJPtfsx5WklIXdGO=se#nLq{xC`9P~G9;G; zjmdU!aRZ7my0uEGE?2h1TMJHYTML#G7K$(|@4d8;JLwe1V=uG@Tv<+nR(>?~17#-r zSeMGcI_7Op5C4rR^Sg}!L>t*hVMfrEG~E49X*-A4utmWIoB5bc$IL>jQNM@`zDH*PN2UB3jRWCf>CZW zt>7LFLE+Yd5mVp$V)5>i&~2lyrfCZTGs+itZi>LQp#qZ3G|~S@(^p4D^}Sz@0wN)e zh#&|I9g-q4^dQ~cE#2KpNP`Rw(h|}jAxL*9h=_DZO2sItGD`8C5Msl`a^jW_o{q>I$^(iECZ%3s&yVm{JyI4#Wt4@ z#Jo3)Q;olmub-OKef4y5Uo=n{uB#9&aX%48oDMSn{S?NvkU0FaPh-+z7nnXgQCFJB zn>NwNoE5((-mntfTg3qqM~JGg-A`vt^RG@X#e(SWLVN}??(;mmP9I4-i1tF8WVx;5 z=lUCfJ;`UpkMNQ8vPb(aWy~=O4D@twIE6b?JdigAlfo;$c^X(ta^&B0)TS;lG=FvNNpL!pV_l#iTJ+(M0&<=wqXK z27rWfot{b9MIV8&{=8OL5FkPDy?!1uJx<;Rh?}0eieRoZQ)Z*GLYbduRuQV#=R)|` zz|O==$T?!@YZZAZU^efC3M0NM`}y@IA=t;L8#3yz$mX+?biJtYo3!8^y~r|-r3fC{ zsm&&(aT%8jmM3FHW*qObNjjrYcjBY^`*v(`Vc?Z#HmdM;f1BAzPad{q;wbdS(N7X% z4OcNw_0t24E>*^`v^N;6a_NwJ@+4-RN{@|V2Z&f537);8qmJ~YB(Ztw)vX|_#r@XR zuj(#cl!V~Bur<>JCAl$L9V2HQ-vK2wS&t=cOwK$TdHo;s&>RMU8o!Fa@HEIzsSBcwEayx(*Rf%L=K_Kl*>iyeK=+Phb%wk)2vn-Bm!A&)}@h|L3Zku_v zbQL8!9VHg^;<)tVHx(wLNibpWJqar-#++cUl)p=4HC6kuEi@1{$-#gc552hRQ_@dM zkGwJ~7R>@0lVQ#oBonOuQ5T#;e837(t!QYVZ>**Dy%&go(6B_>p|yA^yq;CCdia#5 zzdx(Wgl8bl8)is=GtI$v%i9bs!i#P9yZ0%`!tWhvi>9dq)nA4O?N0!{Kl&o~eME{Pr%2%6LCN=VFEZFlbmZMgNj?`r6i3+t%=z z&)KxxxAdbOkErYy0WB>_t$n2-y5=-U4Qa{DwNbwQoagdO~HV~2_% z1Jwwe9JdS!BI0#Dy^S;?V&cSMcv4Y^-=PH9U1o3>FHbz%RraAmjK06CtEql%e52qp zmONRFsadWSQqIyIxWi9B&dY{C3epefSgnkykmDo@5|$|J!*QvVjVU)Ptan#~GFx-5 z|1zBB^D0f?J~u13)xY29$atlWUMf=L4R#!6)X&&9-=Mq89eXCoiA^XGOW(W8dngxK zNtT~Dr|tOo^fS_WrXdlL=+9MWT2j%2Kd;7n6P_gr%N0OX>_^lb01@gunDjk)oXTnP z);Qr&5k=z* zWu$y5)1RYd{Pg03nn%$43r28n!6(N$19IGBX}k8lXL>vt@$1XV=32ZQr$bhHM{6@N z*dOZ?+OvO)@aul=eOkW9s51fKw%w;BS=tfl)A^FT&tqxrY5YBY_o$n@YHUv0pD$cC z6g-=JjQV|^$}*JXz$28tEmeI!8LF`>g{2qgtQbGWlYqlCp0ZZ+h9P5K&8{QcfpTx- zbY>tJ*-LK%m0|`6l`^+*Ug9I+l7MR8oXwFfrhAdFfPfe^@>hKxkggHK0UDJOdT0U?(}AUUnb4)j|{IsFL$Lwa~`pOM5xI#(Gk>(9T^I zq%*-OCvMe#IK{4%6@)%Kq4##WAnt$bqu{446}?jru2XF;wIRsa6>5H!&B-TvI`k7n zWp6BHM?Z3`ibRVL64g7(*HV?nS1VN)HTzj9EA!`#JS)K-RON3AO)615#mD;efSVbu z;RC+oa%|dq!gYZzi;90|TI@YJi&?r21^uEG7(NBM2w}PY99XI}yKrWs)k5@S`8z_Y zXsk)Ry`JU(Z4NEvCa1tGcf~ri%c`DYj9O`eo}~LoK>Lu0IC1JQ6GSqGm9GndvRp=( z`YV+KrSaiKFQcg}%idM2erT;#fyG>^&e48tQfW{CF-VMoPl0mOQENZGn3vN(Rl&<7 zd9-s&!tbLzja2j|)u8=%&P#lkGZ^nLN#8vNSrHv8pX%!4|7bC{=1{n3^34FRh2? zLTAXpMkHG{!VAJO_cz4nal$!3tv7PsxMQHNF*DwzeAnw&)rFaG|D)*=rn`&KIi<>Szr z3%*4yVUs6uycK6%6!Vl%U%Nx(Ss3`FR+@@9F`9$IuSDbDNTio6R`JJp5g9zN@_s8PMRzG-Fk-co9q8I3;( z2l@S?#DC@38cbE8zE@uE980o)wXRW!Fw+p2a_cvp@{3jMae_*OKmm;S7rMF&G! zS;py~w~KlDqlLMFWlk{zJcTl#*7x82QemtAwiC2mS*M@HW3toxul`F;3F znYSc~nqw3%YvA<2 zzCS$1uBzXAT3j?e!r97KWwPAeWbuRgn_1&t)c%WF2g3Xo)yO<0wUN$4|Y+g)kw*5C`lZW>A0n%;N9sq9QY9=$7>{>*XFDis;!29G;w5 z`?%diYf{llkex#Fpq|exaoEQ9S3I0?3}k!4Y6mGR)J9abVdY2u!Y`blhkdc~-`F>J zx`a$92^D2OeZ<-LGpD<#@#C>FWk~LcSmJbWn4*W#?K5cA!L{+?e5r$iltiE2(}?g2 zHDni1Mn%)#!_3ZW@@;%K?;HCEdbgyd&>c|<-CL&3ESA<=NTNaCTf4f7wZE-?O4`&Q z-+8gVV;2s@CW3u>vDd~xN2V3#Vf0K8Ap&Qs{icpLYsC7evd1}QR;kvHS!ARmVOR^C z11@o(BK1IC$C0BfzFU+9X;W7nV0#*Q+X?FLrb2#v$N2u;A6)_z(k7=Pz#WY#y~q>s z3nCi#8F4Syq5nM*^WfycOlttH?n2UULV)$OxrFc27r?$EE*OVv|5>!TZwl+Vepy*e z6#Gnw#TX_5hi}(btZ8@(Tw&7&n9`b`C_kRJh|pXsiS9`nNSKKrICjs-aaC^8WG>b3 z0j=_!XoeDyxgDXf`rL7^v!ajdE@EyC7Qzm6G zZO{qq+H@;xtggy40%{mkJ~Aq@%)h(Ocmat4S+5Iy3M_|n877jv3!YO(by831=>_y? zHuh07vU;v-95e@{CmRW}_roKPN8H-g8&(b%ESRPJ55gG33JPj2OhLI|6z0LaTT~#z zKmWn-K_(%SQqJy*0fjPX_66J7$#|lAlsGlPnFE@YD;bC4!`+2<0+nNXn~P?5cANj^ znI9MY>3K*0@1ObZ9O|CaQv9mNT*S2M(i@P>Ggq4$5dNoX3+{8J`#9tVo`eSV#Kae! zOHP68!eJ7WLdV7^RaMDHaQH`})vn5KFs8rW`@Y}-QO>l{wI`1fpMd}<)26<;2-P^H z(>|~qDqX4dNPFS7R{Fv8-c~zmAKU_?0RIvH>wilcIpir%&u4<1cL(4ZPxxtDTbtE} zhRM4EZ8M5(*D(U96v)R+#O%ENvY&yA1=8%}=gzpR-!eLuV@As{KJDY6mkEj5&Kc=b zU+BRkBqqlY(>{cM$6g95_X;TiLjInVbi-^&WTcz9uP$i?lXf?Di8=|47{`oA?S|FF zb$SAv3{LFC*?C)g<^M$VUn+uhD&%8K7HMhfnY!!klZfqkv7tz5=~v_lR?ldYJ^-WaK2d(bPqS0OVEpYnbo+q^HzS zszL~65jzfz#fpF_4HchXGL^HDYKUd8So7KFn?Os^^NIW>%RU|!kk~>~g!M|`5s|a? zpE7qJb5NA|Z$PFA@)RcOos=druhbWip@Jk3h3;xyYlTNAb@5+X_njX}(l;C$h zhL-s!tDM$=k`^tY=>_rq!6^)BQp!GkA6RPmUp&RTA>W!Z7>toBZc9T!mR{&Mi9Ql} z9s7~nZ3+PM*;(beh=dqp1xh^l^MMb1$7*+SEp6&h|^u0bgV7$3D&+wE}NiH;+FP4`M zw486Vk>d%wQp_GkXRF2~zRY&b9!Eb*5CuaEHGg{cq}I7wE7xcDleXs%;w-Tq%zxd$ zkRZ?BY*yQQt?6wLR2xq%gz#6DHU&lFzDV06rMV`_DbIj_Kcuavm-`N2I zFQWG@h+^!D#7ghRiEZ(3`nr9gGClnUm|8msz}bmgtI7p z<=Pb&mx7JZ7z!>~Tn2CN1<$m=!MM#P)(s5ua(ArUluBOnBMhS{iyF^o&HuJG!LDXg zO**wy(MD7rS2z1Q?@N%^iszA`Nx{Tv<@Hbw{uqfn*Q{$|#XI@(5pI0owY8 zY;7ljc2fXyPAiD^eE)=Q&j;6u%;iac$4(RerEvIVI$qR46fJ(ZZ9b@$WAfKu6bb>c zWqh&v5G0t3-vt@;bnD1)r_nNGV2ph}s`QKJ2*J~YmhafPLB)%4@!6)6Vaz%*05p3H zJ9l$T2M>(Js7DS$iB#EHzeh`*QKBe}coG6HCOzfG*Iua_@l9I`js#bmd+u-MdQV4~ z7fb>A^gK(*SF&wILZZPyc}Uf0ilOHFpU=?)A|M+24oq|t;^PU>cdV!(N3PD(KsPx( zl`doXPY)*XMN00NGpGu;7pNvgx62p?sd|}D2uU|+N(Y}|ik}*BKHD@D1qO@IA7hKv zCtLQJErJpo0?#bLDYL&@Ftsk@879c)D#Eq}j9$HS=UU+d0xOTMB1Wg^2t1K|TE zN_~6u_Aqc6w(>_dF?_Yu8q44d9!mae3vrpjLm_JbkiE#xWmez_ZZc?c8Uk6jI-oNf z@qddz_S!WYF0`c7u@oA;qK0ewuqo^3ik;;LmKsPt3bORgA%*@-<{O;cl|a5#DRXBa z={{r_5rvrq6>0OBC6Y=QOgf>PhZ#%ov$;vn9=D1eKTTz5 zyM?dz-riOsxy)!PS9vXA^KbJx=E>&y;DINpUhhCR(mq4L>iO`q2+>&a>wq%BF(+#_ zTDU4&e*$vcsYJwY1M)Xj>wC{bNBEj`Euv+5@7pttq_z|VKw;Mfr>R68N^P{@s-S7W z;o-kP>7*Z~8i56Sd$-P!QP&(##iNY5@?xkCN8ypa7YC66Z+#qb6H~p z?YjF;Y$<$>ekp-0EOhwv^aZWNii#cz_d_FD3fqOfccm7k%A8@`qUM4R)7 zU&3?$mXm=X_p&?S$J7A)o`e4ppiAOXU(~cYq^;0^a<-kE*5thR6F4iPG{FUWr*UaI zn&w_{unE7pTLZ1d6igW=-tFPj0e2keUH@G%ed5lP#%T-7J86&bUyh<*0IriWTYFWb z5^v8rJ3rq9Cux|o_Zi_qN&rO%0I6?_zwK+1ZeL+76CkJKiydFMpU#^urrt9JkAbkU z^~I*zrh=Q$;4Qo5>o+oz!p0>7_~;AnvcHQ+a6)zonwX@Kz4K>M`d~`SHo^bB0UHZc zvT)XoP~h1cjt-41ovy)9q`_e;hADv+{B-<%Pf9f#75CSZ-=(FBed)2=c74Pyy7W6f zYGmuwVz-+vt9%2t++mTdf2)yD+8+ruXbeAog3ay|IoKq>ZLT)MLni(!z@_1{hWev@ z987uXYsF9!NjgA7W3Ew|ds2NhEa9cP0AP{QJbK28e_UF6A+E|Jy?sfKRIq{E*oqSE z5^8&R7FEHA@v+)-OY?>_aLcINKU5?481$m(q840xk#o9!6 zCVhgiT%cd87+-Fe}fMEw{qi4#F*CrN)_9Kb3_J0Eyvgy?>npk5wq_7;c;;gwo* zH-X=$fy(jWP4qNi>Ep+v#3iYY_WVjQYdik85D2)Wg^SC3a5w^Pg{^wOKkR)G^lUcG zL@v5tWTIHPmN1TU?VcyNyKp$PH;jK>A>+dI+h4W1l|eaYm4v{*29&O7$db+J2H&nlG6RZ$~c{9Zh!}>BD_0lC{!mZ`p7-M{-fCOL#*z9Vh;GDRetK^z5@!K8=XMxRy>yF zef_UA1nT<@hvrm`k-j5xm>}LFAy;X#{Ma zPX>ucZUJ%PRAO^^hw8(mlmGhxNQ#%?|?D`Md zWoqvnsuu*5W4ZLfI-v=Mln7XUSG(95y3t0Z{OW75!lsh|aopTPi<7f85ps%W+8Gx^ zOH9mKy%6BCH&SicCjn26vZDK4l6$c#nr%qRPa8|^Tm6w6mj5llb?z{pDpT}n3R?x0+fkx9^W_vJ`uZPB(_-`$ovG*} z-ZkdB71g^*utk3Gue(8odjE=6Lv3fSFy>sp9%=!~2yTY>J)ry5?De<IRzh!`<_qzLvJ}wOW+J#^jCJte;ZvH^ zX3{ed&`x~A{tu}|@bWVHUe$Pijz;I{?=$7;2n<=MubUrJ4Twc|Us8JdgvhjCeOGEN zh^*3!&S!hH{joIRMI&~*J-gP5aQOA*{(Rz-?l<$EI>VSW%!*O6a8Ws&9OH$rTV>wQjNOSHUcodE>Ul$HkX>4y>2 z3@J=H%a)nmgk@umz*OO|US>=IwRlS> zpkc!wfhi1Z=Av&PFcsDlrbHRbU|27?=Yt8&N7=y#A$?S0NyHHj+}4|UdgMJXH>0n5qqW}QH1#iMe0LKXoYUtWiEeRNM3gq=&lN&SK5{*L zVrdA(4QVUvr%6>i3lQ^-T7}%pLM19l#nql2>c_kxDv5+R@fbk5rGP*q^+(O1d<&V$ zr^?-SEXdE4&QI(=$v}Lx%-THV&#oQOJ>CAyz%en!O_bqskkD84vg z+=Vec+z=T_=34NPOS@hWJpG<6rz0r2?uC@zLvxf3w!(r9PQkDX!VAq)GL@v7KX_f_J_TnFSbaeB(E>6WI?_X#&LPkJ8scG9Z?)< zEHg8~Uy0vn$0E^?43Mfv(KF@Wo^3dtn)gXQk8${tjHFf{Dvbn_UwK)@*%*WrJ8XW1 zNKiuAm`g5@{rUv-f9g!BWcESXiWhFYXvs(wtziK5UMFND6zD3C0D$|b>V{m`Vck0)dVrY1R{>8m{tcm$@ah4TfN4fHFQ_qjDKO$1vGV?ZYqy=OP;WheN#;3Xx)1z7t%Qe{N- zHtcCc@9H$?+*!f?hHQtQ{;4n+c&`csS=%~4-!}*C3bw*p|Bz7{p@g#$K~NBa7Y!HU zePhG7R}22+FuCGDUv>S)JDp&@AnAFs3ZRss83wx~Nb=syS5(sVagxT8Z^yCY`RSH5tF}lo4Ml?yZ+}KlsN$Y zKbwf8|IEzA$yph96g}VAN!FGrV@-SVX23IxQ1i+ckBxN0<=MA+o6P3~QNM++F&z(K z)lDS>qQ5xl@W+Tb7Ii^hi~ZntqkIFT9T`?mt(51%iw=M8dGMOk(H|Tgg#jfZ z572o;_hGCP@@+I}afn89$Wy!Hu%|+ zqWosR`gm=zSGJpfG$cyv`gMO0jkHRQ&uY;44hwsSAT39!ap}CZ^e-jpsgwO~8H7k- zrZx2&(t_$NQW1xR4bg}yfj=I%4}co{@ul*1j2;OLoy%pQJdWS_8Cz>Ah-~Qy^OX-$b!Ao76X8244Fv(~Y$htt;sL7_5D({^ZQHoB@7rm{) zCVa28MZzfgg3#3`P;e6(BQ=#-uLbMIBs}{=M=#wQizJ_UYZfMt=WgIpqBOAcqBT+Ds&yW zDVhw{fq7I5b{vfv4mJWIf?C3xYP_0}4m1a_;`Te+ge!0e{ zQ2!MN8{b1`ee{g3%$WDz#F%Ikp{K%q2J4n88BZg^yFT{_qUIYLCwyKHC4Je&cCyO+nk?wbCiS1J#ApqaFCpxU z{rMfDY6-73(I6vI z+H5R?@b|B(9stCx`Vf2)cxH@Z?BJTfG(d)a+`Vth6PUkyzm-LBoA;_@>{5Q5ao?PY zpr&|Rq;TkBf=7S+GNyL3{ynTR<*RF1|0&#K#;T#xXYt0gAl zqdxg6zUS8n$qTOm)(0rVI3GxN$M}T~&=3AG`|~SF?aiJtYKveMU1N zZCU{mSSy?5Qw)%Px<_JD@>4|F=O7#c)sTrjUV^w@gpF_Z{C$U>-|SM$NhRnyZcAgJ0B@g+vU04S zJ;1oP`1~D5_eLndiQE9uf(_)NIgqkf7qdZM9Vl`Sd+!` zH0$qkK>aE~0HnY0_< z+6Z$%h1CiKp>)Zu+=!&qs0kTs5e~+OgwdIVn)3sgvYH>AN}X#6BN)RZNk09qvj@!Z zG^NOtyn%1L-KijZLZ#ZIar#`#D3hKSeo)plaIyn6tc}OeTry$hH zWbwT4N#PLpcoKLnIi>3wKyYcEM%4lc5#;;xCn{OHa`H@p!|w=t(DQRzK zfoWAC*rw!!#J(D>001UF38Rni@o?P(wEFXA!EwP*63_uwQpB8U3CDP5U5UF&e+<>v zeZlYa1)6(w>D_N`$jWUvDFpWaspEyYP#UlWX}a|}i$0nxOWpZUwkKBiSEAr*IAnq|v;IrjK9?FlWPJKD+La4knX)Ol6MV@adoNp3*{#L>+{ zS~*+`mk(t=(JX%qV*xg1YkWklBeW>mi>czL21I7mApnxoj}|Ryb2Ol5${4SqJDf?E zW!eXjPV3y_-CSZIR+C`H29>S3=UM4}B<9(_;PRp~w*x zN_DtRdlpkg&gfemeSG8~1AQHaYOIxj5g-doPIeRoC5o|Fo!36mnMa?SphE0yubxsn ziT~Z|i)?JN*y8>j{6z5Pe7$wv zQS>j6`oMiER>7N%xa%;Hstw5Q`40m`_oLPy6EV{MM{sD^if#V|n*)tTvtLQcS+UJ+ zc4?SuGB=>_Xt@t{NXC#T@o7>8 ze~a&FGwX^ zed#i(n<)TvrYty*n$g?H)qV*5$%DA(9xJvGBugK8YN@95R5comOLlwcaPnEY&NEO2 zl~Bm9w!@G>*{vO?9JuKmMqR#H;Rjj6r#9?^*WSEDyM2J4Q)1d=p5j7A?#ACvw;0W! zj3w*?g0%5pbu*i5p2uPwN?o!4Nvi$vak|XEu6YX0^)!2;F6ZIjutAgfw`UiD-d2hZ z#`%G~bDPZHm~-vI_I6Wr|NgBcPmQLFK6vuvNx{;SWs^x*kiI~UrChp6$4x`U3qu}o ztb2T{M*B}(F2e>!iVn}l|FJiol(Q`N^!sevcABdPG&~QmAk=)LBkR28Zi(yOI(=+A z5gJK5?g5AHLnT0Cn`}gguiVI~;w*GN9Kv~3J}tCSJ7v5S2R3#XXAmp+x^&^cxr#cq z{^P|$+gTpUBcH_DXX7y&wIFAB08o=7evp%gWvwasNdAZCbtCJaq>4k^UP!tB>u=`* zXnembS?z(z=^DBb8jF*IXi2vUb)u(_S~2}8#0;9IX_ol;#2o8-cl-DwC!&Vf=~40@ zeLX&TjhdnWnnm{idLDEQ$|=9&Qf5`=fM9vQQb1F*!$M2LX+DI^fzkrXGLSj&{GA}{ z*XUXZ#~F6nzWp8OsPxw*n?}O4{4W&Ea8rXj&gr4>l%nC77aUQi&pKMT0OJC$0#ou> zB2OiXX5-i_KmF?qXlentzkl0@5@O8n^~$>GkIn)2RfoI(I)dw#5Xc$MNO}E}; z7np(6@5TJWf}S#OH-VRp$mM^L`Vo@kuBXS#7SN4Rxo>MP90wd#;lso7ON@S)sPFLt zCf1)nE#Em;kcBV0fTFwcb!hNzbUNYUblZpy1?)2z^2}v53X3?nN#~DIidl?cc)BfR zJ1`CKX(H2g;!tKQ1#Bh_Uqx2zB$oEnLcCwN$ z)G3FuglvsT#LbCiG{?(`6+*!CGSxmKjs;ajt!VWN{*DzAfteJrXUXgQCZv6?shO?% zjy~sHaPN+-oTBq=tKL@lW9!izDZm_%s`ED3%6de*6yxd)7;Zql5xJa|c@N0co=!N)6E=yUa|SkLo@ZZ$rgx+yr{pnN@8N&&%Gq`Lwb1oc7NUQdbpU;8Q21z$F*5okNChOff@Z1EBBf|9FJ7`nfiAns0i1Ynifami3(e_zgRw zCr=f@V@wOFj!0hzU>FND=)-`3VP8Lc@Vq%K7Y30PVjx)2pC6djs@vMBz+)6fwnVIf zl$M(64eyY7BL@1^=H%~(xAji=!Yc7V984l-XihqYd_l#%E<&A9ykB2Z<|ZxzScWXv z4;A@Wp-j6BKa$nVxwg$#SYLmUSP5~*P8+E^+5;AwQnBGOZDAfy_)v09p*P^Li}mEp zz}qHqetCTdjH}6e6uwyiqsnsgBs-(@8cpMe$E};%)nt1HCmKoBqHa#(uQ~K%Y&yuu5f|fjK(&d zT~eoJOFD%w8F#@CgaKn0T44p2n-yTcD6kU|&$pvlg*klM`10>lroU`>jxk4z@~f+> zuP^VXBDFx{U9ax2)ra~Yw`36(JS+!sSmRjklK)P4OlZ2%Gv=>*T}3(q zmRX!~=LH6+>_a=P!htzAhg^XcE0h8!mQEdv!wz+G=k^bTzR>|5ZP&$AOXLt4t46n_L?Pw;oW~>g|v3{FgFR5rZ9ToXi%VKL{mvk-}{iwYyBYx`F%)du){)CfWTb{5NvzF z1n$&~E14<`1XuLFiU7nJB{Z~}Jn!mnmcs!~5g1VVh1DLi-#6eYY*=RjMAs^84Ob#& zz|GW@90mzyjglo;gte6Fmir*&Xe5MSNwDbQHVQ5$>VN{_EqOH~zr3Nm+fH*^5KTL%8!I&;?)sdc*L9xuoK15bIk zl9M*lbTMbIKN!QINe98Ew9#8Fqt~w3LuMV*mWWMq9J+O_mNjM3hZ{pKr-sRu_#(Y8 zRN_J_$@fRHs8ZS%(cTqN{u*2w3}(Hk(jT)xmRhI_JNb zk2`x{$>fbdOu|H`G3dbx9fh_>!Va!{KOY^1tH5)EAvWUXv_-8~HuApqi@P+H8%GnO zF!2>{0cxk^f=v9_+^<&Slj98CjY{&ft*jroh6=Cd5w*IjFWxYoR$s6-V?dY_Xg?FO z-Xf8>5!dDdkdCv79FWS_Bq3|cT9e;GM81-DCpO($(nibP4^@n{cf8-|Dnze|D1B-j3-noTq5Gl@yp`cK>-7 zBKFj>85{nqJgK_>qEo#k2$*c#LWVwrfTEvW+~+@{5~m+W5;FV|o{B@h<+($~%k;J- z_JFvi!)dGTF??>0N9@lx@&E4yu(+)63R0+3g0;A=#Bz&AbJoUwcHL_YPXv? zuYJeKR5RtUm-eie1^Y2P1u@H83-|WaQ6H?QG0bscU+m^Vt`a#AY*PX8<~#op9+aeg z;JxfATtLa57r`&FsxmCpT{V_kJ*W;*v~$c9%B! z$7WIY61PjT5FNB^%^3H~hrgHw(0r6Tj-BA&qkV-H>&CAXb9Xu(uBF}qu=V)QW*OId!(DThikuFweLr859i z%LN*PNd-u3R2~bpwy(dLo8cdkrv-o~2%e8>18Vs9?_b%o&+?feaN!0F$S76tO}=fW z(@iMcQ~7vjE>V`GXtz2^Q;p{?8g>8*;xM$qYq2u)^B`a>_|gJ-{LvCIV8&}h028&f z>-C}x>D>f}Qgr4TEE9hjVGu&8V&$GH2&^|F_YeiHW9T%N-qafJ2&8<2_9Bq*#CM^N4T!trJ+ut;A;vz2&CHh8z!U#rknNQBuGcr<4zW88~wrt{`mqJ0G!|%wKM#gq8E4fUHe6HV;7aR;gZ_GrS z8ZXZiE(gsFN^)Z**?NRxMul-p#XWG-+RNawZl$u3^7o5uK%?JH##h=OK~_$c`^N5E z$#9?E z3{`T&!J65vz~ndj;Q|lb(x+J=t9M3!k`Ah5rDXxY z6`SxZVCA21mIEPWBERc{Jh56*33EvitB`si=!v!;$}N`9kdRb!Yo0vU&Q876mGRZ!4z7{*qz&1@E`B`yVW)njVZ?ql?sl93#b4UI60hzmy$F3 zRt#E&%)H(ilBNzKMf?2%T^HxSa>UqP{cM(;@DwSzJw$O@soi%Pjfynnt^UZ>Z`XBq zoWGK)IHev?OY~*0VL>H8q;eWGMbTJ7@}6`Wo@Q}zSnwXdz;vv7{!5VMe^T%) z0JtL;;_)5+p{pl+e#2*JhvciiomC}gOk9DNLhaY51_8(uPw;eMY)F{c09q*n&U#4a zdpfB@(2Fn`WENvkLC=4Z?qRg80hcIR=gf+j1uA^YbzrRAC32${YRh1c5gj?0h`} z^eh~o?qKwTNCfZ%{h0J*m{6EYw4r;9^}`YBG{MVO)1PsJNmu$%fq@{;uzT#921={;PVd#8oMx(K3SZMqE>QmLutSJeM?!NW}332mNZFLIvv?p*L z9&LFVzJw<)d`el)!hs`*@V{e|3Oo?HMn)F=Rd3$AU7?;k{>;e>ozIUq%p7o?Ju-hG zE`?t!+IVCPwEu10o{Qi?l&Ip+?-WJpY%^KZ$F3z=c(5G1(DymfkZE>Naf&+;_ zWcI``Eo9?~?8W@F+Up0Hs{>%;e%+i>b#(Xgw5Lu&IA5}XXYqo7X(H%|b2Kpi&NOcE zP>P<|Tqpi5qZtUT}-Xp{eDl+Ijhg3UWheG^KI5 zy&kG2qM6Vuv=c1UHhK_c2TA8yojT3^4vvgTA-a#iGeyTl!dr9YWlr4VSh|wzW1c62 zo?f#zCk|gjMD9YO<6QF_cGtdw!%l*Z68BaBksQBVjPl6317_%2N8AmT!uPuFH3Ih> zd}fVcc~9hN>||}~bH|Xy9e5eee-6XbK&qc0N>_kW{-eFrJ+N=~?`@WTJJ!44t^dsY zHthqmY?TotL1{Wf^jP|X<^jOXee2OHX+Jk?+4}A+bX9+%{jS>?l3T}|xpbOv_h$0( z=fXmZJWyrn66+gohh8>ofW`+5bZAxagAkIULbqY|2BF$`uwGYl(OK<(ln%z#=ht{U z#5-HyFy=dtXW5~>cUOZSRetKuEXH!bHhk=e+IW0sn3kx!T=&ElAq>9k@@x$Gbz=j} zaUPwKCZb>ej4oH8z0Pg5RNXmeeZv5tr5I3RC-{}7mmIkP(4F9StS+`)qEECjxfiiE z3e@q)lYf^t$U5{PxfMQ|K%unw!QBvyi16F4zw<4CORVj@xSvwKo&tZy`3H&&cm?sJ}Be#zO~P&fr>4moGn ze(AW?@5y1}cI0D#YTEb?P!qk({Sk^G&Oa@6jFWSd2W2KTsf z&RNJZDC4Yw0L0(hg3B2xZsT;MQ%1e+K9nir8L0F(sH|$&o!^yrt{1t0JQ|E@3hwDx z5+*?xcsWvLOU3tt)6Q_X$CFcJ;QJ(S;PJ)94R$lM#7GSoF*Tt3DERt-MmTDz(q!k8JYEO4Q%&YmT{%6FaX_GwRfDlu)?&lEf(CZWBd%L+ zF@`k$ViLaB@6-4;)rh_t@AfCOAN-a+$$S1lpP&R|^1k{{8p%rXmea_m2a~W@U%%d< zY5~Fk(#VHu=MwyqhcVtufm3;>vF@e%>1?Yv%Js`-(*bXrQEK6%Ami#SWdylX%tKC&ma$l=lTiYrDSN9v zXjW|=8~(&~W9W-Fc@<79r`C7LDb&{3yEF(4Y)OgL;?03=#BD}`On6-0n*5oc6FRiP zG0rXR_c`L`3>*91(nVE@f^PtB$>*6@)l8~3))gT5=lp)rj7E6j)J$H1s=!cA{_x|K zF-1>*M@H{z;1{1Q4a1Hb0buOM2lk;C%fEGj{=^MzSEbaq;=d=luZQ9FG6NDA-#eBgjkI!4s1!y?o*bAGhx;F%Gqzc zyIFV1zJP}W#!_`MO$T)#05mKBhlHaq-_l75s7PGCoYQ&kY92KYW$40)JZl7VEq8U5 zPD93{E$du^_UB+a&eDc*0?!RXg>aPlc4I8eJtZB7iX|Z~)>Ye{7A?fHbPe=9G#^5y zTpL<>PZ@dtY0r(L_Gm~m5vtE(vmvJd0Y`tL+a9`%tbkEuB^`h)u8eSkf|AT~hy)1j^$pN>MvgAfB;4tT)w=bm6 z{C7;cc+UVW%m8zj#yph>fhDZ}wX5?2BwNsJx1Xr&qnm$? zHIi|*THt2^kdc{{UR@5%;FdaSL>C%@ao>z&7B|lX zci`O4;!^>Ou+OtpYxdx;v!eCzmB0_{-ce^O$@Q;aIDn?wRswwicqXtQ*af*M?@Jbx4%8b?UZ z0LKM5kkFW~-75-uW*{toLk}BFH=Z)k_s-Z1xu{?;>rTu+NC$s(fNN(hHS?9D+^Y7ubHbRKS8VMeFl0;TW`a6Ti}(x1b+6A18)b zeb0l~=&CLwywgXt6&pbDC@hAaEIQmBFhVobs&Xdtd7=em3>qCO*7%XeGrjK)IFL?c zUeI9`4H5J_A!G_m+s07b-M7M3KB;Kuek~FK9+U`&Va6;N7*O%gvwq5DLCR0+VaUlC zcpy4P2;5Gm?c-U+Kr)KR#{dpP-_DBo|H9ft+59jv{f+Gfr!%M_Vzej2{BM34%KMs+UY=9 zeqeKpoO=*w4slFmFe8h(&GzZ2XnT9M34asXx!Te8lS6EOVsKSTf%^uT8&mRT6*JdB z90PIX^*b_JFlC0s+*$pq;HLZjl1Tud;h0-?0k8eN+Ih9}OTv=f6sts@1RsgaJaFx% zyjdf-R;b0Kf?w)AH4%450mYiw%jSZ=H#c=B{a=?YApN@^k)*J|K&@(fknvl89Co!D ztZq|5A1pDTNh6!s5L%e(b5i4wWIap(GQD^oj`BUQTKlFA%nu*|#H6Ls@%XXEO3Ltr zcx6~Fl#H>VrVsFp2wgW&9d)5B>e&*iw$J}VALO5vi-q1XgFIg6)qr7>k$ht21xYml z5K!Kl*G!!`9r;DS1(KvxcE%-6e}Ssq$->arFGBFfBh$D5=Zw$>(u-z zvtdWcG5%EjC>KCj?e%3W*w0-(9&iqpS|?X=6&AemTR6DS^B1o9=W>m;@VvPGhLypk zjQJ<>6eQv}ZSGA^)cjWa>HD&$7m0-DTB2#LV_Y+mMB;6QzGCjU2Iw-=Ghg~%h29q= zx$T4eob3*NJ4E~x$}EHVuI?2?PW?PNL7M-LN4q^!jXogU&?69Y+NZW1GI|!T=VGyV z=OiW=^xBAxjm^Ab?wM^&B_1umYAFNoFB3#LESdx-F}Quh*6Vdx8A%8+jag1#Gg)9M z%q?2YzuMa@2Pv*`cV%l=nFDQp{;O-p&q0>gMa5P9PpT*P{Vhl=gr{3vw`fi-S6KlG z>hSwY0zeW8-kEea?X&)(aVwBd6vu%W=W4wI9EzHN1SdQFrAjc=Z)NVKfJ_@zmBJVao~2pRal-`)yFS@3olF7ur$|Lp(d8YI2y_dSOhVz&yTe6 z`O0+g1mEd;^XwFf-4a_9HRcW3$^kQN_tb3U#J`j)$HvFKgObUC+S@v)P0vnD{0&;n z=yO%FP)=x7U#$d6nvF_5x31rPXUd(a`b}khWlV@4g$so9A%_Plrb9?7DMHHuzU?5W ze)%2E4_Ox7Y5%F{o33M!X!wgV6DjCBmeN~R0SJISix20%Pxd8!Tw^Y_H& zyn=lBwc=NqY~5ykV*^HlZ(eo&a8O@H&r~-D4Ph!DP#Qb|&Ne7jeorp+Df~fv=&2b` zxcNOx36KU?+wuqrWF#|y482OITmsn(Zfv6jT;|VC^L5JJ;*$+kNRm;i7W8CKsZ0knDxdAgs_I^W&Wq2NbyoWiJvh!+PZkTLY&7rb+gBSP zhnYtkQ<#jG642%fTF-amn=jL1jl*=S-#=q|*I8KcIK&kGKmT9#^h|G_so7ilq40sxk4X*sV=Y0SwHhAg(iH zNn`v*0z0nk(i#)8_|K8fMV?OD?zj6jo}}6j19k26rd@*0{?0mfnZ-9bw%p=o!Te1Z zkgZ@$^aN!fsSBJITxUE;8LID z{?rATSdcn%cy1o74q7e&UVcP?=J@e;OpMU%2_=;9M@y&T-_gtu3YXKEsmb{1oW3&; zZI$u#uUVv5iS1U3`;LQqZP4LA>F@aotY-jK2?HdYW*&KHst`oac?8_)TyLuY@SqXN zN)$97_yX#4zs&4EFLiKmp<4tFGbNyrg+hu*!UOR-^+BRTg}BNbsDFIhrq4J{h(N+Q z5;Nt-1qpKzMx*25c)^^?I&yokD`vtc1FSP&Q)VXvAO`!Vh zF00J}p{UQCib7m1DmKimC5I)5WV#6_a+j2)AlUC6x@g^^ufZbZjmGODx{fz>my*iL zmMlFllGff2tAU{Yo9_{nP>Hf>HW3yD8Y{^V#UI@MTjCDwe7j;f;6iC%HG{(r-RD1qluJXk_ z{|m)*n3_LI8!EQ5cZa>sA7@YY5W_E8OG+X~iNIaUFL>4OmrO=0!>5Ih|C_%J??Xu} zI&Ne}1xS@iNP<7+RTy4D=6z45FPGE~+JFUunyu|!X-tuc=rQZc9@3>2*=f`wfn@+=~rW2%9%|V$!?xR*|4H{Mykx8gk z%O6D|662y$i=;p+t#`IY`uRuA{7hSw;_J_=3@d5Y4iguUG{1>Uh-rYzzaAZmbzO;Q z%-4c4P(ur-nOi0ki;}#RA!tjW1)J{EPgcz|bLMJ8IjSxFa({Y5s)mx*e>rTV2Sg|` zKMR^3I*z(yVLlVEo_lzO#r{(oZoI=U_$ss&6}vlTg|BS6nSaKxOBi)P5m=AjJ``aF z5t3@OGct+nIG-c>yvt`~X4r{cQ1&oovdWOW1nhBps3P{H-rdFJ9}MWidQfp((4SDf z=SaA9U!A*>RU+b2t{J2u+sb~Zs{^ee*>h- zUY8Yc+h;^&MfEUsfkTU(^v9VP6%+nG=+YqJfU{hvH9^MW$Z5XhOB<;GdVD!>npa}$ zde;Z*i7jM5Jdfbey9sf>0bzC*Y)$b{N_u0px@SJKr^-a z|HHDn)zY_NWN>3|4>46W^#~{zw3xUiNs)?43wIJJ;v$*O<@^miW^L7MGXe8#nR41# z#kfu1_H2^=H&F@3lN5|k%Zy~kbnaLi^*aSUa(Que@&{i`e^Ti^QUYbyj3H_(jIl(G zWCY}NjsX?Xi+tp9I742>iQN4Y!P_verx)Wz|M8R_0H36MVgKg>q)b4S?8#!lTD2bt z;yx@oSt8Pvl<;sElsSvSCc=&rz2GHhDfslRpM%hriJFL)|d zwB8Us4P~}jcGVAaAhA!hUuto_2>VX~px)gK45HE3jN7!%f~;gjQT)wcc4=&UGsn+8 zr1)zvys)g&Nasx~=1ifygw&zb2MlkZdNv2*jgwFGRF0nolLnq|Yqz4yF-ZXmR`%Go z|D$zQ_khs(!)SqTw0+qFciEKwe9~I6v8)o%`eGF6ZWNRYl{HEHFk+->B)eH0E%Cz+ zxc9M}G;<3akM90~{ao%{a5Kk2EoxBzIw(>gsPIAW?A*|J+7uSxR?q|jJjA_J5j$;B zculG-PiY*fKz0lTZ-InI9BVgJSovHua&t)GkBD-?8rS-XlzvnfDah#(2WaEM3dlxS za%mS^lV;aE3VBf1#b>kgd(e4Afb(E+lX#BV;ZRbRPuS+};P!61X8R;Vv)=iG51rF} zV6~VOfgC6=vUiJrD<%Y;|B^=UI{#lTK(Z>8u&wOD<=cY+XY)gE&`r&*c*a}IRfBgs z^yFYgibcC$(0S(bL2c2txLl*^Hxypo@aS%#YqB$OV0V9WF-y2|ve${fi)O6L>aizV zYZ=q#TL#H?BJ(Nyz+P)0zBt|GPgK#``Z#>W1G>Qh&L1c_LfpvRZz1=Dn5a5^e`8(n zMhh$(f@46xN}Ykr@)W3Eolz+=MGi^8$tbMNr|V3W7E!1)Xa;7nn$A`WIoDA%Pb8~o zoS|x=gU58vcT7PQXMR4R9#kDnTVgk^e@&KO1_*6c4dF15$#}xX0cLmz`mKHO{9&X^ z<0zj9`}zX~1e9V|9mpr@xpFQfr#47M6w$CF@_z*yx;?@@M?0NkQ~~A0?Rr!paI%A> z``szZ-W`!WUyxGJr>}GXdIp2{yUWQKE5cJjHs`n4{df*9yLjH2oKR5;wbHk#knL?RaZYa6?p-1iCLBJGeGB?ZcoXM@3YWi7wk7Us2 z(t!q1bT~-}Vuh9uwC$hLHb5a0eyO3O$W~vq$=5V$+ zlfv5Fag}vvwcwl5gg>&(mgn+w#TN8#_peUErEvfbzj;3@t{98)8_bawa1Z(Ogty)H z+$RYJjDP};G@_l}{oNM|O5>7~y^Nqves5noWx0Qubt+YqQ%A!FR#8FbwO7FRc`7ND z_#i^)wB>hrpV1L@idqev0`KibgM(lZsm!bTQXbL4>wJ&}R_e=7#?v0gB&A2 znurOuKAwxt$E%Ar6a5|CS6QFT2B55L01r+UKYviJ_+=-!18NjNYt=S#9R*`_aWW8f z&2ZRu$?M*5$e(^6^xYonrPui0Znf1GExdszy-$U++9C z{Tj6dm0Qw<0Tuf~Vwo8IZypr|pyqN_)cISE-jafZGE6e;pKm~o?-HPYBy{!UWZygRxy{tNUMPaWtu!z1;FHxfwQVQzR;_HdN<>K0EaOrw$FyR zy1lbFfc2n1W8_P@8bUXLR|=fU-2=fLzZ#aQuyl8o3|QE!Sh!sPq}UY(2548DcZNY$ z_T!xcp&>kh&N-6Tzh!j>!@kPq7JxL~2O<&{ygjCD1;OWkkY($h(K@qW#{yr~*4E^p z>YcD*8;u@`{LZ^?s@tDCuB(^7ssOq85|tdIU#Kcq*G%hK`>OAyX15?SiTWXOs}_i- zejA(mKBC1=J9!PL#o`qrFA(*7D|#$;+xZpcz1b8?v5-7#{en({4NxlRpcHv~YrQ5YT&K&$TmvZ! z>xB-#WN30$@wHVFWoTYiFAchI)t#}bPUJ5?-L5k%2R(P)au3pl8|&5V(xy^-g=BB+DXI}`@cDJd)8g~hSC0B29$Xl>M6rP7UWps|miTF~JDB;>F~^KB8dG$Ft_@0s z1le76Lmvd7di@mc-_1S}HvUBnwr`Gt$Yn zc$oNTKh|F3gVqXiIoTpsil0+C6%`Lbvt>p%t9uE6VE}CNlz#`%4zKDZ34my1nVqqN zqw~MpA`sYhq1?g|a@(8tk=TJprBU<{6HvnMpH{(s?KjJ-S#-ylMYc<=Jm|bqkYyg5 z7bp)1HWFM0Gn9OSrVm?LIq*m<-Soxkc`|Rwh>2npK`M=#prt|(?9r05MMguX(TT*s zb7h&(IA}A3wZQGw+nF})3x5hZiWm!x%#n}M3`2SZGK)dMA+{HTSNTbqdA?;U$V(FQ zmq4Z1hm{*_a_3z+L}5?AmRv{HueL>P{U>i6wCnKoCr|JL-F^rdn0owyA(_yi=UJ&x zQFzqpL4wOWqivaQDWcG4bDx~MDnjpE5%4pdJiGRMx+sDED-n^Jbi_*}GpKLE);2+S zsT@EM=WBZ$=fltLkDN(F>P${CIt85xTW{v)X1{f+&0Ub)2#z)rF#ppd7 znq(2~t>7qlP*N6h;LE5xvXh^(?{J&@QQEGRqJC9ms?NdU*kJU_ZOs26N z&n2)=jNl#W$Gl7Ix`i`Uv}8?nZj%yRO-H1(GW+bXxO(LxQfjQ2<0Q>VKo9OVshLO0r3Z2 z=6h+U!o2Ctd8tF!SG&J2Vm_X`GjM&WL`z-%E_j;?N51pZmu%O{aLbvhR}Buw;ajLyHjr)z#C(+>dM-l zqrmacY>B}*E0P-T$+5OfwaeesH@TNMY|RuTS-gHVP$Bj^{91MT+i;~ z!Qzv{WBt})y7l~RzQ+Xq-Qt)Nhy&dS7GOubH8FWbUd6VgrZ7!W$!yYw(zcc+$d6^ zjh1U#Ra-Xd*z6k4UEI=?kyg~DlssZu)>NvY>->EDkbRw3G1#x75F9e~X&t-p_>y*w zLqSndaC+W-T2y|fz$pe04-~dYaSnqMw>Ml5LsN ztGH#+*Z(M`VdsZ^GkcMh^nqS0HAi7dSmEQn4-b%(B-iFd)todQU@>$hb*Ei7vRMTu zV1%if$hLTRD3v;YiMqIx>ANp<{2QS#SAv{1J;ZG>Rjh5n!8!>0&V#U zQOLJizOYvrn8xdzZCkQvsHaL{ISnGOKGXXaKU zhUsMXqq5~7xRew8M3d#mli0)|bo7Vcd`Ju08Xj~I?q%a1#pHb`FN$gycc@+B)juv^ z`f{Kk>wKu>D?qx%#YDVB~wKpdZHe=o= zG>N+?&(O7lg(h7nERDAyHFZub$=+e_PCFUf*|w^;U#EOYNpQ7=yZ=;N*`Qi)+?}0` zUtr_Vu}R0)hYjtHzIQoGtKjURYDmRP%oQ!sI)zpfEH_#zr&Rj&kyK5DGPXK#uE3x3Kh}PQ-EtM}Q zshmEk?H{qphJd`NZ`q%GnwgK6a4)>)??#F3N;a-;RV@M-4}b5|-FSj6$W0WACL=~?_jv5DmglIJhgN()8poq~U* z%#{=edqYRgT>3>!7g3=q7ti{$vCh{1Sgp61Rg6^FT4i-Wcd$T7akJeIlh0*YUm+x% zprQSg3ORM*Fm?Ef>7mX?=Y4^+qs%|1o@efiDG!^dCJG8{d`|ou$VbO5baG;OiZRRx zruOmZ?br5juYPA`h+PQ1lkJ|Z%@zKEdHn5UHS%#~CG#+J7t`k5Yh&20WGuQAGf&jc zb*WJWjJN2Y50jrZsrAge)Y}>|&QJBkl^|SPG;}+Xb;pJWQm0}q`DD~ikB-f61s`jz zhT&~}*ElBY)S~^- zSk%K$YoGsJM=)pNi*e;jU(pqDk_~e5TMVc6tQc!e<8a|}gl@z6%HWRN=`}j~QGB>i zPi+}I*RD%Z;0NZ0NA&*dZDsGB$|@jMrfPQ87GFI4%J_eRM{Nu)Qa^s~B7yGj-|L(r zM0e`UJ1B#c#bZ+OQj)J>2q9>yUUu*J9Joo1KR61}aJH6l+Pw^`gY25%i#JKB17}}f zU0q|BZSo8VY7fo3^gk-0 zqlB0Tk~n)YAv>fa$+e$wG{W%S)jKN(p6%+4hNaRUHf0b-@kfP@S9|U0Q1j79s=Z0` z@{)rms!Dx&^tJpRs(@cCU%f)TU91}+{>L5+dm5?obDz`K3cJzj2d*biv>U44tv@Z% zEPt)>MBBvRrzg-){V5k>{=!0;N(0Gb+P)|`0=rLXe}_H9vaeNSlqH&ti<@I+W6@Je z$Oxm^j=e}GDzG;8{-?&P%bH|~G`#S`eB{YVwU(`1gGI}BQU>B3IWQn7d%NclArz<#P1aapEfkEak9M7L`e^&z5GGZo7f|N8^a?1!birR6a*IEF(PymZDa^Aq^Ushu(9 z45~xz1{|+e>Z4b3S23=Ykd5b~b#lk@G6^4$pRTAbT3CAd z@?{PyS)9Ji&LLadVHszvgM&Fh6yx)shfEALYM>(+p*Q5TISNor;Zxf=G*#Z2Xm&C$ z1R+Xv0iv|=Ot}jFz*F|~A9-0&=29jS%26v0^dsSBw_ z*gCpAoKY$A=PtZ%Sk%OU3^(8}0>Z1fxg%hP;S@x%z<76nTJvRoQr=(CvA+3gH~;?t z7SqukU$pnx4IUeM6+?J#V99aiR#O@Gq;PA1y^amWH$D&A|K4nNKmmNhIs&+zpZJ{A5u_`YHY@bPlK~kS+NB%6Y}mgYb$h z+bd6UM=jJtnDTy9n+(gOUIT_O^#Wti4ymR_agzVZuTJ%L-Wp2y?CaRzbczL0E%gaJ zwMTRG297c;uj=J(SAHZ|Z$UcV_D?iX7t?EuzPOzcxTBnv2a(vEv-V2gie&OS$0ur= zt~YcnV-H=3QV=itY|?()ol&p&+`z@C&nXTq^NM&8H?{QCYUd`G!>G?`pTHVC%m!4d)&dc>XyQy1GC?cz@ar zorG|wpsPnw+SXRzDI;TDWy+)}y0=)XuiMj;l#M|a0;NqO8H>EHuwa4E-pvD}pE*Ah z*2B#c5mpIg-i&nvl}BAc4Gb?x=LQ4{neL3($Bg8#mLavTZ5S_m(Qj^6tqtu;PtMlY znE?SmTtT>y3pnn)cDL^yB1((i(dkvq!BMRrA%*8J+4_N2Z#%Bxt~<>|8RKfQnx^>| zYo{V;0#Tf^<0Kk)iK8-TPz8XF{%7=YAZ?c!0aCUzeaBUgE!7MVobXOE(=)s%cDdck zL%aExekYC-%(bTeJj)m)$Ye20)O&D8Sygkkn0vJL1=>`iwWvLj<>6YQ0%bmoS(VHi z?nf_W1hW&$5c&?iciE6r8+CbXf8?Zj@Z9UZFLvH^m(UVHTPm=b0g}H}#6DBQn`}-) zi#N48ghvmP)Zn!<1YFaW%3f0Aa$itd_7zF^8zoD=R9vfvgn8tWd)$Z$&Ys42l$+M> zOnffAmdvCB$TzI}fy2Y&qWiF|J)O;m>lZ|r%jQw|Ybx;))okw{9|+OWgu)*$*%MU$ zeiB1tHts#OS878R>3Yq&{#Iu8*X&T4c6H;Kjm%IstKV~dyT`5AjZJH;JJPEin7lMT zgUb6J9xwFe^JbcyZAyzS_b#xxzpm9h^d&9InnxknlX%+=w^W^eF@9f^V85 zell!*Xjkb^^4k!0`bLtjKw|wn%iP+_B>p$iDNTR6`*qBLp|G5D(as-Jhw$|Ee8LL1DPfSM{G>tv5U-3=H5t0}As`S^%>>R==EYm{J3Fidt4Q9e_G z?D-6=!35Fu@uMjRR>f1F6pvR%k#|hK5k@O_)^+Rp-+(uIzcwyQzsH{W00w)fFQIL| zOk_QonLuElOpw;(bUVjtUhK_<6zR&zn_CR00qqJVJi8?`Sg(}KWCN$vcoHp)en z=Z&kM(5Qy#1Mde;H2CvH-d=g^CvH8Av<0*e<+MDJP-2{T#9EyL@tDmE=zxJI6I zipE)ldmP8cW1{g#9fiy=8#|SV<>TzZU|#H03rLq`Bk9sMY}p84;WLXYPL3~Pfuq2; zw-_{Aiy_|ndiBp%Hd?)} zbX4P&)5&Bhb*mP2`SPc2s>`4kUh%-hX1tJ<4yS=^oa5Xv>1+jE&shzh++Szr>N+-F z?)f^^gs6DBFFFkN0hd%jgx%Ry#J=#yT^R_6b`Ke7RlEx<)tpd`KIL4(Gqm5CA>g~* z$Ay%rB8jk$NqRIl1J4LizKgZmKSx+(l5e}cfpi$s2%0s-d70oMm>5WB`7uICNCioI ze&YUFAuikO0#Wga8H;p|&ESr)1O%S&wTd9ilXLFTdIO zMsV`lp}5+XT%3xg;OqRT6d!cth98nN9x@yle;t3 z2~94A%V&YnfWr{JMX_ds}+NIz(~#1+ptSESfzhiLr%Gjqf;Nq)nS3jp`#*tA8hkTiF| zP)5Hu_DTE{9cjRc5wvvJD;ns5Vr(;@E%cXMCP2qi9e@?`0YUzm%O$hJ0q(?vDL<+aF7EaZH zPn(bXSOt8I3S4j2YHCK64c|tF5tDQgMNW59mlfqLuuR$+hA;PrA!?h&f&J&Sa~${| z&nfwt17^B5`ge10@HOyBSDDl5l{v9yJ9|aJvuE7x#Gtj%tMJ~v-9?AHGLIdCL@Gfc3{BfIbn zm|-kWH;J^==Owjw&}Ao;=!es1EKNQ^P!c zcikziEJ7T=J6}^?q>XmuE_*7g6vFY2>5JT6?O@6EUz+|@ww3~G91npC{z9{ zS6yEWKjX9C82R?X-hgTZ2^trcFT#KVozA-bK1nqKQ%p}LbQ+gi;uI@FH|#S#6VF~? z;fLV+HlZx004XsOJQ+H8G;Z3T{Jh>|Ql&42ze}s@cGIpe(E!#8V14~1{aK#y&#z}r z1sAK!QBeeI45&#&w;KqXc-RBbBUSnGci=)(M>$NFep~&t6ei|kMgT+gBRKimloZ18 zqE1vv)!@Td0^ewoMI}gw=p5R^hX7hS9?-R@KbsiJk>j-7nW->I<@40ZQ%b9Ix*2|! zXAhV?5HQMnMHs`6&Li5(b-Oqb`w!dz*Z@%z2Es?Tt}|VIrr8!LW@H2*901!W@=3bj zYeOp@lio^6OoY^Yz0Hp#x>F>oz_nhi~vu5MIOINwewGvRH z2zFjCih9pD^5V2Wp%TNLMb7^@fu#;T-t9MFCM-ut;kD#Sc{7sL=}(f8YPNuR(Ff+G z{RgI4=kksuHT-EOO9b&zl~XNuT^6VLM`6ag#lBCD#F3oqXzG6l`Ah=_fuz|2&Nf;4 zEgX=K+L}M4KgLYBqJM6;PKF!UoxeM*_t0u=_!;6qM5#>sv}Hbf1|}WVxz_pjfcMHi zV%$n*XWm6()?4A!*ILUuv7s{$qiUvEGsFJey@Yx8to@0+hbjtd@7_EBY67USs3Y2G zw2@b}+0cyLSQrcD9dJS5I*%yD1!&dD!rR|kKzZ=!77o-Acjd9f`@Bi0LWqzaJGIdN zojnXjZkq2UZF9N=t{h6tSJ!?{AAPX#77ZBx{(^}01A;x79S?!1lc`eBKiEg<-ydKE ze7x@@u+?P?A8|lJiU8w=K9G9b{Q&^ce?K|K6JGvTPRfwg8X=tJhk3O=>#QQwpE4W2 zUATzgW%M?(SS6c3*(jX~1m>F;mNaoBl?YTA>>HszzKlt9%vHIhO694s_&rO< zgOELwote6tjoT-jiHM^ML^Y5P*y-A;4mH)7dY6UDXc;sFJeHB&sD*|w?);R`ifx52 zQ{Vsgl}9qQ8>S%jU-PqC&;)i+uD!C)hP1Pe0p^=3ol zoqp~|Fc@9{TtZZ}!-*2@_NiRrt<;XfslWg!0#EsHTOB3so<7D?!a!Pd09s)f1eVyz zuSt1Pd2fGP6&EQS+JmnF+sK71v<1W9kh~TcaTclyun2noqUOHgsGMF-;A3%daTzbw zjv0E$e)7#Ga^3rp@T^On^mZI_iiPf04*?^Jl#ur*u;sTQ#*s0*W3M-N=jWQ$HCVOm zI9gxT+Xb%lr4#||l6!u*I{Ih&0wK4OQWRH?jtz~$`4l;oB~r7(w!W@2B0wQ z_t+C3z#_10_zW0ZUwyI4xIXVyF!^iws;u;)@e${-K`kqQg-0}6h6M73Mdzz@dpupb zv|DVa)3(2znwysweJxfU=AY0Ca-~o0!HIh(#uW%DuG^8vsEl2!Ta5nI5nIpTGt=dE;!u;c74;{Y!JTqb@RU_tTEWL50HRMd46 z_T7Pfirs=4{#U65W*b~t4Ezcu@K<#_?6wY%MATehvWfGN7;_8dQuw!s{Tz#dPRwOPD!BmkcU zfDV65dd1>y)Xb6FVV$h}dSW5eFv&Wy`-y2Lx9sgTIQt%NMt}pGjS8D&&5Q%ST)V>q zB;1jvAeEl_4e;{e&vh3Kr|-z`4f{QKcCAaco86pU5Y)&)+v24R$N|ys z;;Gr&$jCGSqynb^T2YKWfQh_nw(fmgU0c>CG^3vpV-jpQXAF!BKX-AZs;qr7za#5~ za=RE2GzN|npji&^yE`~HsLg*kAmt}O?&u7pi*%E$GrIQL#A`P4gV3+9GPcA|T`tB^ zqcko;98ySb!f&T=N3!+h9n11dp=ogfp*FY>I56fE=o9D%kxmqVG8U5jzC%( z=Jg!IbH3N5&pJ^#mILKX`UO7guPXA#s!js2?)$RKtJto1I~0x?s=%W?{DF>#2ZL=d z$y#n)ad%*_p2+Wwhu;X=u${m>6oG0rAorLc9M;Q9l_k`#r!5%}II%j-s$cJnjbkcI z1t9+4y&a@jC{WfEOZ5sRJwy>k7x)15->dVZAI8(Ps^3nwl?tovtxp!eaOCz$3I1m9 zw3D4maV`c7jf-o|G-G~GKVpuS`&||mv_=dRy9Q>#%jRLQ1yfXk%76AE=q(z&()T^)d}}uMvT4l0Gel>2 zAHx<<0H@e}KSJ8=_P)ykf3T=6LVTL02lU*iTA4HrkT^ z7yu#F6H4UU8=Q}Wr1S`ta~Ur67Cxt}L1)nbs~e~`0=D8a0s}^|_dXe$gwJgfqeNu7 zE{k6G`(YFZokhs2HyJGOsBw5qgq`JyzTSPu>}15-l@ZRR5 zFJAIH9<2fDmUUcw-6Ay+U9j^LzP^0;Oj9^nzegkRL$3KQYn4CpN7)N5>>O_jmn-B6_@t}^YsVZvt=#;QXObs0nVfpD*{{k)as(X*On zD5x{(|DT7mhb>K%QdU7aIaa^pHMypOGP^5KsF=&F@~^qIa)M4|H%_Er$S`HR&QG`o zmx7p^0iUb>2G_zpF0(Ytl@xlew1rpPlkG_8cLP-Q<%6@2O8Oc~n2sK3fuI>dgM}z4-?*$~#4Pb*nvDVkb?N7Y4&J%l`Vw6yugfdwb=xa+ zM)(suUsa7qp3UMm9hpcTQZO%vLWJPThOY2=Ze9RI-Pk{0NeLX)TA84p zlos+nCnJ@ifSKpl`X9_;Fkz0)ue7sC%{OOw)77@p;ngmeZ-Ao$xe-EwwpyzLY2#nd zz5p_zY{9b<1Ve8<=h2aoY!D_G1o}uN+{T@`%ZVHQhdgX)upC?y8eFjG91JIxJt>!C z2K_PjP)Nmk`PhUSCnd{sjSzB-NQLd2`GS`ZP80%|c7~0F4O3l_tHCsq00RFMsC79l zR>%2REqI_6_<2mLbuv=jDi(pe6@^|N;Rh)ut{f<$e6i;hss-H{3P6FgKnN3nTBda& zcvs=&Z-1KV+owJSZ?5*-#%`%qLl^tyixRli123g$)}QkN{QqGQA}~B|xu#NZ@!cuVVY>8c@FD{iK3p8=_VZ$OW=J zo7QzJu-P0*48KYV!eFf}7*4SCr>Fu*98fAl0qr3nf)7BNsSu8oD4iaGIFdAIuixfT zppK30&Xy7APDDGeeOlr*uh{@=xt9EU^5CznfGZIvYBEMK4xHuvYH?LI3^MQit zi3ErQ_cKUb@NdOM8v4?C)U98=01i)Hb_3YCq}5b4CN6^lD%78;f-QYQF*Gx`yR~t< zYa%rsbr+ZLC@E>-;j*;(mkYz*a!@E4qMjib@egz!7+kmd{6|Dl@M@QEkTH06P7n@_ zl5i9M1rBf#z(*McRRH|)-9U;UJjQklpS=TM2yknA>ziCyVs0JP3`3t9V+ek1bby2b zW7n1P7OWcup4l%$ilTc>mTpU)rZ?B8PqAr9yNt_K!Cqrv=>G^o>q#%sc&z_^#*w6J z`CJv2&&1~h#)wdA$$q!ujc9Oq#f0WTa5Gypxfn)cKb?Pz_a7sX-p;0%aF}sD=Z8Mq zw!37?jYDmU&w?D_svaS#WwWx<49LiANa#%pXaFFhu!Ao48YD;}I$=v+X&)GnP5(U1 z287}iBS!PcbOSa2Oyt_!?bap!-8!JBX;-%z{%UbmV{86Qe_>UoT$ zog>Uksx5#U{P~YT$a-Xz;Nb+D=W(*f-XMy@Js-AZqQ1D8krm$l?3@bl*j9+}1ZoRt zFs~>=eJL75yz^E|SlS@4+~x&T6E*A8jvIg?8xU%ovQGZs2lSDM2y`_K2!GxI-%+uD zPwtd`ft#c95?|KaT-en_qtq%d9=dU0&wa5VlN|Ho--H|x{XA;7^wbCA?sd6a-g+?P zLMyzANue%a+|LlN6`KKZe=u5G5@0{ytds*G%}Nuj?p2Y@uZ7}aH)gIh7gn+={?Jr_ zaO>TYVO|xG3nhZGT15 zl@QZFuEcbB)@fNX4g%%|IdmgBKskV1p1SrF9RQxdCtvth0F-g{<^8kW6F^}I0m#&t z`+2)dOe138_jm1Q%XsFU1TQk0fKJk4xC`zr29OA4qJO_yX1dh;Lm!C1`pLA#Fu*oX z)cj1Cadt?eN6a&TIDp**_ZJ3Z$Am;-yH7ysJ06*2drCe}>8)4x28ntK`myP4?$NVw zj?yO+_df6gD+7=iEL{sakbifta%HNBnwr%{D(=MW+nqPEdn1muiFz*RVY;? zgS&v#k9z%%`ac98Dxi!7LBS@!_7q%910(#joj=2&`wWVRm~5WzjGuj-bcsiU-<(da9=DV>(dmA%C11S7)Mv!NvcO;_ zJ=V#%b(8~hTU|PCdf`B>7_S>NHgz(59(=FB8nO5_J9s^?)ES#Z#4sgNMCDt zr`Hxy+C}d9wxPbhIZsl2Pr!(&HD+6g7Ge?I z>+sqd*0HSeE)16*Rk6>}QN&yKN6J~i0VMy=qxuPlDIG0SSKQJ%)@s`S#T_l4zkiFh zh0z?#ZWxwV6f)5A%M4CW+XhS0u!5^Z;}QnChy&s;<)UK2C$)UOsl_mj@_orju5|K z*bR`qe&PnisDF^%@jH|M60t=E1#k@(Gr*22XxaVNFi|%xfHIjz z&JX$A|AbucpR(C)j1;Kw+dX{%58@BQEA#LJqgDUcXp2w~=|hol13$q@pr1^S6U#J8 zN>$`T!s9C(6*<7I+q^$m0`84Z$X}=mU7^;)_SF2D&v}y;TwKvnF%g^;jH3DA2kk!p z{T*eFPQZ7&W=2h^{%RH!iizQ*0x=eA5_-Mr$6$!ZKuo=QAc79e$IIk^Py5%LLe#sE zKv|Q0ix<0lg?w>p-#OsXHXX@u#63GYb9`Vf-}K(NpEv`@T!doq_AY;1vA?VuIiFVK z3wx%rx(l@Dg4`sjfT%P&wwKH8MF6A2-rPz7gz}(&-F`%zp__gC@lt34Kk%aXb8~So VY;TU$dK&s~xTK;)sn{Ez{|7j_Q7Qlc literal 0 HcmV?d00001 diff --git a/cyclops-ui/src/components/pages/Login/styles.module.css b/cyclops-ui/src/components/pages/Login/styles.module.css index 74650e06..4e4a5f76 100644 --- a/cyclops-ui/src/components/pages/Login/styles.module.css +++ b/cyclops-ui/src/components/pages/Login/styles.module.css @@ -5,10 +5,14 @@ .left_banner { background-color: rgb(6, 25, 61); background-image: url("https://cyclops-ui.com/assets/images/yaml_background-cc1699351922ead2cae5da1dbb75cbd8.png"); - background-size: cover; background-position: 0 -10rem; height: 100vh; - width: 70vw; + width: 120%; +} + +.right_banner { + height: 100vh; + width: 80%; } .cyclops_image_container { @@ -18,10 +22,9 @@ } .cyclops_brand_image { - height: 25rem; + width: 50%; position: fixed; bottom: 0; - /* width: 10rem; */ } .login_header { @@ -29,8 +32,8 @@ } .cyclops_login_header_login { - height: 7rem; - margin-bottom: 4rem; + width: 80%; + margin-bottom: 16px; } .login_form { @@ -38,36 +41,19 @@ } .login_container { - width: 30vw; + width: 80%; padding: 0 5%; + margin: 0 10%; } .field_container { margin: 1rem 0; + width: 100%; } -.submit_buttom { - background-color: #ffffff; - border: 1px solid rgb(209, 213, 219); - border-radius: 0.5rem; - box-sizing: border-box; - color: #111827; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 0.875rem; - font-weight: 600; - line-height: 1.25rem; - padding: 0.75rem 1rem; - text-align: center; - text-decoration: none #d1d5db solid; - text-decoration-thickness: auto; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - cursor: pointer; - user-select: none; +.submit_button { + border: none; width: 100%; - -webkit-user-select: none; - touch-action: manipulation; } .submit_buttom:hover { From 99e6280223a35e4dbb398ebe7920b1d4da7b788b Mon Sep 17 00:00:00 2001 From: petar-cvit Date: Wed, 14 Aug 2024 16:22:08 +0200 Subject: [PATCH 30/44] update login logo --- cyclops-ui/src/components/pages/Login/Login.tsx | 2 +- .../src/components/pages/Login/cyclops-logo.png | Bin 0 -> 28827 bytes .../pages/Login/cyclops-simplistic.png | Bin 54302 -> 0 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 cyclops-ui/src/components/pages/Login/cyclops-logo.png delete mode 100644 cyclops-ui/src/components/pages/Login/cyclops-simplistic.png diff --git a/cyclops-ui/src/components/pages/Login/Login.tsx b/cyclops-ui/src/components/pages/Login/Login.tsx index 946454e9..1ed92b93 100644 --- a/cyclops-ui/src/components/pages/Login/Login.tsx +++ b/cyclops-ui/src/components/pages/Login/Login.tsx @@ -77,7 +77,7 @@ const 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 0000000000000000000000000000000000000000..13077cd6915de7e73b7412d4ae156461fd4c2f1a GIT binary patch literal 28827 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 HA?p7C$ds{Y literal 0 HcmV?d00001 diff --git a/cyclops-ui/src/components/pages/Login/cyclops-simplistic.png b/cyclops-ui/src/components/pages/Login/cyclops-simplistic.png deleted file mode 100644 index dfc1d74db6a1e9accae8f04678a3b30028aa0223..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54302 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 VY;TU$dK&s~xTK;)sn{Ez{|7j_Q7Qlc From ee54ef7e5e87243ca1e6107e9dcdd84448c95a47 Mon Sep 17 00:00:00 2001 From: petar-cvit Date: Wed, 14 Aug 2024 18:41:58 +0200 Subject: [PATCH 31/44] replace href with navigate --- .../src/components/pages/Modules/Modules.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/cyclops-ui/src/components/pages/Modules/Modules.tsx b/cyclops-ui/src/components/pages/Modules/Modules.tsx index 61f35468..4f946637 100644 --- a/cyclops-ui/src/components/pages/Modules/Modules.tsx +++ b/cyclops-ui/src/components/pages/Modules/Modules.tsx @@ -88,7 +88,7 @@ const Modules = () => { if (resolvedVersion !== "") { return resolvedVersion; } - if(version !== ""){ + if (version !== "") { return version; } return "main"; @@ -109,7 +109,11 @@ const Modules = () => { return filteredData.map((module: any, index) => ( - + )); }; From 3cb8f96a371ebde5d3b907ec7824e18cb8b16f91 Mon Sep 17 00:00:00 2001 From: petar-cvit Date: Wed, 14 Aug 2024 19:37:13 +0200 Subject: [PATCH 32/44] no repeat background --- cyclops-ui/src/components/pages/Login/styles.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/cyclops-ui/src/components/pages/Login/styles.module.css b/cyclops-ui/src/components/pages/Login/styles.module.css index 4e4a5f76..4f6f3eba 100644 --- a/cyclops-ui/src/components/pages/Login/styles.module.css +++ b/cyclops-ui/src/components/pages/Login/styles.module.css @@ -6,6 +6,7 @@ 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%; } From 0ce6b17bd8ee80c001ec8b68ddbefc8c2fdd1742 Mon Sep 17 00:00:00 2001 From: petar-cvit Date: Wed, 14 Aug 2024 19:37:59 +0200 Subject: [PATCH 33/44] remove redis from docker compose --- cyclops-ctrl/docker-compose.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cyclops-ctrl/docker-compose.yaml b/cyclops-ctrl/docker-compose.yaml index c6f8df47..6c758d34 100644 --- a/cyclops-ctrl/docker-compose.yaml +++ b/cyclops-ctrl/docker-compose.yaml @@ -1,12 +1,6 @@ version: "3.8" services: - redis: - container_name: redis - image: redis - ports: - - 6379:6379 - cerbos: container_name: cerbos image: ghcr.io/cerbos/cerbos:latest @@ -16,4 +10,4 @@ services: volumes: - ./internal/cerbos/config:/config - ./internal/cerbos/policies:/policies - command: server --config=/config/conf.yaml \ No newline at end of file + command: server --config=/config/conf.yaml From 24a53c60cd9510eca1e5f54c971f7d852a142859 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 24 Aug 2024 19:37:40 +0530 Subject: [PATCH 34/44] error message --- cyclops-ctrl/internal/cerbos/controller.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cyclops-ctrl/internal/cerbos/controller.go b/cyclops-ctrl/internal/cerbos/controller.go index 1e4eb99b..7bdbe9c7 100644 --- a/cyclops-ctrl/internal/cerbos/controller.go +++ b/cyclops-ctrl/internal/cerbos/controller.go @@ -25,13 +25,13 @@ func Login(cerbosClient *CerbosSvc) gin.HandlerFunc { } if err := c.ShouldBindJSON(&credentials); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) + 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": "user does not exist."}) + c.JSON(http.StatusOK, gin.H{"error": "invalid credentials"}) return } @@ -42,7 +42,7 @@ func Login(cerbosClient *CerbosSvc) gin.HandlerFunc { authCtx, err := buildAuthContext(credentials.Username, c, cerbosClient) if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) return } requestCtx := c.Request.Context() @@ -72,3 +72,11 @@ func Login(cerbosClient *CerbosSvc) gin.HandlerFunc { 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"}) + } +} From 3afdc0011147a9399f6afc7e7c42781ae6577064 Mon Sep 17 00:00:00 2001 From: siddhantprateek Date: Sat, 24 Aug 2024 19:37:54 +0530 Subject: [PATCH 35/44] logout button --- cyclops-ctrl/internal/handler/handler.go | 2 ++ cyclops-ui/src/components/layouts/Sidebar.tsx | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cyclops-ctrl/internal/handler/handler.go b/cyclops-ctrl/internal/handler/handler.go index 125ba780..386c5da3 100644 --- a/cyclops-ctrl/internal/handler/handler.go +++ b/cyclops-ctrl/internal/handler/handler.go @@ -66,6 +66,8 @@ func (h *Handler) Start() error { h.router.Use(cerbos.AuthMiddleware(h.cerbosClient)) } + h.router.POST("/logout", cerbos.Logout()) + // templates h.router.GET("/templates", templatesController.GetTemplate) h.router.GET("/templates/initial", templatesController.GetTemplateInitialValues) diff --git a/cyclops-ui/src/components/layouts/Sidebar.tsx b/cyclops-ui/src/components/layouts/Sidebar.tsx index be18f59e..9def6001 100644 --- a/cyclops-ui/src/components/layouts/Sidebar.tsx +++ b/cyclops-ui/src/components/layouts/Sidebar.tsx @@ -66,14 +66,17 @@ const SideNav = () => { />
    - + {window.__RUNTIME_CONFIG__.REACT_APP_CYCLOPS_AUTHORIZATION === + "enabled" && ( + + )}