How do I set a current_user in Rack middleware for GraphQL - ruby

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

Related

session[:id] variable not being picked up within another route from sinatra

I am following this tutorial here on setting up a user login https://learn.co/lessons/sinatra-user-auth
It says that in order to save a session[:id] in my route I would do something like this:
post '/login' do
#user = User.get_id(params['email'],params['password'])
session[:id] = #user # equals a numeric value of the matching user
redirect '/home'
end
however, when i get to my /home route I am no longer able to access the session[:id] that was set in my /login route, it returns nil
# this doesnt return the session id
# consequently I cant lookup my user records
get '/home' do
#user = User.object(session[:id]) # looks up user object with numeric value
erb :home
end
I have tried enabling sessions in my config controller, that didnt work either
module Controllers
class ApplicationController < Sinatra::Base
# . . . .
configure :production, :development do
enable :sessions, :logging
end
end
end
how do I make my app persist the session[:id] between routes?
Have you tried enable :sessions after you require your sinatra gem in your sinatra app? Not in your controller

Rails access request in routes.rb for dynamic routes

Our websites should allow to show different contents related to the given url .. something like a multisite in wordpress where we have one installation and serve the content according to the url.
as it is necessary to have the routes in the correct language I want to use a "dynamic route" approach to serve the right content. My problem is now that I dont find a way how to serve the proper routes in routes.rb if they are dynamic.
How can I "access" or "pass" the request object into any method inside the routes.rb file
f.e. like this
routes.rb
Frontend::Application.routes.draw do
DynamicRouter.load request
end
app/models/dynamic_router.rb
class DynamicRouter
def self.load request
current_site = Site.find_by_host(request.host)
Frontend::Application.routes.draw do
current_site.routes do |route|
get "#{route.match}", to: "#{route.to}"
end
end
end
end
this doesnt work because request is undefined in routes.rb
To answer your question: How can I "access" or "pass" the request object into any method inside the routes.rb file Obtain it as ENV object from rack middleware.See code below
# lib/dynamicrouterrequest.rb
require 'rack'
class DynamicRouterRequest
def initialize(app)
#app = app
end
def call(env)
request=Rack::Request.new(env)
ENV["OBJ_REQUEST"]=request.inspect.to_s
#app.call(env)
end
end
Grab it again in routes
# routes.rb
Frontend::Application.routes.draw do
request=ENV["OBJ_REQUEST"]
DynamicRouter.load request
end
A possible soluction is to create the default rules on routes.rb and add a rack middleware that can transform a path according to the domain
# routes.rb
get '/category/:id', :to => 'categories#show'
In the middleware you can transform a path like 'categoria/:id' to '/category/:id' if the domain matches '.es', before the application hits the router layer.
More on rack middleware: http://guides.rubyonrails.org/rails_on_rack.html

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.

How do I do a really simple Sinatra LDAP authentication?

I looked at the Sinatra docs and they only seem to reference HTTP authentication. I'm looking for a really simple way to control access to routes based on a user being authorised/authenticated via an LDAP server.
I've already built a class that does the LDAP bit and returns an LDAP object if the user has successfully authenticated and nil if they haven't:
>>DirectoryUser.authenticate('user', 'password')
#<DirectoryUser:0x007ffb589a2328>
I can use this to determine if they've successfully authenticated or not.
As a next step I want to splice this into a simple Sinatra app that provides a form to collect the LDAP user and password:
require 'directoryUser'
require 'sinatra'
enable :sessions
get '/form' do
username = params[:username]
password = params[:password]
haml :form
end
Then I want to only allow routes if the 'DirectoryUser' object exists:
get '/protected' do # Only if DirectoryUser object exists
"This route is protected"
end
get '/unprotected' do
"This route is unprotected"
end
I've spent hours trying to find an answer to this but so far and can't seem to find anything that works for me.
I'd probably go with something like this:
require 'directoryUser'
require 'sinatra'
enable :sessions
helpers do
def authorize!
redirect(to('/login')) unless session[:user_id]
end
end
get '/login' do
haml :login # with the login form
end
post '/login' do
user = DirectoryUser.authenticate(params[:username], params[:password])
if user
session[:user_id] = user.id
# Or: session[:logged_in] = true, depending on your needs.
redirect to('/protected')
else
redirect to('/login')
end
end
get '/protected' do
authorize!
'This route is protected'
end
get '/unprotected' do
'This route is unprotected'
end

Resources