Skip to content

Commit

Permalink
adds Guardian based token authentication for data endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
entone committed Jun 28, 2017
1 parent 44ebcd5 commit 9e656e4
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 20 deletions.
13 changes: 13 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,17 @@ config :satori,
app_key: System.get_env("SATORI_APP_KEY"),
role_secret: System.get_env("SATORI_ROLE_SECRET")

config :guardian, Guardian,
allowed_algos: ["HS512"],
verify_module: Guardian.JWT,
issuer: "Brood",
ttl: { 30, :days },
allowed_drift: 2000,
verify_issuer: true,
secret_key: %{
"k" => "R3GxdTsUzG0tfb8ZyME51rF9m51So-mzeMNp2ZMHq1OIu59Rg4naH2EpBx7hueSwEKmQK1vRQXGqGF9BXdkOgA",
"kty" => "oct"
},
serializer: Brood.Resource.Account.GuardianSerializer

import_config "keys.exs"
2 changes: 2 additions & 0 deletions lib/brood/http_router.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
defmodule Brood.HTTPRouter do
use Plug.Router
alias Brood.Resource.Account
alias Brood.Resource.Data

plug :match
plug :dispatch

forward "/account", to: Account.Router
forward "/data", to: Data.Router

end
12 changes: 10 additions & 2 deletions lib/brood/resources/account/account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ defmodule Brood.Resource.Account do
def register(%Account{} = account, password_conf) do
case account.password == password_conf do
true ->
account = %Account{account | password: account.password |> Pbkdf2.hashpwsalt}
:mongo_brood |> Mongo.insert_one(@account_collection, account, pool: DBConnection.Poolboy)
account = %Account{account |
password: account.password |> Pbkdf2.hashpwsalt,
_id: Mongo.object_id()
}
:mongo_brood |> Mongo.insert_one(@account_collection, Map.from_struct(account), pool: DBConnection.Poolboy)
_ -> :password_mismatch
end
end
Expand All @@ -32,6 +35,11 @@ defmodule Brood.Resource.Account do
do: account
end

def from_id(%BSON.ObjectId{} = id) do
:mongo_brood |> Mongo.find_one(@account_collection, %{_id: id}, pool: DBConnection.Poolboy) |> parse_params
end
def from_id(id), do: BSON.ObjectId.decode!(id) |> from_id

def validate_pw(%Account{password: password} = auth, %Account{password: hash} = account) do
Pbkdf2.checkpw(password, hash)
end
Expand Down
14 changes: 6 additions & 8 deletions lib/brood/resources/account/login.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Brood.Resource.Account.Login do
use PlugRest.Resource
alias Brood.Resource.Account
alias Brood.Resource.Account.Router
require Logger

def allowed_methods(conn, state) do
Expand All @@ -15,14 +16,11 @@ defmodule Brood.Resource.Account.Login do
with %Account{} = auth <- conn.params |> Account.parse_params,
%Account{} = account <- auth |> Account.authenticate,
id <- account._id |> BSON.ObjectId.encode!,
do: respond("{\"success\": \"#{id}\"}", conn, state)
end

def respond(data, conn, state) do
{true, conn
|> put_resp_content_type("application/json")
|> put_rest_body(data),
state}
do:
conn
|> Router.sign(account)
|> Router.response_body
|> Router.respond(state)
end

end
16 changes: 8 additions & 8 deletions lib/brood/resources/account/register.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Brood.Resource.Account.Register do
use PlugRest.Resource
alias Brood.Resource.Account
alias Brood.Resource.Account.Router
require Logger

def allowed_methods(conn, state) do
Expand All @@ -14,15 +15,14 @@ defmodule Brood.Resource.Account.Register do
def from_multipart(conn, state) do
with %Account{} = account <- conn.params |> Account.parse_params,
{:ok, %Mongo.InsertOneResult{} = result} <- account |> Account.register(conn.params["password_conf"]),
id <- result.inserted_id |> BSON.ObjectId.encode!,
do: respond(id, conn, state)
account <- Account.from_id(result.inserted_id),
do:
conn
|> Router.sign(account)
|> Router.response_body
|> Router.respond(state)
end

def respond(id, conn, state) do
{true, conn
|> put_resp_content_type("application/json")
|> put_rest_body("{\"success\": \"#{id}\"}"),
state}
end


end
22 changes: 22 additions & 0 deletions lib/brood/resources/account/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,28 @@ defmodule Brood.Resource.Account.Router do
resource "/register", Account.Register
resource "/login", Account.Login

def sign(conn, account) do
conn = Guardian.Plug.api_sign_in(conn, account)
jwt = Guardian.Plug.current_token(conn)
{:ok, claims} = Guardian.Plug.claims(conn)
exp = Map.get(claims, "exp") |> Integer.to_string
{conn
|> Plug.Conn.put_resp_header("authorization", "Bearer #{jwt}")
|> Plug.Conn.put_resp_header("x-expires", exp),
jwt}
end

def response_body({conn, jwt}) do
{conn, "{\"success\": \"#{jwt}\"}"}
end

def respond({conn, data}, state) do
{true, conn
|> Plug.Conn.put_resp_content_type("application/json")
|> PlugRest.Resource.put_rest_body(data),
state}
end

def handle_errors(conn, %{reason: %WithClauseError{term: :no_account}}) do
send_error(conn, 422, "{\"error\": \"Invalid login\"}")
end
Expand Down
17 changes: 17 additions & 0 deletions lib/brood/resources/account/serializer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
defmodule Brood.Resource.Account.GuardianSerializer do
@behaviour Guardian.Serializer

alias Brood.Resource.Account

def for_token(account = %Account{}) do
id = BSON.ObjectId.encode!(account._id)
{:ok, "Account:#{id}" }
end
def for_token(_), do: { :error, "Unknown resource type" }

def from_token("Account:" <> id) do
account = Account.from_id(id)
{ :ok, account }
end
def from_token(_), do: { :error, "Unknown resource type" }
end
21 changes: 21 additions & 0 deletions lib/brood/resources/data/query.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule Brood.Resource.Data.Query do
use PlugRest.Resource
require Logger

def allowed_methods(conn, state) do
{["POST"], conn, state}
end

def content_types_accepted(conn, state) do
{[{{"application", "json", :*}, :from_json}], conn, state}
end

def from_json(conn, state) do
account = Guardian.Plug.current_resource(conn)
Logger.info("#{inspect account}")
Logger.info("#{inspect conn.params}")
#Query InfluxDB
{true, conn, state}
end

end
33 changes: 33 additions & 0 deletions lib/brood/resources/data/router.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule Brood.Resource.Data.Router do
use PlugRest.Router
use Plug.ErrorHandler
alias Brood.Resource.Data
require Logger

plug Plug.Parsers, parsers: [:json], json_decoder: Poison
plug Guardian.Plug.VerifyHeader, realm: "Bearer"
plug Guardian.Plug.LoadResource
plug Guardian.Plug.EnsureAuthenticated, handler: __MODULE__
plug :match
plug :dispatch

resource "/:type/:timeframe", Data.Query

def unauthenticated(conn, params) do
Logger.error "Unauthenticated: #{inspect conn}"
Logger.error "Unauthenticated: #{inspect params}"
conn |> send_error(401, "{\"error\": \"Unauthorized\"}")
end

def handle_errors(conn, other) do
Logger.error("#{inspect other}")
send_error(conn, 500, "{\"error\": \"Ruh Roh!\"}")
end

def send_error(conn, status, data) do
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> send_resp(status, data)
end

end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defmodule CertTest.Mixfile do

def application do
[
applications: [:logger, :cowboy, :plug, :plug_rest, :comeonin, :httpoison, :instream, :mongodb, :poolboy, :gen_mqtt, :satori, :wobserver],
applications: [:logger, :cowboy, :plug, :plug_rest, :comeonin, :guardian, :httpoison, :instream, :mongodb, :poolboy, :gen_mqtt, :satori, :wobserver],
mod: {Brood.Application, []}
]
end
Expand All @@ -23,6 +23,7 @@ defmodule CertTest.Mixfile do
{:cowboy, "~> 1.1"},
{:plug, "~> 1.3"},
{:plug_rest, "~> 0.12.0"},
{:guardian, "~> 0.14.4"},
{:poison, "~> 3.0", override: true},
{:instream, "~> 0.15.0"},
{:mongodb, "~> 0.4.0"},
Expand Down
6 changes: 5 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
%{"certifi": {:hex, :certifi, "1.2.1", "c3904f192bd5284e5b13f20db3ceac9626e14eeacfbb492e19583cf0e37b22be", [:rebar3], []},
%{"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], []},
"certifi": {:hex, :certifi, "1.2.1", "c3904f192bd5284e5b13f20db3ceac9626e14eeacfbb492e19583cf0e37b22be", [:rebar3], []},
"comeonin": {:hex, :comeonin, "3.1.0", "fbf18d43a7cfe7edebaa3bcf702d9a78821d3d3ed0c57c65419f99a8816dfaee", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, optional: false]}]},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, optional: false]}]},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
"elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [:mix], []},
"gen_mqtt": {:hex, :gen_mqtt, "0.3.1", "6ce6af7c2bcb125d5b4125c67c5ab1f29bcec2638236509bcc6abf510a6661ed", [:mix], [{:vmq_commons, "1.0.0", [hex: :vmq_commons, optional: false]}]},
"guardian": {:hex, :guardian, "0.14.4", "331e659e59d8dd2f0a4f05168fbdf511a025359d3c79b2a5406a87c7708393ca", [:mix], [{:jose, "~> 1.8", [hex: :jose, optional: false]}, {:phoenix, "~> 1.2", [hex: :phoenix, optional: true]}, {:plug, "~> 1.3", [hex: :plug, optional: false]}, {:poison, ">= 1.3.0", [hex: :poison, optional: false]}, {:uuid, ">=1.1.1", [hex: :uuid, optional: false]}]},
"hackney": {:hex, :hackney, "1.8.5", "56025cf08aaf0998a5ad6494888a069fc3800f60eb6bdd1fca7adf0f1329c888", [:rebar3], [{:certifi, "1.2.1", [hex: :certifi, optional: false]}, {:idna, "5.0.2", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]},
"httpoison": {:hex, :httpoison, "0.11.2", "9e59f17a473ef6948f63c51db07320477bad8ba88cf1df60a3eee01150306665", [:mix], [{:hackney, "~> 1.8.0", [hex: :hackney, optional: false]}]},
"idna": {:hex, :idna, "5.0.2", "ac203208ada855d95dc591a764b6e87259cb0e2a364218f215ad662daa8cd6b4", [:rebar3], [{:unicode_util_compat, "0.2.0", [hex: :unicode_util_compat, optional: false]}]},
"instream": {:hex, :instream, "0.15.0", "8a7be71b6a9c16ca0ce41a31a5450e14b316dc7f3bba48d02abf1218cade9032", [:mix], [{:hackney, "~> 1.1", [hex: :hackney, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}]},
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, optional: false]}]},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
Expand All @@ -22,6 +25,7 @@
"satori": {:hex, :satori, "0.2.0", "24c9bc5f6881e83ca3b433b21961f6f5d33396b6965a1514330dbe8a638fd8db", [:mix], [{:poison, "~> 3.1", [hex: :poison, optional: false]}, {:websocket_client, "~> 1.2", [hex: :websocket_client, optional: false]}]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.2.0", "dbbccf6781821b1c0701845eaf966c9b6d83d7c3bfc65ca2b78b88b8678bfa35", [:rebar3], []},
"uuid": {:hex, :uuid, "1.1.7", "007afd58273bc0bc7f849c3bdc763e2f8124e83b957e515368c498b641f7ab69", [:mix], []},
"vmq_commons": {:hex, :vmq_commons, "1.0.0", "5f5005c12db33f92f40e818a3617fb148972d59adcf99298c9d3808ef3582e34", [:rebar3], []},
"websocket_client": {:hex, :websocket_client, "1.2.1", "a965ce0be5583c90347400bceca66629e4debd5feb9bd516107e2924bdf39dad", [:rebar3], []},
"wobserver": {:hex, :wobserver, "0.1.7", "377b9a2903728b62e4e89d4e200ec17d60669ccdd3ed72b23a2ab3a2c079694d", [:mix], [{:cowboy, "~> 1.1", [hex: :cowboy, optional: false]}, {:httpoison, "~> 0.11", [hex: :httpoison, optional: false]}, {:plug, "~> 1.3", [hex: :plug, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}, {:websocket_client, "~> 1.2", [hex: :websocket_client, optional: false]}]}}

0 comments on commit 9e656e4

Please sign in to comment.