Skip to content

Commit cf097d0

Browse files
committed
Issue #505: Optional basic authentication for web sockets
1 parent 5077442 commit cf097d0

File tree

2 files changed

+36
-10
lines changed

2 files changed

+36
-10
lines changed

doc/Connectors.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ To create a web socket connector you set:
113113
- **Publish Uplinks** to a URL pattern starting with a slash, e.g. '/ws/uplink/{devaddr}'
114114
- **Publish Events** to another URL pattern, e.g. '/ws/events/{devaddr}'
115115

116+
HTTP Basic authentication is supported. On the Authentication tab you may set the
117+
*Name* and *Password/Key* that shall be verified by the server.
118+
116119
The patterns may have any structure (doesn't have to start with `/ws`), but must
117120
be unique across the entire server, including the web-admin itself.
118121

src/lorawan_connector_ws.erl

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,44 +39,67 @@ stop_connector(Id) ->
3939
lorawan_http_registry:delete({ws, Id}).
4040

4141
init(Req, [#connector{connid=Id}=Connector, Type]) ->
42-
Bindings = lorawan_admin:parse(cowboy_req:bindings(Req)),
43-
case validate(maps:to_list(Bindings)) of
44-
ok ->
42+
case authorize(Req, Connector) of
43+
{ok, Bindings} ->
4544
{ok, Timeout} = application:get_env(lorawan_server, websocket_timeout),
4645
{cowboy_websocket, Req,
4746
#state{conn=Connector, type=Type, path=cowboy_req:path(Req), bindings=Bindings},
4847
#{idle_timeout => Timeout}};
48+
unauthorized ->
49+
lorawan_utils:throw_error({connector, Id}, unauthorized),
50+
Req2 = cowboy_req:reply(403, Req),
51+
{ok, Req2, undefined};
4952
{error, Error} ->
5053
lorawan_utils:throw_error({connector, Id}, Error),
5154
Req2 = cowboy_req:reply(404, Req),
5255
{ok, Req2, undefined}
5356
end.
5457

55-
validate([{Key, Value} | Other]) ->
56-
case validate0(Key, Value) of
58+
authorize(Req, #connector{name=User})
59+
when User == undefined; User == <<>> ->
60+
validate(Req);
61+
authorize(Req, #connector{name=User, pass=Pass}) ->
62+
case cowboy_req:parse_header(<<"authorization">>, Req) of
63+
{basic, User, Pass} ->
64+
validate(Req);
65+
_Else ->
66+
unauthorized
67+
end.
68+
69+
validate(Req) ->
70+
Bindings = lorawan_admin:parse(cowboy_req:bindings(Req)),
71+
case validate0(maps:to_list(Bindings)) of
72+
ok ->
73+
{ok, Bindings};
74+
Error ->
75+
Error
76+
end.
77+
78+
validate0([{Key, Value} | Other]) ->
79+
case validate_key(Key, Value) of
5780
ok ->
5881
validate(Other);
5982
Else ->
6083
Else
6184
end;
62-
validate([])->
85+
validate0([])->
6386
ok.
6487

65-
validate0(app, App) ->
88+
validate_key(app, App) ->
6689
case mnesia:dirty_read(handler, App) of
6790
[#handler{}] ->
6891
ok;
6992
_Else ->
7093
{error, {unknown_application, App}}
7194
end;
72-
validate0(deveui, DevEUI) ->
95+
validate_key(deveui, DevEUI) ->
7396
case mnesia:dirty_read(device, DevEUI) of
7497
[#device{}] ->
7598
ok;
7699
_Else ->
77100
{error, {unknown_deveui, lorawan_utils:binary_to_hex(DevEUI)}}
78101
end;
79-
validate0(devaddr, DevAddr) ->
102+
validate_key(devaddr, DevAddr) ->
80103
case mnesia:dirty_read(node, DevAddr) of
81104
[#node{}] ->
82105
ok;
@@ -88,7 +111,7 @@ validate0(devaddr, DevAddr) ->
88111
{error, {unknown_devaddr, lorawan_utils:binary_to_hex(DevAddr)}}
89112
end
90113
end;
91-
validate0(_Else, _) ->
114+
validate_key(_Else, _) ->
92115
ok.
93116

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

0 commit comments

Comments
 (0)