package app import ( "context" "errors" "fmt" "log" "git.farahty.com/nimer/go-mongo/models" "git.farahty.com/nimer/go-mongo/utils" "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(Config.MongoURI), Config.MongoDB) 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 := Config.AdminEmail password, _ := models.MakeHash(Config.AdminPass) 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 := utils.GetUserFromToken[models.UserJWT](token, Config.AccessSecret) 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 }