What's the alternative of before_filter from Rails in Phoenix/Elixir? - rest

In my Phoenix application, I have a pipe and scope "api"
pipeline :api do
plug(:accepts, ["json"])
end
scope "/api" .....
# .....
end
How can I protect it by an API Key which is passed through a special header? That is, I'd like something like this:
defmodule MyApp.MyController do
use MyApp.Web, :controller
:before_filter :authenticate_user_by_api_key!
def authenticate_user_by_api_key!(conn, params) do
# check if a header exists and key is valid
end
end
I'm planning on validating a header. How can I do that without relying on any third-party libraries?
Also. If I wanted to use a module instead of a single function, how would I do that?

Local Plug
If it's a local method, you can simply use the plug construct within the controller.
defmodule MyApp.MyController do
use MyApp.Web, :controller
plug :authenticate_user_by_api_key!
defp authenticate_user_by_api_key!(conn, params) do
# Authenticate or something
end
end
See this answer and read more about Plugs here.
Module Plug
If you'd like to call the function from a Module, your module must export the init/1 and call/2 methods:
defmodule MyApp.Plugs.Authentication do
import Plug.Conn
def init(default), do: default
def call(conn, default) do
# Check header for API Key
end
end
And use it like this in your controller:
defmodule MyApp.MyController do
use MyApp.Web, :controller
plug MyApp.Plugs.Authentication
# Controller Methods
end
Read the Phoenix Guide on Module Plugs for more details.

Related

Header-based authentication for REST API in Phoenix

I'm implementing a rest api in Elixir. An api-key is passed to each request to authenticate a user. In a plug I have this:
defmodule MyApp.ApiSecurity do
def init(options) do
options
end
def call(conn, _opts) do
# checking if "api-key" headers exists
# and key is valid
# .... what's next?
# if it's a) valid
# b) invalid or there's no "api-key" header
# ???
end
end
I know how to implement it for a normal, form-based authentication with a state and session. But in rest api there's no session. Then, what should I do? In other words, What should be in the rest of the function "call" when a) an api-key is valid b) invalid?
If the key is invalid or not present, you'd normally send the error message with a proper error status code and then call Plug.Conn.halt/1, which will stop this request from going further through the plug pipeline. If they key is valid, you'd probably want to assign some value to the conn, (e.g. user_id) which the rest of your application can use.
For example:
def call(conn, _opts) do
case authenticate(conn) do
{:ok, user_id} ->
conn
|> assign(:user_id, user_id)
:error ->
conn
|> send_resp(401, "Unauthenticated")
|> halt()
end
end
end
Now, any plugs that are plugged after this one can be sure that there exists a valid user_id in conn.assigns and can make use of it.
For a more real-world approach, you can see how guardian does this:
Guardian.Plug.EnsureResource
Guardian.Plug.ErrorHandler

2 separate controllers for the same end point in html and json or a single one?

I have the end points "/customers" and "/api/v1/customers", in html and json respectively for a list of customers. Do I have to create 2 different controllers and thus actions for them? Or can I return html or json from a single controller and action depending a requested format: html or json? Note that for "/api/v1/customers" I need authentication via an Api Key.
You can have one controller and action for both endpoints, but I would advise against it.
You mentioned that those controllers need to do different stuff, so instead of adding stuff like "if json then check api key" make two separate controllers and extract common code of getting all the customers.
There is a great talk about untangling business logic from http interface: http://www.elixirconf.eu/elixirconf2016/lance-halvorsen Getting a list of customers might be out of your controllers, so at the end you will have two controllers like this:
defmodule MyApp.Api.CustomersController do
plug MaApp.ApiAuth #plug for checking api key
def index(conn, params) do
...
customers = ActualLogic.get_customers()
...
end
end
def MyApp.CustomersController do
plug MyApp.UserAuth #for example checks if user is logged in
def index(conn, params) do
...
customers = ActualLogic.get_customers()
...
end
end
At the end your controller does not perform any logic, it calls something else to do the job and action is responsible only for web stuff like parsing params, authentication via api key, session cookies and translating end result to json/html.

Qualified element/attribute forms and unqualified forms with Spyne soap server

Is there any way to use elementFormDefault="unqualified" server schema type with Spyne server?
Now my all trials end up with method response result:
<senv:Envelope xmlns:tns="http://test.com/remoteService/"
xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:testResponse>
<tns:status>ok</tns:status>
</tns:testResponse>
</senv:Body>
And generated wsdl fragment with "qualified" elementFormDefault :
<xs:schema targetNamespace="http://test.com/remoteService/" elementFormDefault="qualified"></xs:schema>
How to configure method or parameters model to get result like this:
<senv:Envelope xmlns:tns="http://test.com/remoteService/"
xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<tns:testResponse>
<status>ok<status>
</tns:testResponse>
</senv:Body>
My goal is to generate result where child element:
<tns:status>ok</tns:status>
will appear without namespace prefix - like this:
<status>ok<status>
If you wonder how to add listener to the event_manager for method_return_string or for another event, see bellow a full example:
from spyne import Application, rpc, ServiceBase, Iterable, Integer, Unicode
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
class HelloWorldService(ServiceBase):
#rpc(Unicode, Integer, _returns=Iterable(Unicode))
def say_hello(ctx, name, times):
for i in range(times):
yield u'Hello, %s' % name
def on_method_return_string(ctx):
ctx.out_string[0] = ctx.out_string[0].replace(b'Hello>', b'Good by')
HelloWorldService.event_manager.add_listener('method_return_string',
on_method_return_string)
application = Application([HelloWorldService], 'spyne.examples.hello.soap',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11())
wsgi_application = WsgiApplication(application)
if __name__ == '__main__':
import logging
from wsgiref.simple_server import make_server
server = make_server('127.0.0.1', 8000, wsgi_application)
server.serve_forever()
As of Spyne 2.12 this is still the only way to remove namespaces from response variables.
As of 2.10, Spyne does not support this.
The patch would be a bit hairy. Chime in at soap#python.org if you're willing to work on this.
A workaround would be to remove namespace prefixes manually from outgoing documents in a method_return_document hook. If you need to enforce the same for incoming documents as well, you either have to modify the Wsdl as well in a document_built event, or use soft validation (soft validation does not care about namespaces) or no validation at all.

Redirect Sinatra request changing method and body

Is there a way to handle a GET request on Sinatra and make a PATCH request with a different body on the same server? User makes a request GET /clean_beautiful_api and server redirects it to PATCH /dirty/clogged_api_url_1?crap=2 "{request_body: 1}"?
I want to clean up legacy API without interfering with the functionality.
If I've understood correctly, the easiest way is to extract the block used for the patch into a helper:
patch "/dirty/clogged_api_url_1"
crap= params[:crap]
end
to:
helpers do
def patch_instead( params={} )
# whatever you want to do in here
crap= params[:crap]
end
end
get "/clean_beautiful_api" do
patch_instead( params.merge(request_body: 1) )
end
patch "/dirty/clogged_api_url_1"
patch_instead( params )
end
Or you could use a lambda…
Patch_instead = ->( params={} ) {
# whatever you want to do in here
crap= params[:crap]
}
get "/clean_beautiful_api" do
Patch_instead.call( params.merge(request_body: 1) )
end
# you get the picture
the main thing is to extract the method to somewhere else and then call it.
Edit: You can also trigger another route internally using the Rack interface via call.

Multiple instances of Sinatra::Base applications with different configurations

I developed a Rack application based on Sinatra::Base. Now I would like to use many instances of it, each with a slightly different configuration, in a single Rack application.
My rackup should look like
use Rack::Lint
map '/mel' do
run Site.new('/home/mel/site').app
end
map '/pub' do
run Site.new('/pub').app
end
The Site class collects various parameters (in this example only the root dir) and does some preparatory work. The #app method should return a Server object that holds a reference to the served Site instance.
This is an example of the Site and Server code:
class Site
def initialize(root_dir)
#root_dir = root_dir
# ... set up things ...
end
def app
# This is where a new Server Rack application should be created
return Server.new { |server| server.set :site, self }
end
end
class Server < Sinatra::Base
before do
#content = settings.site.all_files
end
get /(.*)/ do |url_path|
# do things...
end
end
The problem with this code is that the #app method does not return a valid Rack application.
What should I do in #app to return a new, configured Server Rack application?
This is a way to make it work suggested by "carloslopes" on #sinatra.
The Site#app method becomes
class Site
def app
# This is where a new Server object should be created
return Server.new(self)
end
end
and the Server objects get their parameters via instance variables:
class Server < Sinatra::Base
def initialize(site)
super()
#site = site
end
before do
#content = #site.all_files
end
get /(.*)/ do |url_path|
# do things...
end
end
Edit: made community wiki so that other can make the solution even better and share the credit.