Skip to content

Commit

Permalink
match relays with a regular expression
Browse files Browse the repository at this point in the history
during making decision where to send a request.

From this moment, proxy configuration contains not just a realm/prefix
string, but regular expression.

For example:

{routes,
  [{"^test-",  {{127, 0, 0, 1}, 1812, <<"secret1">>}},
   {"*_test$", {{127, 0, 0, 2}, 1813, <<"secret2">>}}]}

Requests where realm will be started from `test-`, will
be routed to the first 127.0.0.1:1812 relay.

Requests where realm will be ends with `_test`, will be
routed to the second 127.0.0.1:1813 relay.
  • Loading branch information
0xAX authored and surik committed Oct 4, 2017
1 parent 42a930e commit 326e697
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 9 deletions.
3 changes: 2 additions & 1 deletion src/eradius_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ validate_behavior({Module, _, _}) ->
validate_behavior(Term) ->
?invalid("bad Term in Behavior specifification: ~p", [Term]).

validate_arguments({Module, _Nas, Args} = Value) ->
validate_arguments({Module, Nas, Args} = Value) ->
case Module:validate_arguments(Args) of
true -> Value;
{true, NewArgs} -> {Module, Nas, NewArgs};
false -> ?invalid("~p: bad configuration", [Module]);
Error -> ?invalid("~p: bad configuration: ~p", [Module, Error])
end.
Expand Down
56 changes: 50 additions & 6 deletions src/eradius_proxy.erl
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
%% @doc
%% This module implements a RADIUS proxy.
%%
%% It accepts following configuration:
%%
%% ```
%% [{default_route, {{127, 0, 0, 1}, 1813, <<"secret">>}},
%% {options, [{type, realm}, {strip, true}, {separator, "@"}]},
%% {routes, [{"^test-[0-9].", {{127, 0, 0, 1}, 1815, <<"secret1">>}}]}],
%% ```
%%
%% == WARNING ==
%%
%% Define `routes` carefully. The `test` here in example above, is
%% a regular expression that may cause to problemts with performance.
-module(eradius_proxy).

-behaviour(eradius_server).
Expand Down Expand Up @@ -31,12 +46,34 @@ radius_request(Request, _NasProp, Args) ->
send_to_server(new_request(Request, Username, NewUsername), Route).

validate_arguments(Args) ->
Route = proplists:get_value(default_route, Args, {undefined, 0, []}),
DefaultRoute = proplists:get_value(default_route, Args, {undefined, 0, []}),
Options = proplists:get_value(options, Args, ?DEFAULT_OPTIONS),
case {validate_route(Route), validate_options(Options)} of
{false, _} -> default_route;
{_, false} -> options;
_ -> true
Routes = proplists:get_value(routes, Args, []),
case {validate_route(DefaultRoute), validate_options(Options), compile_routes(Routes)} of
{false, _, _} -> default_route;
{_, false, _} -> options;
{_, _, false} -> routes;
{_, _, NewRoutes} ->
{true, [{default_route, DefaultRoute}, {options, Options}, {routes, NewRoutes}]}
end.

compile_routes(Routes) ->
RoutesOpts = lists:map(fun ({Name, Relay}) ->
case re:compile(Name) of
{ok, R} ->
case validate_route(Relay) of
false -> false;
_ -> {R, Relay}
end;
{error, {Error, Position}} ->
throw("Error during regexp compilation - " ++ Error ++ " at position " ++ integer_to_list(Position))
end
end, Routes),
RelaysRegexps = lists:any(fun(Route) -> Route == false end, RoutesOpts),
if RelaysRegexps == false ->
RoutesOpts;
true ->
false
end.

% @private
Expand Down Expand Up @@ -115,13 +152,20 @@ resolve_routes(Username, {_, _, DefaultSecret} = DefaultRoute, Routes, Options)
{not_found, NewUsername} ->
{NewUsername, DefaultRoute};
{Key, NewUsername} ->
case lists:keyfind(Key, 1, Routes) of
case find_suitable_relay(Key, Routes) of
{Key, {_IP, _Port, _Secret} = Route} -> {NewUsername, Route};
{Key, {IP, Port}} -> {NewUsername, {IP, Port, DefaultSecret}};
_ -> {NewUsername, DefaultRoute}
end
end.

find_suitable_relay(_Key, []) -> [];
find_suitable_relay(Key, [{Regexp, Relay} | Routes]) ->
case re:run(Key, Regexp, [{capture, none}]) of
nomatch -> find_suitable_relay(Key, Routes);
_ -> {Key, Relay}
end.

% @private
-spec get_key(Username :: binary() | string() | [], Type :: atom(), Strip :: boolean(), Separator :: list()) ->
{Key :: not_found | string(), NewUsername :: string()}.
Expand Down
19 changes: 17 additions & 2 deletions test/eradius_proxy_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ resolve_routes_test(_) ->
DefaultRoute = {{127, 0, 0, 1}, 1813, <<"secret">>},
Prod = {{127, 0, 0, 1}, 1812, <<"prod">>},
Test = {{127, 0, 0, 1}, 11813, <<"test">>},
Routes = [{"prod", Prod}, {"test", Test}],
Dev = {{127, 0, 0, 1}, 11814, <<"dev">>},
{ok, R1} = re:compile("prod"),
{ok, R2} = re:compile("test"),
{ok, R3} = re:compile("^dev_.*"),
Routes = [{R1, Prod}, {R2, Test}, {R3, Dev}],
% default
?equal({undefined, DefaultRoute}, eradius_proxy:resolve_routes(undefined, DefaultRoute, Routes,[])),
?equal({"user", DefaultRoute}, eradius_proxy:resolve_routes(<<"user">>, DefaultRoute, Routes, [])),
Expand All @@ -48,6 +52,9 @@ resolve_routes_test(_) ->
?equal({"user", DefaultRoute}, eradius_proxy:resolve_routes(<<"user">>, DefaultRoute, Routes, Opts)),
?equal({"user", Prod}, eradius_proxy:resolve_routes(<<"user@prod">>, DefaultRoute, Routes, Opts)),
?equal({"user", Test}, eradius_proxy:resolve_routes(<<"user@test">>, DefaultRoute, Routes, Opts)),
?equal({"user", Dev}, eradius_proxy:resolve_routes(<<"user@dev_server">>, DefaultRoute, Routes, Opts)),
?equal({"user", DefaultRoute}, eradius_proxy:resolve_routes(<<"user@dev-server">>, DefaultRoute, Routes, Opts)),

% prefix
Opts1 = [{type, prefix}, {separator, "/"}],
?equal({"user/example", DefaultRoute}, eradius_proxy:resolve_routes(<<"user/example">>, DefaultRoute, Routes, Opts1)),
Expand All @@ -69,11 +76,19 @@ validate_arguments_test(_) ->
],
BadConfig1 = [{default_route, {{127, 0, 0, 1}, 0, <<"secret">>}}],
BadConfig2 = [{default_route, {abc, 123, <<"secret">>}}],
?equal(true, eradius_proxy:validate_arguments(GoodConfig)),
BadConfig3 = [{default_route, {{127, 0, 0, 1}, 1813, <<"secret">>}},
{options, [{type, realm}, {strip, true}, {separator, "@"}]},
{routes, [{"test", {wrong_ip, 1815, <<"secret1">>}}]}],
{Result, ConfigData} = eradius_proxy:validate_arguments(GoodConfig),
?equal(true, Result),
{routes, Routes} = lists:keyfind(routes, 1, ConfigData),
[{{CompiledRegexp, _, _, _, _}, _}] = Routes,
?equal(re_pattern, CompiledRegexp),
?equal(default_route, eradius_proxy:validate_arguments([])),
?equal(options, eradius_proxy:validate_arguments(BadConfig)),
?equal(default_route, eradius_proxy:validate_arguments(BadConfig1)),
?equal(default_route, eradius_proxy:validate_arguments(BadConfig2)),
?equal(routes, eradius_proxy:validate_arguments(BadConfig3)),
ok.

validate_options_test(_) ->
Expand Down

0 comments on commit 326e697

Please sign in to comment.