Rails: Action Cable: Как авторизовать пользователя для подключения определенного канала в зависимости от роли? - PullRequest
0 голосов
/ 28 ноября 2018

В моем приложении на аукционе rails авторизованные пользователи могут одновременно подключаться к 2 каналам на странице продукта (один канал all_users для продукта, другой канал для прямого обмена сообщениями для конкретного пользователя.)

Теперь я хотел бынравится отправлять конфиденциальную информацию только пользователям группы администратора.Я пытался определить запрос подключения к третьему каналу (admin_channel) в сценарии кофе, но я не мог понять, как я могу авторизовать подключение пользователя для 3-го канала в зависимости от роли.

Другой альтернативой может быть использование существующего пользовательского канала, но здесь я не мог понять, как бэкэнд-классы могут знать, какие пользователи в группе администраторов в данный момент находятся в сети (есть ли пользовательский канал запущен и работает) ..

Ты хоть представляешь, как мне этого добиться?Будем благодарны за любую поддержку.

Ниже вы можете найти мой существующий файл connection.rb и файлы coffeescript.

Вот мой файл 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

сценарий кофе:

$( 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)
    )

1 Ответ

0 голосов
/ 28 ноября 2018
$(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. Admin User1 открывает страницу, затем JS-подписывается на UserConnectedChannel
  2. Не администратор User2 открывает страницу, затемJS-подписывается на PrivateAdminMessagesChannel, передавая данные: user_id: CURRENT_USER_ID
  3. Начиная с 2. выше, поскольку User2 только что подписался;затем на бэкэнде, внутри def subscribed при подключении, вы ActionCable.server.broadcast :user_connected, { user_id: current_user.id }
  4. Admin User1 подписываетесь на UserConnectedChannel, а затем получаете с 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, что означает, что они могут общаться друг с другом в частном порядке (другиеАдминистраторы также должны были получить JS-данные :user_connected: { user_id: THAT_USER2_ID }, и поэтому они также должны быть подписаны, поскольку имеет смысл, что AdminUser1, NonAdminUser2 и AdminUser3 могут общаться в одном и том же канале чата ... отчто я получаю с вашими требованиями)
  7. TODO: С 1 по 6 выше, сделайте что-то подобное также с процессом «отключения»

Викторины:

  • Те, которые вы определяете с помощью identified_by в вашем ApplicationCable::Connection, могут быть доступны в файлах вашего канала.В частности, в этом случае можно вызвать current_user.
  • Относительно отказа в подписках см. документы здесь
...