简体   繁体   English

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

[英]Rails: Action Cable: How to authorize user to connect specific channel based on role?

In my rails auction app, authorized users can connect to 2 channels simultaneously within the product page (one is all_users channel for the product, the other is user specific channel for direct messaging.) 在我的Rails拍卖应用中,授权用户可以在产品页面中同时连接到2个通道(一个是产品的all_users通道,另一个是用于直接消息传递的用户特定通道。)

Now I would like to send sensitive information only to admin group users. 现在,我只想向管理员组用户发送敏感信息。 I tought I can define a third channel connection request (admin_channel) in the coffee script but I couldn't figured out how I can authorize user connection for the 3rd channel based on role. 我可以在coffee脚本中定义第三个通道连接请求(admin_channel),但我不知道如何根据角色授权第三通道的用户连接。

Another alternative might be utilizing the existing user specific channel but here I couldn't figured out how backend classes may know which users in the admin group currently is online (has a user channel up&running).. 另一个选择可能是利用现有的特定于用户的渠道,但是在这里我无法弄清楚后端类如何知道admin组中的哪些用户当前处于联机状态(具有正在运行的用户渠道)。

Do you have any idea how I can achieve it? 您有什么想法可以实现吗? Any kind of support will be appreciated.. 任何形式的支持将不胜感激。

Below you can find my existing connection.rb file and coffeescript files. 在下面可以找到我现有的connection.rb文件和coffeescript文件。

Here is my connection.rb file: 这是我的connection.rb文件:

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

coffee script: 咖啡脚本:

$( document ).ready ->
    App.myauction = App.cable.subscriptions.create({
      channel: 'MyauctionChannel'
      id: $('#auctionID').attr('data-id')
      },
      connected: ->
        console.log "Connected"
        # Called when the subscription is ready for use on the server

      disconnected: ->
        # Called when the subscription has been terminated by the server

      speak: (message) ->
        @perform 'speak', message: message

      received: (data) ->
        console.log(data)
        # Called when there's incoming data on the websocket for this channel
    )

    App.myauctionuser = App.cable.subscriptions.create({
      channel: 'MyauctionChannel',
      id: $('#auctionID').attr('data-uuid-code')
      },
      connected: ->
        console.log "user connected"
        # Called when the subscription is ready for use on the server

      disconnected: ->
        # Called when the subscription has been terminated by the server

      speak: (message) ->
        @perform 'speak', message: message

      received: (data) ->
        # console.log ("user channel ")
        # console.log(data)
    )
$(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 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

Above is the easiest way I could think of. 以上是我能想到的最简单的方法。 Not the most efficient one because there's an extra level of authorization happening per stream (see inside stream_from block) unlike if we have different broadcast-names of which the authorization would happen only once on the "connecting" itself, and not each "streaming"... which can be done via something like: 这不是最有效的广播,因为每个流都有一个额外的授权级别(请参见stream_from块内部),这与我们是否具有不同的广播名称不同,而该广播名称的授权只会在“连接”本身上发生一次,而不是在每个“流”上发生一次...可以通过以下方式完成:

  1. Admin User1 opens page then JS-subscribes to UserConnectedChannel 管理员User1打开页面,然后JS订阅到UserConnectedChannel
  2. Non-admin User2 opens page then JS-subscribes to PrivateAdminMessagesChannel passing in data: user_id: CURRENT_USER_ID 非管理员User2打开页面,然后JS订阅到PrivateAdminMessagesChannel传入数据: user_id: CURRENT_USER_ID
  3. From 2. above, as User2 has just subscribed; 从上面的2.开始,因为User2刚刚订阅; then on the backend, inside def subscribed upon connection, you ActionCable.server.broadcast :user_connected, { user_id: current_user.id } 然后在后端,在连接内def subscribed ,您ActionCable.server.broadcast :user_connected, { user_id: current_user.id }
  4. Admin User1 being subscribed to UserConnectedChannel then receives with data { user_id: THAT_USER2_id } 订阅UserConnectedChannel管理员User1然后接收data { user_id: THAT_USER2_id }
  5. From 4 above, inside the JS received(data) callback, you then now JS-subscribe to PrivateAdminMessagesChannel passing in data: THAT_USER2_id`. 从上面的4开始,您现在在JS received(data)回调内部,通过JS订阅了PrivateAdminMessagesChannel传入数据:THAT_USER2_id。
  6. Now User1 and User2 are both subscribed to PrivateAdminMessagesChannel user_id: THAT_USER2_id which means that they can privately talk with each other (other admins should also have had received :user_connected 's JS data: { user_id: THAT_USER2_ID } , and so they should also be subscribed as well, because it makes sense that AdminUser1, NonAdminUser2, and AdminUser3 can talk in the same chat channel... from what I was getting with your requirements) 现在,User1和User2都已订阅PrivateAdminMessagesChannel user_id: THAT_USER2_id ,这意味着它们可以彼此PrivateAdminMessagesChannel user_id: THAT_USER2_id (其他管理员也应该已经收到:user_connected的JS数据: { user_id: THAT_USER2_ID } ,因此也应该订阅它们同样,因为AdminUser1,NonAdminUser2和AdminUser3可以在同一个聊天通道中进行交谈是合理的……根据我的要求,
  7. TODO: From 1 to 6 above, do something similar also with the "disconnection" process 待办事项:从上面的1到6,也可以对“断开连接”过程执行类似的操作

Trivias: Trivias:

  • Those you define with identified_by in your ApplicationCable::Connection can be acceessed in your channel files. 您在ApplicationCable::Connection使用identified_by定义的那些可以在您的通道文件中使用。 In particular, in this case, current_user can be called. 特别是在这种情况下,可以调用current_user
  • Regarding, rejecting subscriptions, see docs here 关于拒绝订阅,请参阅此处的文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM