go-mongo/app/auth.go
2025-05-31 18:38:25 +03:00

135 lines
3.4 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" {
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
}