Skip to content

Commit

Permalink
adding rbac
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Freeman committed Oct 5, 2024
1 parent 60e62bf commit e23073f
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 0 deletions.
35 changes: 35 additions & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"github.com/carverauto/eventrunner/cmd/api/migrations"

Check failure on line 4 in cmd/api/main.go

View workflow job for this annotation

GitHub Actions / lint

could not import github.com/carverauto/eventrunner/cmd/api/migrations (-: # github.com/carverauto/eventrunner/cmd/api/migrations
"github.com/carverauto/eventrunner/pkg/api/handlers"
"github.com/carverauto/eventrunner/pkg/api/middleware"
"gofr.dev/pkg/gofr"
"gofr.dev/pkg/gofr/datasource/mongo"
)

func main() {
app := gofr.New()

// Set up MongoDB
db := mongo.New(mongo.Config{URI: "mongodb://localhost:27017", Database: "eventrunner"})
app.AddMongo(db)

// Run migrations
app.Migrate(migrations.All())

// Set up routes
tenantHandler := &handlers.TenantHandler{}
userHandler := &handlers.UserHandler{}

// Tenant routes (protected by API key)
app.POST("/tenants", tenantHandler.Create, middleware.AuthenticateAPIKey)

Check failure on line 26 in cmd/api/main.go

View workflow job for this annotation

GitHub Actions / lint

too many arguments in call to app.POST
app.GET("/tenants", tenantHandler.GetAll, middleware.AuthenticateAPIKey)

Check failure on line 27 in cmd/api/main.go

View workflow job for this annotation

GitHub Actions / lint

too many arguments in call to app.GET

// User routes (protected by API key and role-based access)
app.POST("/tenants/{tenant_id}/users", userHandler.Create, middleware.AuthenticateAPIKey, middleware.RequireRole("admin"))

Check failure on line 30 in cmd/api/main.go

View workflow job for this annotation

GitHub Actions / lint

too many arguments in call to app.POST
app.GET("/tenants/{tenant_id}/users", userHandler.GetAll, middleware.AuthenticateAPIKey, middleware.RequireRole("admin", "user"))

Check failure on line 31 in cmd/api/main.go

View workflow job for this annotation

GitHub Actions / lint

too many arguments in call to app.GET

// Run the application
app.Run()
}
12 changes: 12 additions & 0 deletions cmd/api/migrations/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package migrations

import (
"gofr.dev/pkg/gofr/migration"
)

func All() map[int64]migration.Migrate {
return map[int64]migration.Migrate{
20240226153000: createCollections(),

Check failure on line 9 in cmd/api/migrations/all.go

View workflow job for this annotation

GitHub Actions / lint

undefined: createCollections

Check failure on line 9 in cmd/api/migrations/all.go

View workflow job for this annotation

GitHub Actions / lint

undefined: createCollections
20240226153100: createIndexes(),

Check failure on line 10 in cmd/api/migrations/all.go

View workflow job for this annotation

GitHub Actions / lint

undefined: createIndexes (typecheck)

Check failure on line 10 in cmd/api/migrations/all.go

View workflow job for this annotation

GitHub Actions / lint

undefined: createIndexes) (typecheck)
}
}
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ require (
github.com/google/uuid v1.6.0
github.com/nats-io/nats-server/v2 v2.10.21
github.com/stretchr/testify v1.9.0
go.mongodb.org/mongo-driver v1.17.1
go.uber.org/mock v0.4.0
gofr.dev v1.22.1
gofr.dev/pkg/gofr/datasource/cassandra v0.1.0
gofr.dev/pkg/gofr/datasource/mongo v0.2.0
)

require (
Expand Down Expand Up @@ -60,6 +62,7 @@ require (
github.com/minio/highwayhash v1.0.3 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/jwt/v2 v2.5.8 // indirect
github.com/nats-io/nats.go v1.37.0 // indirect
Expand All @@ -79,6 +82,10 @@ require (
github.com/redis/go-redis/v9 v9.6.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/segmentio/kafka-go v0.4.47 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
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/nats-io/jwt/v2 v2.5.8 h1:uvdSzwWiEGWGXf+0Q+70qv6AQdvcvxrv9hPM0RiPamE=
Expand Down Expand Up @@ -230,13 +232,17 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw=
go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q=
Expand Down Expand Up @@ -279,6 +285,8 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
gofr.dev v1.22.1 h1:WLD9pdVkWeDMdRdggmZtq//t41RIN/6XGr++IXlN3jM=
gofr.dev v1.22.1/go.mod h1:jldZJGrUKxD6BUEFwdlODcBCGBSvgkVoMy9q15sJm2Q=
gofr.dev/pkg/gofr/datasource/mongo v0.2.0 h1:J6rPgxF84NV+LbrfaybP6QM3oP7A2EZIcY++v4FBsEg=
gofr.dev/pkg/gofr/datasource/mongo v0.2.0/go.mod h1:ymDX/8PxmKBDjZrpLKx/IcLLBGhRzc8whEe22Q9C7sQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package api

import "errors"

var (
ErrInsufficientPermissions = errors.New("insufficient permissions")
)
65 changes: 65 additions & 0 deletions pkg/api/handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package handlers

import (
"github.com/carverauto/eventrunner/pkg/api/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"gofr.dev/pkg/gofr"
)

type TenantHandler struct{}

func (h *TenantHandler) Create(c *gofr.Context) (interface{}, error) {
var tenant models.Tenant
if err := c.Bind(&tenant); err != nil {
return nil, err
}

result, err := c.Mongo.InsertOne(c, "tenants", tenant)
if err != nil {
return nil, err
}

tenant.ID = result.(primitive.ObjectID)

return tenant, nil
}

func (h *TenantHandler) GetAll(c *gofr.Context) (interface{}, error) {
var tenants []models.Tenant
err := c.Mongo.Find(c, "tenants", bson.M{}, &tenants)

return tenants, err
}

type UserHandler struct{}

func (h *UserHandler) Create(c *gofr.Context) (interface{}, error) {
var user models.User
if err := c.Bind(&user); err != nil {
return nil, err
}

// TODO: Hash password before storing

result, err := c.Mongo.InsertOne(c, "users", user)
if err != nil {
return nil, err
}

user.ID = result.(primitive.ObjectID)

return user, nil
}

func (h *UserHandler) GetAll(c *gofr.Context) (interface{}, error) {
tenantID, err := primitive.ObjectIDFromHex(c.Param("tenant_id"))
if err != nil {
return nil, err
}

var users []models.User
err = c.Mongo.Find(c, "users", bson.M{"tenant_id": tenantID}, &users)

return users, err
}
46 changes: 46 additions & 0 deletions pkg/api/middleware/rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package middleware

import (
"github.com/carverauto/eventrunner/pkg/api"
"github.com/carverauto/eventrunner/pkg/api/models"
"go.mongodb.org/mongo-driver/bson"
"gofr.dev/pkg/gofr"
gofrHTTP "gofr.dev/pkg/gofr/http"
)

func AuthenticateAPIKey(next gofr.Handler) gofr.Handler {
return func(c *gofr.Context) (interface{}, error) {
apiKey := c.Request.Param("X-API-Key")
if apiKey == "" {
return nil, gofrHTTP.ErrorMissingParam{Params: []string{"X-API-Key"}}
}

var key models.APIKey
if err := c.Mongo.FindOne(c, "api_keys", bson.M{"key": apiKey, "active": true}, &key); err != nil {
return nil, gofrHTTP.ErrorInvalidParam{Params: []string{"X-API-Key"}}
}

// Store the API key in the context for later use if needed
// Note: There doesn't seem to be a direct method to set parameters in the context
// You might need to implement a custom method or use a different approach to store this

return next(c)
}
}

func RequireRole(roles ...string) gofr.Handler {
return func(c *gofr.Context) (interface{}, error) {
userRole := c.Request.Param("X-User-Role")
if userRole == "" {
return nil, gofrHTTP.ErrorMissingParam{Params: []string{"X-User-Role"}}
}

for _, role := range roles {
if userRole == role {
return nil, nil // Allow the request to proceed
}
}

return nil, api.ErrInsufficientPermissions
}
}
25 changes: 25 additions & 0 deletions pkg/api/models/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package models

import (
"go.mongodb.org/mongo-driver/bson/primitive"
)

type Tenant struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
Name string `bson:"name" json:"name"`
}

type User struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
TenantID primitive.ObjectID `bson:"tenant_id" json:"tenant_id"`
Username string `bson:"username" json:"username"`
Email string `bson:"email" json:"email"`
Password string `bson:"password" json:"-"` // Don't expose password in JSON
Role string `bson:"role" json:"role"`
}

type APIKey struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
Key string `bson:"key" json:"key"`
Active bool `bson:"active" json:"active"`
}

0 comments on commit e23073f

Please sign in to comment.