diff --git a/cmd/api-admin/.ko.yaml b/cmd/api-admin/.ko.yaml deleted file mode 100644 index 06b4c0e..0000000 --- a/cmd/api-admin/.ko.yaml +++ /dev/null @@ -1,6 +0,0 @@ -defaultBaseImage: ghcr.io/carverauto/eventrunner-base:v2 -builds: - - id: api-admin - main: . - platforms: - - linux/amd64 \ No newline at end of file diff --git a/cmd/api-admin/Dockerfile b/cmd/api-admin/Dockerfile deleted file mode 100644 index b470b56..0000000 --- a/cmd/api-admin/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -# Start from a minimal Alpine image to set up our directory structure -FROM --platform=linux/amd64 alpine:3.14 AS builder - -# Create necessary directories -WORKDIR /app -RUN mkdir -p /app/configs /app/migrations - -# Copy migrations and configs -COPY migrations/ /app/migrations/ -COPY configs/.env /app/configs/.env -COPY configs/.staging.env /app/configs/.staging.env - -# Now, create our final image based on distroless -FROM --platform=linux/amd64 cgr.dev/chainguard/go:latest -# debug using a container with a shell -#FROM --platform=linux/amd64 alpine:3.14 - -# Copy the directory structure from the builder stage -COPY --from=builder /app /app - -# Set working directory -WORKDIR /app - -# Set environment variables -ENV APP_ENV=staging -ENV GOFR_MIGRATIONS_DIR=/app/migrations - -# The ko-built binary will be added as "app" in the container -# We don't set an ENTRYPOINT here as ko will do that for us -ENTRYPOINT ["sh"] diff --git a/cmd/api-admin/Makefile b/cmd/api-admin/Makefile deleted file mode 100644 index c219fe1..0000000 --- a/cmd/api-admin/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# Define variables -KO_DOCKER_REPO := ghcr.io/carverauto/api-admin -VERSION := v0.0.01 - -# Default target -all: build - -# Build the binary locally -build: - go build -o api-admin . - -# Build and push the container image using ko -ko-build: - @export KO_DOCKER_REPO=$(KO_DOCKER_REPO) && \ - export VERSION=$(VERSION) && \ - ko build --bare --tags $(VERSION) ./ - -# Clean up -clean: - rm -f api-admin - -.PHONY: all build ko-build clean \ No newline at end of file diff --git a/cmd/api-admin/configs/.env b/cmd/api-admin/configs/.env deleted file mode 100644 index 8b4ba4d..0000000 --- a/cmd/api-admin/configs/.env +++ /dev/null @@ -1,13 +0,0 @@ -APP_NAME=api-admin -HTTP_PORT=8201 - -LOG_LEVEL=DEBUG - -DSN=mongodb://er-mongodb.mongo.svc.cluster.local:27017 -ORY_SCHEMA_ID= -DB_HOST=localhost -DB_USER=root -DB_PASSWORD=password -DB_NAME=test -DB_PORT=2001 -DB_DIALECT=mongo diff --git a/cmd/api-admin/main.go b/cmd/api-admin/main.go deleted file mode 100644 index 1d816a4..0000000 --- a/cmd/api-admin/main.go +++ /dev/null @@ -1,87 +0,0 @@ -// Package main cmd/api-admin/main.go - -package main - -import ( - "context" - "os" - "time" - - "github.com/carverauto/eventrunner/pkg/api/handlers" - "github.com/carverauto/eventrunner/pkg/api/middleware" - ory "github.com/ory/client-go" - "gofr.dev/pkg/gofr" - "gofr.dev/pkg/gofr/datasource/mongo" -) - -const ( - dbConnectTimeout = 10 * time.Second -) - -func main() { - app := gofr.New() - - ctx := context.Background() - - // Set up MongoDB - db := mongo.New(&mongo.Config{URI: os.Getenv("DB_URL"), Database: "eventrunner"}) - - // setup a context with a timeout - dbCtx, cancel := context.WithTimeout(ctx, dbConnectTimeout) - defer cancel() - - err := app.AddMongo(dbCtx, db) - if err != nil { - app.Logger().Errorf("Failed to connect to MongoDB: %v", err) - return - } - - // Initialize Ory client - oryClient := ory.NewConfiguration() - oryClient.Servers = ory.ServerConfigurations{{URL: os.Getenv("ORY_SDK_URL")}} - - apiClient := ory.NewAPIClient(oryClient) - - // Initialize handlers - h := handlers.NewHandlers(apiClient) - - // Add debug logging middleware - - app.UseMiddleware(middleware.DebugHeadersMiddleware()) - - // Add other middleware and routes - app.UseMiddleware(middleware.CustomHeadersMiddleware()) - - // API Credentials routes - app.POST("/api/credentials", middleware.Adapt( - h.CreateAPICredential, - middleware.RequireUser, - )) - - app.GET("/api/credentials", middleware.Adapt( - h.ListAPICredentials, - middleware.RequireUser, - )) - - // this endpoint is used by the Ory Kratos login flow - app.GET("/callback", func(ctx *gofr.Context) (interface{}, error) { - code := ctx.Request.Param("code") - state := ctx.Request.Param("state") - - ctx.Logger.Infof("Received callback with code: %s and state: %s", code, state) - - return map[string]string{ - "code": code, - "state": state, - }, nil - }) - - app.GET("/api/test", testEndpoint) - - // Run the application - app.Run() -} - -func testEndpoint(c *gofr.Context) (interface{}, error) { - return map[string]string{"message": "Hello, world!"}, nil -} diff --git a/cmd/api-admin/superuser.json b/cmd/api-admin/superuser.json deleted file mode 100644 index fbb000c..0000000 --- a/cmd/api-admin/superuser.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "email": "mfreeman451@gmail.com" -} diff --git a/cmd/api/configs/.env b/cmd/api/configs/.env index 423408d..b710ee1 100644 --- a/cmd/api/configs/.env +++ b/cmd/api/configs/.env @@ -3,8 +3,8 @@ HTTP_PORT=8200 LOG_LEVEL=DEBUG -JWKS_SERVER=https://affectionate-brattain-fl0yahcycw.projects.oryapis.com/.well-known/jwks.json DSN=mongodb://er-mongodb.mongo.svc.cluster.local:27017 +ORY_SDK_URL=http://hydra-admin.auth:4445 ORY_SCHEMA_ID= DB_HOST=localhost DB_USER=root diff --git a/cmd/api/configs/.staging.env b/cmd/api/configs/.staging.env index da537a2..67dde8d 100644 --- a/cmd/api/configs/.staging.env +++ b/cmd/api/configs/.staging.env @@ -4,5 +4,6 @@ HTTP_PORT=8200 LOG_LEVEL=DEBUG ORY_PROJECT_URL=http://localhost:4455 -DB_URL=mongodb://mongodb.svc.cluster.local:27017 +DB_URL=mongodb://er-mongodb.mongo.svc.cluster.local:27017 +DSN=mongodb://er-mongodb.mongo.svc.cluster.local:27017 DB_NAME=eventrunner diff --git a/cmd/api/main.go b/cmd/api/main.go index 5f691f1..378a471 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -4,7 +4,6 @@ package main import ( "context" - "os" "time" "github.com/carverauto/eventrunner/pkg/api/handlers" @@ -38,8 +37,8 @@ func main() { // Initialize Ory client oryClient := ory.NewConfiguration() - oryClient.Servers = ory.ServerConfigurations{{URL: os.Getenv("ORY_SDK_URL")}} - // oryClient.DefaultHeader["Authorization"] = "Bearer " + os.Getenv("ORY_PAT") + + oryClient.Servers = ory.ServerConfigurations{{URL: "http://hydra-admin.auth:4445"}} apiClient := ory.NewAPIClient(oryClient) @@ -61,6 +60,17 @@ func main() { // Add other middleware and routes app.UseMiddleware(middleware.CustomHeadersMiddleware()) + // API Credentials routes + app.POST("/api/admin/credentials", middleware.Adapt( + h.CreateAPICredential, + middleware.RequireUser, + )) + + app.GET("/api/admin/credentials", middleware.Adapt( + h.ListAPICredentials, + middleware.RequireUser, + )) + // this endpoint is used by the Ory Kratos login flow app.GET("/callback", func(ctx *gofr.Context) (interface{}, error) { code := ctx.Request.Param("code") diff --git a/docs/auth.md b/docs/auth.md index fa1da13..0477573 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -9,7 +9,9 @@ EventRunner uses Ory's identity stack for authentication: - Hydra: OAuth2 provider - Oathkeeper: API gateway and access control -## Flow Diagram +## Flow Diagrams + +### Overview ```mermaid sequenceDiagram @@ -46,6 +48,25 @@ sequenceDiagram Hydra-->>Oathkeeper: Token valid + claims Oathkeeper->>API: Request + X-headers ``` +### User Registration Flow + +```mermaid +sequenceDiagram + participant Client + participant Hydra + participant Kratos + + Client->>Hydra: POST /oauth2/register + Note over Client,Hydra: Sends metadata, redirect URIs, etc. + Hydra->>Hydra: Validates request + Hydra-->>Client: Returns client_id, client_secret + Note over Client,Hydra: Also returns registration_access_token + + Note over Client,Hydra: Later... + Client->>Hydra: GET /oauth2/register/{client_id} + Note over Client,Hydra: Uses registration_access_token + Hydra-->>Client: Returns client config +``` ## Step-by-Step Guide diff --git a/k8s/api-admin/base/api-admin.yaml b/k8s/api-admin/base/api-admin.yaml deleted file mode 100644 index 53ab606..0000000 --- a/k8s/api-admin/base/api-admin.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: api-admin - namespace: eventrunner -spec: - replicas: 1 - selector: - matchLabels: - app: api-admin - template: - metadata: - labels: - app: api-admin - spec: - serviceAccountName: eventrunner-account - imagePullSecrets: - - name: ghcr-io-cred - containers: - - name: api-admin - image: ghcr.io/carverauto/api-admin:v0.0.04 - imagePullPolicy: Always - env: - - name: DB_PASSWORD - valueFrom: - secretKeyRef: - name: db-credentials - key: DB_PASSWORD - - name: ORY_SDK_URL - value: "http://hydra-admin.auth:4445" - envFrom: - - configMapRef: - name: api-admin-config - volumeMounts: - - name: config - mountPath: /app/configs - - name: nats-creds - mountPath: /app/nats.creds - subPath: nats.creds - volumes: - - name: config - configMap: - name: api-admin-config - - name: nats-creds - secret: - secretName: nats-creds - items: - - key: nats.creds - path: nats.creds \ No newline at end of file diff --git a/k8s/api-admin/base/configmap.yaml b/k8s/api-admin/base/configmap.yaml deleted file mode 100644 index 19360ea..0000000 --- a/k8s/api-admin/base/configmap.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: api-admin-config - namespace: eventrunner -data: - .env: | - APP_NAME=api-admin - HTTP_PORT=8200 - LOG_LEVEL=DEBUG - ORY_PROJECT_URL=http://hydra-admin.auth:4445 - DB_HOST=localhost - DB_USER=root - DB_PASSWORD=password - DB_NAME=eventrunner - DB_PORT=2001 - DB_DIALECT=mongo - DB_URL=mongodb://er-mongodb.svc.cluster.local:27017 - .staging.env: | - LOG_LEVEL=DEBUG - APP_NAME=api-admin - HTTP_PORT=8200 - ORY_PROJECT_URL=http://hydra-admin.auth:4445 - DB_HOST=localhost - DB_USER=root - DB_PASSWORD=password - DB_NAME=eventrunner - DB_PORT=2001 - DB_DIALECT=mongo - DB_URL=mongodb://er-mongodb.svc.cluster.local:27017 \ No newline at end of file diff --git a/k8s/api-admin/base/db-creds.yaml b/k8s/api-admin/base/db-creds.yaml deleted file mode 100644 index e13fc6b..0000000 --- a/k8s/api-admin/base/db-creds.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: eventrunner-db-credentials - namespace: eventrunner -type: Opaque -stringData: - username: eventrunner - password: changeme diff --git a/k8s/api-admin/base/health-alive-ingress.yaml b/k8s/api-admin/base/health-alive-ingress.yaml deleted file mode 100644 index 2ba30c4..0000000 --- a/k8s/api-admin/base/health-alive-ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-admin-health - namespace: eventrunner - annotations: - cert-manager.io/cluster-issuer: threadr-issuer - nginx.ingress.kubernetes.io/auth-response-headers: X-User,X-Tenant-ID,X-Request-Id,Authorization - nginx.ingress.kubernetes.io/auth-snippet: | - proxy_set_header X-Original-Uri $request_uri; - nginx.ingress.kubernetes.io/auth-url: http://oathkeeper-proxy.auth.svc.cluster.local:4455/decisions/health - nginx.ingress.kubernetes.io/ssl-redirect: "true" -spec: - ingressClassName: nginx - rules: - - host: api-admin.tunnel.threadr.ai - http: - paths: - - path: /.well-known/health - pathType: ImplementationSpecific - backend: - service: - name: api-admin - port: - number: 8200 - tls: - - hosts: - - api-admin.tunnel.threadr.ai - secretName: eventrunner-api-tls ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-admin-alive - namespace: eventrunner - annotations: - cert-manager.io/cluster-issuer: threadr-issuer - nginx.ingress.kubernetes.io/auth-response-headers: X-User,X-Tenant-ID,X-Request-Id,Authorization - nginx.ingress.kubernetes.io/auth-url: http://oathkeeper-proxy.auth.svc.cluster.local:4455/decisions/alive - nginx.ingress.kubernetes.io/ssl-redirect: "true" -spec: - ingressClassName: nginx - rules: - - host: api-admin.tunnel.threadr.ai - http: - paths: - - path: /.well-known/alive - pathType: ImplementationSpecific - backend: - service: - name: api-admin - port: - number: 8200 - tls: - - hosts: - - api-admin.tunnel.threadr.ai - secretName: eventrunner-api-tls \ No newline at end of file diff --git a/k8s/api-admin/base/ingress-api.yaml b/k8s/api-admin/base/ingress-api.yaml deleted file mode 100644 index 0ff5216..0000000 --- a/k8s/api-admin/base/ingress-api.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# ingress-api.yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-admin - namespace: eventrunner - annotations: - cert-manager.io/cluster-issuer: threadr-issuer -spec: - ingressClassName: nginx - tls: - - hosts: - - api-admin.tunnel.threadr.ai - secretName: eventrunner-api-tls - rules: - - host: api-admin.tunnel.threadr.ai - http: - paths: - - path: /api-admin - pathType: Prefix - backend: - service: - name: oathkeeper-proxy - port: - number: 4455 \ No newline at end of file diff --git a/k8s/api-admin/base/ingress-callback.yaml b/k8s/api-admin/base/ingress-callback.yaml deleted file mode 100644 index 88321c4..0000000 --- a/k8s/api-admin/base/ingress-callback.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# ingress-callback.yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-admin-callback - namespace: eventrunner - annotations: - cert-manager.io/cluster-issuer: threadr-issuer - nginx.ingress.kubernetes.io/use-regex: "true" - nginx.ingress.kubernetes.io/proxy-pass-params: "on" - nginx.ingress.kubernetes.io/rewrite-target: /callback$1 -spec: - ingressClassName: nginx - tls: - - hosts: - - api-admin.tunnel.threadr.ai - secretName: eventrunner-api-tls - rules: - - host: api-admin.tunnel.threadr.ai - http: - paths: - - path: /callback(.*) - pathType: ImplementationSpecific - backend: - service: - name: api-admin - port: - number: 8200 \ No newline at end of file diff --git a/k8s/api-admin/base/ingress.yaml b/k8s/api-admin/base/ingress.yaml deleted file mode 100644 index 25849a0..0000000 --- a/k8s/api-admin/base/ingress.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-admin - namespace: eventrunner - annotations: - cert-manager.io/cluster-issuer: threadr-issuer - nginx.ingress.kubernetes.io/rewrite-target: /$2 - nginx.ingress.kubernetes.io/use-regex: "true" -spec: - ingressClassName: nginx - tls: - - hosts: - - api-admin.tunnel.threadr.ai - secretName: eventrunner-api-tls - rules: - - host: api-admin.tunnel.threadr.ai - http: - paths: - - path: /callback(/|$)(.*) - pathType: ImplementationSpecific - backend: - service: - name: api-admin - port: - number: 8200 - - path: /api - pathType: Prefix - backend: - service: - name: oathkeeper-proxy - port: - number: 4455 \ No newline at end of file diff --git a/k8s/api-admin/base/kustomization.yaml b/k8s/api-admin/base/kustomization.yaml deleted file mode 100644 index 742c1cb..0000000 --- a/k8s/api-admin/base/kustomization.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: eventrunner -resources: - - configmap.yaml - - api-admin.yaml - - service.yaml - - health-alive-ingress.yaml - - service-proxy.yaml - - ingress-api.yaml - - ingress-callback.yaml diff --git a/k8s/api-admin/base/service-proxy.yaml b/k8s/api-admin/base/service-proxy.yaml deleted file mode 100644 index 6ce88bb..0000000 --- a/k8s/api-admin/base/service-proxy.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# Service that points to the auth namespace service -apiVersion: v1 -kind: Service -metadata: - name: oathkeeper-proxy - namespace: eventrunner -spec: - type: ExternalName - externalName: oathkeeper-proxy.auth.svc.cluster.local \ No newline at end of file diff --git a/k8s/api-admin/base/service.yaml b/k8s/api-admin/base/service.yaml deleted file mode 100644 index a0b965e..0000000 --- a/k8s/api-admin/base/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: api-admin - namespace: eventrunner - labels: - app: api-admin -spec: - ports: - - port: 80 - targetPort: 8200 - protocol: TCP - name: http - selector: - app: api-admin diff --git a/k8s/api/base/api.yaml b/k8s/api/base/api.yaml index 184daa3..dd48123 100644 --- a/k8s/api/base/api.yaml +++ b/k8s/api/base/api.yaml @@ -18,7 +18,7 @@ spec: - name: ghcr-io-cred containers: - name: api - image: ghcr.io/carverauto/eventrunner-api:v0.0.29 + image: ghcr.io/carverauto/eventrunner-api:v0.0.39 imagePullPolicy: Always env: - name: DB_PASSWORD diff --git a/k8s/hydra/values.yaml b/k8s/hydra/values.yaml index eb3e4ec..2902af0 100644 --- a/k8s/hydra/values.yaml +++ b/k8s/hydra/values.yaml @@ -41,8 +41,10 @@ hydra: oauth2: expose_internal_errors: true allowed_top_level_claims: - - email + - user_metadata + - user_id - tenant_id + - email - roles mirror_top_level_claims: true oidc: diff --git a/k8s/oathkeeper/values.yaml b/k8s/oathkeeper/values.yaml index b75c0fb..e5f399b 100644 --- a/k8s/oathkeeper/values.yaml +++ b/k8s/oathkeeper/values.yaml @@ -167,7 +167,7 @@ oathkeeper: { "id": "eventrunner-rule", "upstream": { - "url": "http://eventrunner-api.eventrunner" + "url": "http://api.eventrunner" }, "match": { "url": "https://api.tunnel.threadr.ai/api/<.*>", @@ -210,9 +210,8 @@ oathkeeper: "handler": "header", "config": { "headers": { - "USER_ID": "{{ print .Subject }}", "X-User-ID": "{{ print .Subject }}", - "X-Tenant-ID": "{{ print .Extra.tenant_id}}", + "X-Tenant-ID": "{{ print .Extra.tenant_id }}", "X-User-Email": "{{ print .Extra.email }}", "X-User-Roles": "{{ print .Extra.roles }}" } diff --git a/pkg/api/handlers/api_credentials.go b/pkg/api/handlers/api_credentials.go index 0c872ba..0cdf1ed 100644 --- a/pkg/api/handlers/api_credentials.go +++ b/pkg/api/handlers/api_credentials.go @@ -2,6 +2,7 @@ package handlers import ( "fmt" + "log" "net/http" "time" @@ -23,20 +24,26 @@ func (h *Handlers) CreateAPICredential(c *gofr.Context) (interface{}, error) { reqBody.Name = "API Key " + time.Now().Format(time.RFC3339) } + log.Println("Request body: ", reqBody) + log.Println("Request body Name: ", reqBody.Name) + oauth2Client := client.NewOAuth2Client() + oauth2Client.SetClientName(reqBody.Name) oauth2Client.SetScope("openid profile email tenant_id") oauth2Client.SetGrantTypes([]string{"authorization_code", "refresh_token", "client_credentials"}) oauth2Client.SetResponseTypes([]string{"code", "id_token"}) - oauth2Client.SetRedirectUris([]string{"https://api-admin.tunnel.threadr.ai/callback"}) - oauth2Client.SetAudience([]string{"https://api-admin.tunnel.threadr.ai"}) + oauth2Client.SetRedirectUris([]string{"https://api.tunnel.threadr.ai/callback"}) metadata := map[string]interface{}{ - "user_id": userInfo.UserID.String(), - "tenant_id": userInfo.TenantID.String(), - "created_at": time.Now(), - "name": reqBody.Name, + "user_id": userInfo.UserID.String(), + "tenant_id": userInfo.TenantID.String(), + "email": userInfo.Email, + "roles": userInfo.Roles, } + + log.Println("Metadata: ", metadata) + oauth2Client.SetMetadata(metadata) resp, httpResp, err := h.OryClient.OAuth2API.CreateOAuth2Client(c.Context). diff --git a/pkg/api/handlers/handlers.go b/pkg/api/handlers/handlers.go index 039023d..cc880cf 100644 --- a/pkg/api/handlers/handlers.go +++ b/pkg/api/handlers/handlers.go @@ -40,7 +40,7 @@ func getCustomContext(c *gofr.Context) (customctx.Context, error) { type UserInfo struct { UserID uuid.UUID - Role string + Roles []string TenantID uuid.UUID Email string } @@ -51,7 +51,7 @@ func getUserInfo(c *gofr.Context) (*UserInfo, error) { return nil, err } - userIDStr, ok := customCtx.GetStringClaim("X-User-ID") + userIDStr, ok := customCtx.GetStringClaim("X-User-Id") if !ok { return nil, errors.NewMissingParamError("user ID") } @@ -66,7 +66,7 @@ func getUserInfo(c *gofr.Context) (*UserInfo, error) { return nil, errors.NewMissingParamError("user role") } - tenantIDStr, ok := customCtx.GetStringClaim("X-Tenant-ID") + tenantIDStr, ok := customCtx.GetStringClaim("X-Tenant-Id") if !ok { return nil, errors.NewMissingParamError("tenant ID") } @@ -80,7 +80,7 @@ func getUserInfo(c *gofr.Context) (*UserInfo, error) { return &UserInfo{ UserID: userID, - Role: role, + Roles: []string{role}, TenantID: tenantID, Email: email, }, nil @@ -232,7 +232,9 @@ func (h *Handlers) CreateTenant(c *gofr.Context) (interface{}, error) { return nil, err } - if userInfo.Role != "superuser" { + // look through userInfo.Roles to see if they are the superUser + isSuperUser := containsRole(userInfo.Roles, "superuser") + if !isSuperUser { return nil, errors.NewForbiddenError("Only superuser can create tenants") } @@ -255,9 +257,10 @@ func (h *Handlers) CreateTenant(c *gofr.Context) (interface{}, error) { return tenant, nil } -func containsRole(roles []interface{}, role string) bool { +// containsRole checks if a role is present in a list of roles from the UserInfo struct. +func containsRole(roles []string, role string) bool { for _, r := range roles { - if r.(string) == role { + if r == role { return true } } diff --git a/pkg/api/middleware/rbac.go b/pkg/api/middleware/rbac.go index 9136f99..64d6296 100644 --- a/pkg/api/middleware/rbac.go +++ b/pkg/api/middleware/rbac.go @@ -107,9 +107,9 @@ func RequireUser(next Handler) Handler { return nil, err } - userID, ok := customCtx.GetStringClaim("X-User-ID") + userID, ok := customCtx.GetStringClaim("X-User-Id") if !ok || userID == "" { - return nil, errors.NewMissingParamError("X-User-ID header") + return nil, errors.NewMissingParamError("X-User-Id header") } return next.Handle(c) @@ -126,10 +126,10 @@ func GetAuthClaims(c *gofr.Context) (map[string]string, error) { claims := make(map[string]string) // Extract common auth claims - if userID, ok := customCtx.GetStringClaim("X-User-ID"); ok { + if userID, ok := customCtx.GetStringClaim("X-User-Id"); ok { claims["user_id"] = userID } - if tenantID, ok := customCtx.GetStringClaim("X-Tenant-ID"); ok { + if tenantID, ok := customCtx.GetStringClaim("X-Tenant-Id"); ok { claims["tenant_id"] = tenantID } if userRole, ok := customCtx.GetStringClaim("X-User-Role"); ok {