Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Riak base #378

Merged
merged 28 commits into from
Aug 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d1e82fe
add riak client as a dep
michalwski Feb 10, 2015
7bbf351
use cuesport from esl's branch
michalwski Feb 10, 2015
af323b6
base riak pool
michalwski Feb 10, 2015
1740f1c
add abstraction over riak_pb_socket API
michalwski Feb 10, 2015
020ef6d
remove notification address from .travis.yml
michalwski Feb 10, 2015
2f87741
do not auto import put/2
michalwski Feb 10, 2015
9f1233a
compile riakc in prehook
michalwski Feb 11, 2015
b15f903
test riak on travis
michalwski Feb 11, 2015
3db064f
test mysql on travis
michalwski Feb 12, 2015
c519c29
simplify setup_riak script
michalwski Feb 12, 2015
6b1217f
add more helpers to mongoose_riak module
michalwski Feb 12, 2015
1e77e66
add basic riak auth backend
michalwski Feb 12, 2015
c7a9258
add riak_config to ejabberd.cfg file
michalwski Feb 12, 2015
1df679a
add more helpers to mongoose_riak
michalwski Feb 12, 2015
a387754
implement some not implemented functions for riak auth
michalwski Feb 12, 2015
1e43b8c
restore ldap and external presets on travis
michalwski Feb 13, 2015
7faba25
do not prepare username and server in auth_riak
michalwski Feb 13, 2015
f829b05
implement more functions defined by the gen_auth behaviour
michalwski Feb 13, 2015
66f1888
restore other jobs on travis
michalwski Feb 16, 2015
11e3f15
add possibility to specify more than on riak pool
michalwski Feb 16, 2015
1cd0337
ignore riak_pools_count local config option during conf reload
michalwski Feb 16, 2015
f226072
keep pools count in riak_sup protected ets table
michalwski Feb 17, 2015
5153fdd
rename config options and use one riak pool
Aug 12, 2015
5c9f2dc
[skip ci] describe how to setup riak and other databases
Aug 13, 2015
d0ad70a
try to override riak config
Aug 13, 2015
3d0f5ff
make riak pool reloadable
Aug 13, 2015
ed05733
[skip ci] typo in docs
Aug 13, 2015
4e1e861
set auto_reconnect and keepalive options
Aug 17, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ after_script:
after_success:
- make cover_report

services: redis-server
services:
- redis-server
branches:
only:
- master
notifications:
email: mongoose-im@erlang-solutions.com

otp_release:
- R16B03
env:
Expand All @@ -37,6 +37,7 @@ env:
- PRESET=odbc_pgsql_mnesia DB=pgsql REL_CONFIG=with-odbc
- PRESET=pgsql_mnesia DB=pgsql REL_CONFIG=with-pgsql
- PRESET=ldap_mnesia DB=mnesia
- PRESET=riak_mnesia DB=riak REL_CONFIG=with-riak
- PRESET=external_mnesia DB=mnesia

matrix:
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ with-mysql include mysql driver
with-pgsql include pgsql driver
with-odbc include standard ODBC driver shipped with Erlang/OTP
with-redis include redis driver
with-riak include riak driver
with-cassandra include cassandra driver
full include all above deps
```
Expand All @@ -135,6 +136,9 @@ full include all above deps

The `make configure` command has to be run only once (unless one need to change the relase config and include some other dependecies).

Take a look [here](http://mongooseim.readthedocs.org/en/latest/advanced-configuration/database-backends-configuration/)
for instructions how to setup the external databases.

`make rel` or `./rebar generate` commands will generate a self-contained OTP system image in the
project's `rel/mongooseim` subdirectory. The contents of that directory are as
follows:
Expand Down
1 change: 1 addition & 0 deletions apps/ejabberd/src/ejabberd_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ start(normal, _Args) ->
mongoose_metrics:init(),
ejabberd_system_monitor:add_handler(),
ejabberd_rdbms:start(),
mongoose_riak:start(),
ejabberd_auth:start(),
cyrsasl:start(),
%% Profiling
Expand Down
5 changes: 3 additions & 2 deletions apps/ejabberd/src/ejabberd_auth_http.erl
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ start(Host) ->
PoolSize = proplists:get_value(connection_pool_size, AuthOpts, 10),
Opts = proplists:get_value(connection_opts, AuthOpts, []),
ChildMods = [fusco],
ChildMFA = {fusco, start_link, [AuthHost, Opts]},
ChildMF = {fusco, start_link},
ChildArgs = {for_all, [AuthHost, Opts]},

{ok, _} = supervisor:start_child(ejabberd_sup,
{{ejabberd_auth_http_sup, Host},
{cuesport, start_link,
[pool_name(Host), PoolSize, ChildMods, ChildMFA]},
[pool_name(Host), PoolSize, ChildMods, ChildMF, ChildArgs]},
transient, 2000, supervisor, [cuesport | ChildMods]}),
ok.

Expand Down
269 changes: 269 additions & 0 deletions apps/ejabberd/src/ejabberd_auth_riak.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
%%==============================================================================
%% Copyright 2015 Erlang Solutions Ltd.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%==============================================================================
-module(ejabberd_auth_riak).

-behaviour(ejabberd_gen_auth).

-include("ejabberd.hrl").

%% API
-export([start/1,
stop/1,
store_type/1,
login/2,
set_password/3,
check_password/3,
check_password/5,
try_register/3,
dirty_get_registered_users/0,
get_vh_registered_users/1,
get_vh_registered_users/2,
get_vh_registered_users_number/1,
get_vh_registered_users_number/2,
get_password/2,
get_password_s/2,
get_password/3,
does_user_exist/2,
remove_user/2,
remove_user/3,
plain_password_required/0]).

-export([bucket_type/1]).

-spec start(ejabberd:lserver()) -> ok.
start(_Host) ->
ok.

-spec stop(ejabberd:lserver()) -> ok.
stop(_Host) ->
ok.

-spec store_type(ejabberd:lserver()) -> plain | scram.
store_type(Host) ->
case scram:enabled(Host) of
false -> plain;
true -> scram
end.

-spec set_password(ejabberd:luser(),ejabberd:lserver(), binary())
-> ok | {error, not_allowed | invalid_jid}.
set_password(LUser, LServer, Password) ->
case prepare_password(LServer, Password) of
false ->
{error, invalid_password};
Password ->
User = mongoose_riak:fetch_type(bucket_type(LServer), LUser),
do_set_password(User, LUser, LServer, Password)
end.

-spec check_password(ejabberd:luser(), ejabberd:lserver(), binary()) -> boolean().
check_password(LUser, LServer, Password) ->
case do_get_password(LUser, LServer) of
false ->
false;
#scram{} = Scram ->
scram:check_password(Password, Scram);
Password when is_binary(Password) ->
Password /= <<"">>;
_ ->
false
end.

-spec check_password(ejabberd:luser(),
ejabberd:lserver(),
binary(),
binary(),
fun()) -> boolean().
check_password(LUser, LServer, Password, Digest, DigestGen) ->
case do_get_password(LUser, LServer) of
false ->
false;
#scram{} = Scram ->
scram:check_digest(Scram, Digest, DigestGen, Password);
PassRiak when is_binary(PassRiak) ->
ejabberd_auth:check_digest(Digest, DigestGen, Password, PassRiak)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check_password/3 function has _ clause. Do we need the same clause here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't, it's covered in do_get_password/2.

end.

try_register(LUser, LServer, Password) ->
try_register_if_does_not_exist(LUser, LServer, Password).

-spec dirty_get_registered_users() -> [ejabberd:simple_jid()].
dirty_get_registered_users() ->
Servers = ejabberd_config:get_vh_by_auth_method(riak),
lists:flatmap(
fun(Server) ->
get_vh_registered_users(Server)
end, Servers).

-spec get_vh_registered_users(ejabberd:lserver()) ->
[ejabberd:simple_jid()].
get_vh_registered_users(LServer) ->
case mongoose_riak:list_keys(bucket_type(LServer)) of
{ok, Users} ->
[{User, LServer} || User <- Users];
_ ->
[]
end.

-spec get_vh_registered_users(ejabberd:lserver(), list()) ->
[ejabberd:simple_jid()].
get_vh_registered_users(LServer, _Opts) ->
get_vh_registered_users(LServer).

-spec get_vh_registered_users_number(ejabberd:lserver()) -> non_neg_integer().
get_vh_registered_users_number(LServer) ->
length(get_vh_registered_users(LServer)).

-spec get_vh_registered_users_number(ejabberd:lserver(), list()) -> non_neg_integer().
get_vh_registered_users_number(LServer, _Opts) ->
get_vh_registered_users_number(LServer).

-spec get_password(ejabberd:luser(), ejabberd:lserver()) -> binary() | false | scram().
get_password(LUser, LServer) ->
case do_get_password(LUser, LServer) of
false ->
false;
#scram{} = Scram ->
scram:scram_to_tuple(Scram);
Password ->
Password
end.

get_password_s(LUser, LServer) ->
case get_password(LUser, LServer) of
Password when is_binary(Password) ->
Password;
_ ->
<<"">>
end.

get_password(_LUser, _LServer, _DefaultValue) ->
erlang:error(not_implemented).

-spec does_user_exist(ejabberd:luser(), ejabberd:lserver()) -> boolean().
does_user_exist(LUser, LServer) ->
case mongoose_riak:fetch_type(bucket_type(LServer), LUser) of
{ok, _} ->
true;
{error, {notfound, map}} ->
false
end.

-spec remove_user(ejabberd:luser(), ejabberd:lserver()) ->
ok | {error, term()}.
remove_user(LUser, LServer) ->
mongoose_riak:delete(bucket_type(LServer), LUser).

remove_user(_LUser, _LServer, _Password) ->
erlang:error(not_implemented).

plain_password_required() ->
false.

login(_LUser, _LServer) ->
erlang:error(not_implemented).

-spec bucket_type(ejabberd:lserver()) -> {binary(), ejabberd:lserver()}.
bucket_type(LServer) ->
{<<"users">>, LServer}.

%% -----------------------------------------------------------------------------
%% Internal functions
%% -----------------------------------------------------------------------------

try_register_if_does_not_exist(LUser, LServer, _)
when LUser =:= error; LServer =:= error ->
{error, invalid_jid};
try_register_if_does_not_exist(LUser, LServer, PasswordIn) ->
case does_user_exist(LUser, LServer) of
false ->
Password = prepare_password(LServer, PasswordIn),
try_register_with_password(LUser, LServer, Password);
true ->
{error, exists}
end.

try_register_with_password(LUser, LServer, Password) ->
Now = integer_to_binary(now_to_seconds(os:timestamp())),
Ops = [{{<<"created">>, register},
fun(R) -> riakc_register:set(Now, R) end},
set_password_map_op(Password)],
UserMap = mongoose_riak:create_new_map(Ops),
case mongoose_riak:update_type(bucket_type(LServer), LUser,
riakc_map:to_op(UserMap)) of
{ok, _Map} ->
ok;
Error ->
Error
end.

do_get_password(LUser, LServer) ->
case mongoose_riak:fetch_type(bucket_type(LServer), LUser) of
{ok, Map} ->
extract_password(Map);
_ ->
false
end.

do_set_password({ok, Map}, LUser, LServer, Password) ->
Ops = [set_password_map_op(Password)],
UpdateMap = mongoose_riak:update_map(Map, Ops),
case mongoose_riak:update_type(bucket_type(LServer), LUser,
riakc_map:to_op(UpdateMap)) of
ok ->
ok;
Reason ->
Reason
end.

prepare_password(Iterations, Password) when is_integer(Iterations) ->
Scram = scram:password_to_scram(Password, Iterations),
PassDetails = scram:serialize(Scram),
{<<"">>, PassDetails};

prepare_password(Server, Password) ->
case scram:enabled(Server) of
true ->
prepare_password(scram:iterations(Server), Password);
_ ->
Password
end.

set_password_map_op({_, Scram}) ->
{{<<"scram">>, register}, fun(R) -> riakc_register:set(Scram, R) end};
set_password_map_op(Password) ->
{{<<"password">>, register}, fun(R) -> riakc_register:set(Password, R) end}.

extract_password(Map) ->
case riakc_map:find({<<"password">>, register}, Map) of
error ->
maybe_extract_scram_password(riakc_map:find({<<"scram">>, register}, Map));
{ok, Password} ->
Password
end.

maybe_extract_scram_password(false) ->
false;
maybe_extract_scram_password({ok, ScramSerialised}) ->
case scram:deserialize(ScramSerialised) of
{ok, Scram} ->
Scram;
_ ->
false
end.

now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
MegaSecs * 1000000 + Secs.
14 changes: 12 additions & 2 deletions apps/ejabberd/src/ejabberd_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ process_host_term(Term, Host, State) ->
State;
{odbc_server, ODBC_server} ->
add_option({odbc_server, Host}, ODBC_server, State);
{riak_server, RiakConfig} ->
add_option(riak_server, RiakConfig, State);
{Opt, Val} ->
add_option({Opt, Host}, Val, State)
end.
Expand Down Expand Up @@ -1000,18 +1002,22 @@ handle_config_change({_Key, _OldValue, _NewValue}) ->
%% ----------------------------------------------------------------
%% LOCAL CONFIG
%% ----------------------------------------------------------------
handle_local_config_add({Key, _Data} = El) ->
handle_local_config_add(#local_config{key = riak_server}) ->
mongoose_riak:start();
handle_local_config_add(#local_config{key=Key} = El) ->
case Key of
true ->
ok;
false ->
?WARNING_MSG("local config add ~p option unhandled",[El])
end.

handle_local_config_del(#local_config{key = riak_server}) ->
mongoose_riak:stop();
handle_local_config_del(#local_config{key = node_start}) ->
%% do nothing with it
ok;
handle_local_config_del({Key, _Data} = El) ->
handle_local_config_del(#local_config{key=Key} = El) ->
case can_be_ignored(Key) of
true ->
ok;
Expand All @@ -1021,6 +1027,10 @@ handle_local_config_del({Key, _Data} = El) ->

handle_local_config_change({listen, Old, New}) ->
reload_listeners(compare_listeners(Old, New));
handle_local_config_change({riak_server, _Old, _New}) ->
mongoose_riak:stop(),
mongoose_riak:start(),
ok;

handle_local_config_change({Key, _Old, _New} = El) ->
case can_be_ignored(Key) of
Expand Down
5 changes: 3 additions & 2 deletions apps/ejabberd/src/ejabberd_redis.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ start_link(Opts) ->
PoolSize = proplists:get_value(pool_size, Opts, 10),
RedoOpts = proplists:get_value(worker_config, Opts, []),
ChildMods = [redo, redo_redis_proto, redo_uri],
ChildMFA = {redo, start_link, [undefined, RedoOpts]},
ChildMF = {redo, start_link},
ChildArgs = {for_all, [undefined, RedoOpts]},

supervisor:start_child(ejabberd_sm_backend_sup,
{ejabberd_redis_sup,
{cuesport, start_link,
[?POOL_NAME, PoolSize, ChildMods, ChildMFA]},
[?POOL_NAME, PoolSize, ChildMods, ChildMF, ChildArgs]},
transient, 2000, supervisor, [cuesport | ChildMods]}).

-spec cmd(iolist()) -> binary()
Expand Down
Loading