135 lines
3.5 KiB
Go
135 lines
3.5 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
|
|
"git.farahty.com/nimer/go-mongo/models"
|
|
"github.com/99designs/gqlgen/graphql"
|
|
"github.com/99designs/gqlgen/graphql/handler/transport"
|
|
"github.com/casbin/casbin/v2"
|
|
mongodbadapter "github.com/casbin/mongodb-adapter/v3"
|
|
"github.com/golang-jwt/jwt/v4"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
var (
|
|
Authorizer *casbin.Enforcer
|
|
)
|
|
|
|
func LoadAuthorizer(ctx context.Context) error {
|
|
a, err := mongodbadapter.NewAdapterWithClientOption(options.Client().ApplyURI(os.Getenv("MONGO_URI")), os.Getenv("MONGO_DB"))
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if enforcer, err := casbin.NewEnforcer("./model.conf", a); err != nil {
|
|
return err
|
|
} else {
|
|
Authorizer = enforcer
|
|
|
|
}
|
|
|
|
a.AddPolicy("mongodb", "p", []string{"*", "login", "mutation"})
|
|
a.AddPolicy("mongodb", "p", []string{"todos-admin", "createTodo", "mutation"})
|
|
a.AddPolicy("mongodb", "p", []string{"todos-admin", "todos", "query"})
|
|
a.AddPolicy("mongodb", "p", []string{"todos-admin", "todo", "query"})
|
|
|
|
a.AddPolicy("mongodb", "p", []string{"category-admin", "createCategory", "mutation"})
|
|
a.AddPolicy("mongodb", "p", []string{"category-admin", "categories", "query"})
|
|
a.AddPolicy("mongodb", "p", []string{"category-admin", "category", "query"})
|
|
|
|
a.AddPolicy("mongodb", "p", []string{"users-admin", "users", "query"})
|
|
a.AddPolicy("mongodb", "p", []string{"users-admin", "createUser", "mutation"})
|
|
|
|
Authorizer.AddGroupingPolicy("admin", "users-admin")
|
|
Authorizer.AddGroupingPolicy("admin", "todos-admin")
|
|
Authorizer.AddGroupingPolicy("admin", "category-admin")
|
|
|
|
email := os.Getenv("ADMIN_EMAIL")
|
|
password, _ := models.MakeHash(os.Getenv("ADMIN_PASSWORD"))
|
|
|
|
if _, err := FindOne[models.User](ctx, "users", bson.M{"email": email}); err != nil {
|
|
log.Println("Creating admin user")
|
|
|
|
admin, err := InsertOne[models.User](ctx, "users", models.User{
|
|
Password: &password,
|
|
Email: &email,
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("error creating admin user: %w", err)
|
|
}
|
|
|
|
Authorizer.AddRoleForUser(admin.ID.Hex(), "admin")
|
|
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func AuthorizeWebSocket(ctx context.Context, initPayload transport.InitPayload) (context.Context, *transport.InitPayload, error) {
|
|
|
|
var token string
|
|
var ok bool
|
|
|
|
if token, ok = initPayload["token"].(string); !ok {
|
|
return ctx, &initPayload, nil
|
|
}
|
|
|
|
user, err := getUserFromToken(token)
|
|
|
|
if err != nil {
|
|
ctx = SetStatus(ctx, err.Error())
|
|
if errors.Is(err, jwt.ErrTokenExpired) {
|
|
ctx = SetTokenExpired(ctx, true)
|
|
}
|
|
return ctx, &initPayload, nil
|
|
}
|
|
|
|
ctx = SetCurrentUser(ctx, user)
|
|
ctx = SetStatus(ctx, "ok")
|
|
|
|
return ctx, &initPayload, nil
|
|
}
|
|
|
|
func RootFieldsAuthorizer(ctx context.Context, next graphql.RootResolver) graphql.Marshaler {
|
|
|
|
if err := AuthorizeOperation(ctx); err != nil {
|
|
graphql.AddError(ctx, err)
|
|
return graphql.Null
|
|
}
|
|
|
|
return next(ctx)
|
|
|
|
}
|
|
|
|
func AuthorizeOperation(ctx context.Context) error {
|
|
|
|
user := "Anonymous"
|
|
object := graphql.GetRootFieldContext(ctx).Field.Name
|
|
action := string(graphql.GetOperationContext(ctx).Operation.Operation)
|
|
|
|
if object == "__type" || object == "__schema" || object == "__typename" {
|
|
return nil
|
|
}
|
|
|
|
if obj, err := CurrentUser(ctx); err == nil {
|
|
user = string(obj.ID)
|
|
}
|
|
|
|
if allowed, err := Authorizer.Enforce(user, object, action); err != nil {
|
|
return fmt.Errorf("error while enforcing user roles \n%s", err.Error())
|
|
|
|
} else if !allowed {
|
|
return fmt.Errorf("user %s is not allowed to access %s %s", user, action, object)
|
|
}
|
|
|
|
return nil
|
|
}
|