NameError trying to access Connection variable in Channel - ruby

I have a Connection that sets a current_user variable:
class MyConnection < ActionCable::Connection::Base
identified_by :current_user
def connect
# snip - work to authenticate the user
self.current_user = user
end
end
According to the docs, this should make the variable current_user available to the corresponding channel. The docs say:
Also note that in this example, current_user is available because it was marked as an identifying attribute on the connection. All such identifiers will automatically create a delegation method of the same name on the channel instance.
However, when I try to access current_user in MyChannel:
class MyChannel < ActionCable::Channel::Base
def subscribed
subscription_name = "my_channel#{current_user.id}"
stream_from subscription_name
end
end
I get a NameError:
NameError - undefined local variable or method `current_user' for #<MyChannel:0x00000000deadbeef>
What am I doing wrong?

Related

How do I set a current_user in Rack middleware for GraphQL

I'm using GraphQLPlayground configured in my config.ru
map "/graphql-playground" do
use GraphqlPlaygroundAuthentication
use GraphQLPlayground, endpoint: "/graphql"
end
end
And I want to authorize my requests via GraphqlPlaygroundAuthentication since Rack does not send cookies with the request.
In my graphql_controller.rb I have this:
def execute
variables = prepare_variables(params[:variables])
query = params[:query]
operation_name = params[:operationName]
context = {
current_actor: current_user
}
# more code here
How do I set current_user inside of GraphqlPlaygroundAuthentication.rb?
I have tried to set cookies with Rack::Utils.set_cookie_header! but even though I see the cookies inside Application tab in my browser, my current_user inside request is nil.
I have no idea how to set the current_user to be available inside the controller and then my playground requests are unauthorized.
Reason why Playground is mounted inside config.ru and not routes.rb: CSP configuration of my project. I cannot change that.
You need to define current_user method in your controllers/application_controller.rb.
class ApplicationController < ActionController::API
def current_user
# If test situation when user is logged in
User.first
# If test situation when user is not logged in
# nil
end
end

User Session Authentication in Rails 5

I am trying to implement notification using the action cable in Rails 5.
After reading the tutorial for the Action cable, which work on the session & Cookies based Authentication to receive the notification for the current logged in user.
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.username
end
protected
def find_verified_user
if verified_user = env['warden'].session(:user)
verified_user
else
reject_unauthorized_connection
end
end
end
In the find_verified_user , I am not able to get the user object from the session.
Can anyone help me, to authenticate the user in action cable.
If your session_store (config/initializers/session_store.rb) uses :cookie_store, then you can read the session from encrypted cookies:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
if user = User.find_by(id: session[:user_id])
self.current_user = user
else
reject_unauthorized_connection
end
end
private
def session
key = Rails.application.config.session_options.fetch(:key)
cookies.encrypted[key]&.symbolize_keys || {}
end
end
end
Because you have not access to session, you must use cookie:
1) Go to your_app/config/initializers/session_store.rb and paste this code:
Rails.application.config.session_store :cookie_store, key: 'your_super_secret_code'
2) After anywhere you can get user id:
key = Rails.application.config.session_options.fetch(:key)
cookies.encrypted[key]&.symbolize_keys || {}
User.find(cookies["warden.user.user.key"][0][0])
Example for session: How can I find a devise user by it's session id?

Setting instance variables in ActionMailer

I have around 30 mailer methods where I'm passing the user as an argument. Since I need access to the #user variable in the view, I'm having to set this instance variable in every mailer method, for example, send_x_email(user).
Normally this would be done in an initialize method, but I've read that mailers act a bit differently. Additionally, some of the methods take a different number of arguments (one just takes user, the other takes user and message).
I've investigated before_action callbacks and looked at this post
Setting instance variables in Action Mailer?
...but I'm still stuck.
I would appreciate any thoughts on how to simplify things and remove #user = user from the 30 or so methods in the mailer class. Cheers!
class ReminderSender < ActionMailer::Base
def send_commands_email(user)
#user = user
mail(to: #user.email,
subject: "All Commands",
from: "<commands##{ENV['DOMAIN']}>")
end
def send_attachment_warning(user, message)
#user = user
#message = message
mail(to: #user.email,
subject: "Attachment Warning",
from: "<attachments##{ENV['DOMAIN']}>")
end
end
Try defining a 'mail' method in your class and declaring an instance variable there e.g.
class YouMailer
def send_email(user, message)
subject = 'something'
body = message
mail(user, {subject: subject, body: body}})
end
def mail(user, options={})
#user = user
mail_options = {to: #user.email}.merge(options)
super(mail_options)
end
end
But you might need to specify the 'template_path' and 'template_name' options with that strategy.
My suggestion would be to keep things as they are. Having "#user = user" in all of your mailer methods out of necessity isn't bad.

Strong parameters and Nested Routes - Rails 4.0

I have no idea how this works in rails but I set up routes like this:
resources :users do
resources :api_keys
end
(User has_many: api_keys, api_key belongs_to: user)
So I then (since I only care about API Keys), created the following controller:
class ApiKeysController < ApplicationController
before_action :authenticate_user!
def index
#user = User.find(params[:user_id])
#api_key = User.apikeys
end
def create
#user = User.find(params[:user_id])
#api_key = ApiKey.new(create_new_api_key)
create_api_key(#api_key, #user)
end
def destroy
destroy_api_key
end
private
def create_new_api_key
params.require(:api_key).permit(user_attributes: [:id], :api_key)
end
end
Which states, authenticate user before every action, index fetches all api keys based on a user id. create is suppose to create an api key based on a user id, (note: create_api_key(#api_key, #user) just an abstracted method that states - if we saved, redirect to user_path with a message, if we failed, back to user path with a error message)
And destroy, well that just finds an api key, destroys it and redirects (again with the abstraction).
Whats the issue?
the create_new_api_key method. Its freaking out and saying:
syntax error, unexpected ')', expecting => (SyntaxError)
I thought this is how I pass in the user id ??
You need to change the order of the arguments passed in to permit to fix the syntax error:
def create_new_api_key
params.require(:api_key).permit(:api_key, user_attributes: [:id])
end

Can't access current_user inside .new do block in the ApplicationController

I'm using devise and the bitbucket api gem and I have a method in my ApplicationController which creates an instance so I can make API calls. To do that, it tries to read the token and secret from the current_user.
This works fine with hardcoded token and secret strings, I'm also able to do puts current_user.inspect before the do block, and that all works fine. I'm also sure that bb_token and bb_secret exist (I'm able to call puts on them individually).
But once I try to create my bitbucket instance, it can't read current_user anymore. Any ideas?
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
def bitbucket
puts "token----------"
puts current_user
#bitbucket = BitBucket.new do |config|
config.oauth_token = current_user.bb_token # replaceing this with hardcoded string works
config.oauth_secret = current_user.bb_secret # replaceing this with hardcoded string works
config.client_id = 'xx'
config.client_secret = 'yy'
config.adapter = :net_http
end
end
end
And the error:
NameError (undefined local variable or method `current_user' for #<BitBucket::Client:0x007fbebc92f540>):
app/controllers/application_controller.rb:12:in `block in bitbucket'
app/controllers/application_controller.rb:11:in `bitbucket'
It seems block passed to BitBucket.new is executed in context of new BitBucket::Client instance (BitBucket.new is really BitBucket::Client.new, according to this).
A glance to the source confirms this supposition.
If you want to pass current_user, you can recall that the blocks are closures, so they keep the context in which they are defined. So you can do something like this:
def bitbucket
# (...)
user = current_user # local variable assignment
#bitbucket = BitBucket.new do |config|
config.oauth_token = user.bb_token # it works because user is local variable and the block is closure
# (...)
end
end
Inside BitBucket.new do..end block,self is set to config. But current_user is not a instance method of BitBucket class. Thus a valid error is thrown.

Resources