Skip to content

Commit

Permalink
Issue #505: Optional basic authentication for web sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
gotthardp committed Oct 12, 2018
1 parent 5077442 commit cf097d0
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 10 deletions.
3 changes: 3 additions & 0 deletions doc/Connectors.md
Expand Up @@ -113,6 +113,9 @@ To create a web socket connector you set:
- **Publish Uplinks** to a URL pattern starting with a slash, e.g. '/ws/uplink/{devaddr}'
- **Publish Events** to another URL pattern, e.g. '/ws/events/{devaddr}'

HTTP Basic authentication is supported. On the Authentication tab you may set the
*Name* and *Password/Key* that shall be verified by the server.

The patterns may have any structure (doesn't have to start with `/ws`), but must
be unique across the entire server, including the web-admin itself.

Expand Down
43 changes: 33 additions & 10 deletions src/lorawan_connector_ws.erl
Expand Up @@ -39,44 +39,67 @@ stop_connector(Id) ->
lorawan_http_registry:delete({ws, Id}).

init(Req, [#connector{connid=Id}=Connector, Type]) ->
Bindings = lorawan_admin:parse(cowboy_req:bindings(Req)),
case validate(maps:to_list(Bindings)) of
ok ->
case authorize(Req, Connector) of
{ok, Bindings} ->
{ok, Timeout} = application:get_env(lorawan_server, websocket_timeout),
{cowboy_websocket, Req,
#state{conn=Connector, type=Type, path=cowboy_req:path(Req), bindings=Bindings},
#{idle_timeout => Timeout}};
unauthorized ->
lorawan_utils:throw_error({connector, Id}, unauthorized),
Req2 = cowboy_req:reply(403, Req),
{ok, Req2, undefined};
{error, Error} ->
lorawan_utils:throw_error({connector, Id}, Error),
Req2 = cowboy_req:reply(404, Req),
{ok, Req2, undefined}
end.

validate([{Key, Value} | Other]) ->
case validate0(Key, Value) of
authorize(Req, #connector{name=User})
when User == undefined; User == <<>> ->
validate(Req);
authorize(Req, #connector{name=User, pass=Pass}) ->
case cowboy_req:parse_header(<<"authorization">>, Req) of
{basic, User, Pass} ->
validate(Req);
_Else ->
unauthorized
end.

validate(Req) ->
Bindings = lorawan_admin:parse(cowboy_req:bindings(Req)),
case validate0(maps:to_list(Bindings)) of
ok ->
{ok, Bindings};
Error ->
Error
end.

validate0([{Key, Value} | Other]) ->
case validate_key(Key, Value) of
ok ->
validate(Other);
Else ->
Else
end;
validate([])->
validate0([])->
ok.

validate0(app, App) ->
validate_key(app, App) ->
case mnesia:dirty_read(handler, App) of
[#handler{}] ->
ok;
_Else ->
{error, {unknown_application, App}}
end;
validate0(deveui, DevEUI) ->
validate_key(deveui, DevEUI) ->
case mnesia:dirty_read(device, DevEUI) of
[#device{}] ->
ok;
_Else ->
{error, {unknown_deveui, lorawan_utils:binary_to_hex(DevEUI)}}
end;
validate0(devaddr, DevAddr) ->
validate_key(devaddr, DevAddr) ->
case mnesia:dirty_read(node, DevAddr) of
[#node{}] ->
ok;
Expand All @@ -88,7 +111,7 @@ validate0(devaddr, DevAddr) ->
{error, {unknown_devaddr, lorawan_utils:binary_to_hex(DevAddr)}}
end
end;
validate0(_Else, _) ->
validate_key(_Else, _) ->
ok.

websocket_init(#state{conn=#connector{connid=Id, app=App}, bindings=Bindings} = State) ->
Expand Down

0 comments on commit cf097d0

Please sign in to comment.