2017-09-24 26 views
2

我试图清理一些被标记为易受SQL注入攻击的代码。因此,我将大量的原始SQL查询字符串转换为ActiveRecord的方法。当我想要将排序(order)应用于嵌套属性时,一种类型的查询会遇到问题,即查找等价物。使用JOIN时ActiveRecord查询中ORDER的动态方向

如果我有一个AccountUser对象,我想通过user_id订购的结果,我可以做到这一点(这是什么码目前有):

->(direction) { Account.joins(:users).order("users.id #{direction}").first } 

然而,这是容易受SQL注入影响。

我知道,如果我通过Account属性排序,你可以只通过一个哈希order

->(direction) { Account.joins(:users).order(created_at: direction).first } 

然而,使用字符串作为属性进行排序(因为它是嵌套)没有按”牛逼结果在正确的查询:

->(direction) { Account.joins(:users).order('users.id': direction).first } 
# SELECT `accounts`.* FROM `accounts` INNER JOIN `users` ON `users`.`account_id` = `accounts`.`id` ORDER BY `accounts`.`users.id` DESC LIMIT 1 

,并使用嵌套哈希也不起作用

->(direction) { Account.joins(:users).order(users: {id: direction}).first } 

我知道我可以强制direction成为一些额外的逻辑接受的值之一,但想知道是否有一个简单的方法来通过ActiveRecord的查询来做到这一点,我错过了。

回答

1

排序由加入协会并通过串恐怕只支持。

然而,有可能merge范围,它允许你写:

->(direction) { Account.joins(:users).merge(User.order(id: direction})).first } 

这确实防止比[:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"]为方向(从引发ArgumentError消息弹拨)以外的值。在我看来,它的代价是降低了可读性,但可以通过将订单定义为User类中的命名范围来尝试减轻问题并提高可重用性。

class User 
    ... 
    self.ordered_by_id(direction) 
    order(id: direction}) 
    end 
    ... 
end 

它使您能够使用

->(direction) { 
    Account.joins(:users).merge(User.ordered_by_id(direction)).first 
} 

诚然,在当前的示例中,这不亮呢,但使用merge更复杂的范围可以大大减少重复。

+0

你可以粘贴一个不工作的例子,因为我害怕我没有完全理解这个问题,但是呢? – ulferts

+0

对不起,正在加载错误版本的文件。它工作得很好,谢谢! – Suever

1

我认为order并不像它那样先进。

但是当你想到ascdesc只有你可以将其过滤:

direction = (%w[asc desc] & [direction.downcase])[0] || 'asc' 
Account.joins(:users).order("users.id #{direction}").first