Rails:操作电缆:如何根据角色授权用户连接特定的通道?

时间:2018-11-28 20:26:41

标签: ruby-on-rails actioncable

在我的Rails拍卖应用中,授权用户可以在产品页面中同时连接到2个通道(一个是产品的all_users通道,另一个是用于直接消息传递的用户特定通道)。

现在,我只想向管理员组用户发送敏感信息。我可以在coffee脚本中定义第三个通道连接请求(admin_channel),但我不知道如何根据角色授权第三通道的用户连接。

另一种替代方法可能是利用现有的特定于用户的渠道,但是在这里我无法弄清楚后端类如何知道管理组中的哪些用户当前处于联机状态(用户通道正在运行)。

您是否知道我该如何实现?任何形式的支持将不胜感激。

在下面,您可以找到我现有的connection.rb文件和coffeescript文件。

这是我的connection.rb文件:

$output = [];
foreach ( $data as $item )  {
    $split = explode(":", $item );
    if ( count($split) > 1 )   {
        $key = array_shift($split);
        $join = ":";
    }
    else    {
        $split = explode(" ", $item);
        if ( strtolower($split[count($split)-1]) == "warranty" ||
                is_numeric($item[0]) ){
            $key = array_pop($split);
        }
        else    {
            $key = array_shift($split);
        }

        $join = " ";
    }
    $output[$key] = implode($join, $split);
}

print_r($output);

咖啡脚本:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user


    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user # this checks whether a user is authenticated with devise
      if verified_user = env['warden'].user
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

1 个答案:

答案 0 :(得分:1)

$(document).ready ->
    App.privateAdminMesssagesChannel = App.cable.subscriptions.create({
        channel: 'PrivateAdminMessagesChannel'
      },
      connected: ->
      disconnected: ->
      // call this function to send a message from a Non-Admin to All Admins
      sendMessageToAdmins: (message) ->
        @perform 'send_messsage_to_admins', message: message
      // call this function to send a messsage from an Admin to (a Non-admin + all Admins)
      sendMessageToUserAndAdmins: (message, toUserId) ->
        @perform 'send_messsage_to_user_and_admins', message: message, to_user_id: toUserId

      received: (data) ->
        console.log(data.from_user_id)
        console.log(data.to_user_id)
        console.log(data.message)

        if data.to_user_id
          // this means the message was sent from an Admin to (a Non-admin + all admins)
        else
          // this means the message was sent from a Non-admin to All Admins
          // do some logic here i.e. if current user is an admin, open up one Chatbox
          // on the page for each unique `from_user_id`, and put data.message
          // in that box accordingly
    )

private_admin_messages_channel.rb

class PrivateAdminMessagesChannel < ActionCable::Channel::Base
  def subscribed    
    stream_from :private_admin_messages_channel, coder: ActiveSupport::JSON do |data|
      from_user = User.find(data.fetch('from_user_id'))
      to_user = User.find(data['to_user_id']) if data['to_user_id']
      message = data.fetch('message')

      # authorize if "message" is sent to you (a non-admin), and also
      # authorize if "message" is sent to you (an admin)

      if (to_user && to_user == current_user) || (!to_user && current_user.is_admin?)
        # now, finally send the Hash data below and transmit it to the client to be received in the JS-side "received(data)" callback
        transmit(
          from_user_id: from_user.id,
          to_user_id: to_user&.id,
          message: message
        )
      end
    end
  end

  def send_message_to_admins(data)
    ActionCable.server.broadcast 'private_admin_messages_channel', 
      from_user_id: current_user.id,
      message: data.fetch('message')
  end

  def send_message_to_user_and_admins(data)
    from_user = current_user

    reject unless from_user.is_admin?

    ActionCable.server.broadcast 'private_admin_messages_channel', 
      from_user_id: from_user.id,
      to_user_id: data.fetch('to_user_id'),
      message: data.fetch('message')
  end
end

以上是我能想到的最简单的方法。效率不是最高的一种,因为每个流都会发生额外级别的授权(请参见stream_from块),这与我们是否具有不同的广播名称不同,而广播名称的授权只会在“连接”本身上发生一次,而不会每个“流式传输” ...都可以通过类似的方式完成:

  1. 管理员User1打开页面,然后JS订阅UserConnectedChannel
  2. 非管理员用户2打开页面,然后JS订阅PrivateAdminMessagesChannel传递数据:user_id: CURRENT_USER_ID
  3. 从上面的2.开始,因为User2刚刚订阅;然后在后端的def subscribed内部ActionCable.server.broadcast :user_connected, { user_id: current_user.id }
  4. 正在订阅UserConnectedChannel的管理员User1,然后收到data { user_id: THAT_USER2_id }
  5. 从上面4开始,在JS received(data)回调内部,您现在通过JS订阅PrivateAdminMessagesChannel传递数据:THAT_USER2_id`。
  6. 现在User1和User2都已订阅PrivateAdminMessagesChannel user_id: THAT_USER2_id,这意味着它们可以彼此私聊(其他管理员也应该已经收到:user_connected的JS数据:{ user_id: THAT_USER2_ID },因此也应该订阅它们,因为AdminUser1,NonAdminUser2和AdminUser3可以在同一个聊天通道中进行交谈是合理的……根据您的要求,我得到了
  7. TODO:从上面的1到6,也可以通过“断开连接”过程执行类似的操作

琐事:

  • 您在identified_by中用ApplicationCable::Connection定义的内容可以加入您的频道文件中。特别是在这种情况下,可以调用current_user
  • 关于拒绝订阅,请参见docs here