./ct_report/coverage/ejabberd_auth_riak.COVER.html

1 %%==============================================================================
2 %% Copyright 2015 Erlang Solutions Ltd.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%==============================================================================
16 -module(ejabberd_auth_riak).
17
18 -behaviour(mongoose_gen_auth).
19
20 -include("mongoose.hrl").
21 -include("mongoose_config_spec.hrl").
22 -include("scram.hrl").
23
24 %% API
25 -export([start/1,
26 stop/1,
27 config_spec/0,
28 supports_sasl_module/2,
29 supported_features/0,
30 set_password/4,
31 authorize/1,
32 try_register/4,
33 get_registered_users/3,
34 get_registered_users_number/3,
35 get_password/3,
36 get_password_s/3,
37 does_user_exist/3,
38 remove_user/3
39 ]).
40
41 %% Internal
42 -export([check_password/4,
43 check_password/6]).
44
45 -spec start(mongooseim:host_type()) -> ok.
46 start(_HostType) ->
47
:-(
ok.
48
49 -spec stop(mongooseim:host_type()) -> ok.
50 stop(_HostType) ->
51
:-(
ok.
52
53 -spec config_spec() -> mongoose_config_spec:config_section().
54 config_spec() ->
55 166 #section{
56 items = #{<<"bucket_type">> => #option{type = binary,
57 validate = non_empty}},
58 defaults = #{<<"bucket_type">> => <<"users">>}
59 }.
60
61 -spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean().
62
:-(
supports_sasl_module(_HostType, cyrsasl_plain) -> true;
63
:-(
supports_sasl_module(HostType, cyrsasl_digest) -> not mongoose_scram:enabled(HostType);
64
:-(
supports_sasl_module(HostType, Mechanism) -> mongoose_scram:enabled(HostType, Mechanism).
65
66 -spec supported_features() -> [atom()].
67
:-(
supported_features() -> [dynamic_domains].
68
69 -spec set_password(mongooseim:host_type(), jid:luser(), jid:lserver(), binary())
70 -> ok | {error, not_allowed | invalid_jid}.
71 set_password(HostType, LUser, LServer, Password) ->
72
:-(
case prepare_password(HostType, Password) of
73 false ->
74
:-(
{error, invalid_password};
75 Password ->
76
:-(
User = mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser),
77
:-(
do_set_password(User, HostType, LUser, LServer, Password);
78 {<<>>, Scram} ->
79
:-(
User = mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser),
80
:-(
do_set_password(User, HostType, LUser, LServer, {<<>>, Scram})
81 end.
82
83 -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()}
84 | {error, any()}.
85 authorize(Creds) ->
86
:-(
ejabberd_auth:authorize_with_check_password(?MODULE, Creds).
87
88 -spec check_password(mongooseim:host_type(), jid:luser(), jid:lserver(), binary()) -> boolean().
89 check_password(HostType, LUser, LServer, Password) ->
90
:-(
case do_get_password(HostType, LUser, LServer) of
91 false ->
92
:-(
false;
93 Scram when is_record(Scram, scram) orelse is_map(Scram)->
94
:-(
mongoose_scram:check_password(Password, Scram);
95 Password when is_binary(Password) ->
96
:-(
Password /= <<"">>;
97 _ ->
98
:-(
false
99 end.
100
101 -spec check_password(mongooseim:host_type(),
102 jid:luser(),
103 jid:lserver(),
104 binary(),
105 binary(),
106 fun()) -> boolean().
107 check_password(HostType, LUser, LServer, Password, Digest, DigestGen) ->
108
:-(
case do_get_password(HostType, LUser, LServer) of
109 false ->
110
:-(
false;
111 Scram when is_record(Scram, scram) orelse is_map(Scram) ->
112
:-(
mongoose_scram:check_digest(Scram, Digest, DigestGen, Password);
113 PassRiak when is_binary(PassRiak) ->
114
:-(
ejabberd_auth:check_digest(Digest, DigestGen, Password, PassRiak)
115 end.
116
117 -spec try_register(HostType :: mongooseim:host_type(),
118 User :: jid:luser(),
119 Server :: jid:lserver(),
120 Password :: binary()
121 ) -> ok | {error, term()}.
122 try_register(HostType, LUser, LServer, Password) ->
123
:-(
try_register_if_does_not_exist(HostType, LUser, LServer, Password).
124
125 -spec get_registered_users(mongooseim:host_type(), jid:lserver(), list()) ->
126 [jid:simple_bare_jid()].
127 get_registered_users(HostType, LServer, _Opts) ->
128
:-(
case mongoose_riak:list_keys(bucket_type(HostType, LServer)) of
129 {ok, Users} ->
130
:-(
[{User, LServer} || User <- Users];
131 _ ->
132
:-(
[]
133 end.
134
135 -spec get_registered_users_number(mongooseim:host_type(), jid:lserver(), list()) ->
136 non_neg_integer().
137 get_registered_users_number(HostType, LServer, Opts) ->
138
:-(
length(get_registered_users(HostType, LServer, Opts)).
139
140 -spec get_password(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
141 ejabberd_auth:passterm() | false.
142 get_password(HostType, LUser, LServer) ->
143
:-(
case do_get_password(HostType, LUser, LServer) of
144 false ->
145
:-(
false;
146 Scram when is_map(Scram) ->
147
:-(
Scram;
148 #scram{} = Scram ->
149
:-(
mongoose_scram:scram_record_to_map(Scram);
150 Password ->
151
:-(
Password
152 end.
153
154 -spec get_password_s(mongooseim:host_type(), jid:luser(), jid:lserver()) -> binary().
155 get_password_s(HostType, LUser, LServer) ->
156
:-(
case get_password(HostType, LUser, LServer) of
157 Password when is_binary(Password) ->
158
:-(
Password;
159 _ ->
160
:-(
<<"">>
161 end.
162
163 -spec does_user_exist(mongooseim:host_type(), jid:luser(), jid:lserver()) -> boolean().
164 does_user_exist(HostType, LUser, LServer) ->
165
:-(
case mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser) of
166 {ok, _} ->
167
:-(
true;
168 {error, {notfound, map}} ->
169
:-(
false
170 end.
171
172 -spec remove_user(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
173 ok | {error, not_allowed}.
174 remove_user(HostType, LUser, LServer) ->
175
:-(
case mongoose_riak:delete(bucket_type(HostType, LServer), LUser) of
176
:-(
ok -> ok;
177 Error ->
178
:-(
?LOG_WARNING(#{what => remove_user_failed, reason => Error,
179
:-(
user => LUser, server => LServer}),
180
:-(
{error, not_allowed}
181 end.
182
183 -spec bucket_type(mongooseim:host_type(), jid:lserver()) -> {binary(), jid:lserver()}.
184 bucket_type(HostType, LServer) ->
185
:-(
BucketType = mongoose_config:get_opt([{auth, HostType}, riak, bucket_type]),
186
:-(
{BucketType, LServer}.
187
188 %% -----------------------------------------------------------------------------
189 %% Internal functions
190 %% -----------------------------------------------------------------------------
191
192 try_register_if_does_not_exist(_, LUser, LServer, _)
193 when LUser =:= error; LServer =:= error ->
194
:-(
{error, invalid_jid};
195 try_register_if_does_not_exist(HostType, LUser, LServer, PasswordIn) ->
196
:-(
case does_user_exist(HostType, LUser, LServer) of
197 false ->
198
:-(
Password = prepare_password(HostType, PasswordIn),
199
:-(
try_register_with_password(HostType, LUser, LServer, Password);
200 true ->
201
:-(
{error, exists}
202 end.
203
204 try_register_with_password(HostType, LUser, LServer, Password) ->
205
:-(
Now = integer_to_binary(os:system_time(second)),
206
:-(
Ops = [{{<<"created">>, register},
207
:-(
fun(R) -> riakc_register:set(Now, R) end},
208 set_password_map_op(Password)],
209
:-(
UserMap = mongoose_riak:create_new_map(Ops),
210
:-(
mongoose_riak:update_type(bucket_type(HostType, LServer), LUser, riakc_map:to_op(UserMap)).
211
212 do_get_password(HostType, LUser, LServer) ->
213
:-(
case mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser) of
214 {ok, Map} ->
215
:-(
case extract_password(Map) of
216 false ->
217
:-(
?LOG_WARNING(#{what => scram_serialisation_incorrect,
218
:-(
user => LUser, server => LServer});
219
:-(
Pwd -> Pwd
220 end;
221 _ ->
222
:-(
false
223 end.
224
225 do_set_password({ok, Map}, HostType, LUser, LServer, Password) ->
226
:-(
Ops = [set_password_map_op(Password)],
227
:-(
UpdateMap = mongoose_riak:update_map(Map, Ops),
228
:-(
mongoose_riak:update_type(bucket_type(HostType, LServer), LUser, riakc_map:to_op(UpdateMap)).
229
230 prepare_password(HostType, Iterations, Password) when is_integer(Iterations) ->
231
:-(
Scram = mongoose_scram:password_to_scram(HostType, Password, Iterations),
232
:-(
PassDetails = mongoose_scram:serialize(Scram),
233
:-(
{<<"">>, PassDetails}.
234
235 prepare_password(HostType, Password) ->
236
:-(
case mongoose_scram:enabled(HostType) of
237 true ->
238
:-(
prepare_password(HostType, mongoose_scram:iterations(HostType), Password);
239 _ ->
240
:-(
Password
241 end.
242
243 set_password_map_op({_, Scram}) ->
244
:-(
{{<<"scram">>, register}, fun(R) -> riakc_register:set(Scram, R) end};
245 set_password_map_op(Password) ->
246
:-(
{{<<"password">>, register}, fun(R) -> riakc_register:set(Password, R) end}.
247
248 extract_password(Map) ->
249
:-(
case riakc_map:find({<<"password">>, register}, Map) of
250 error ->
251
:-(
maybe_extract_scram_password(riakc_map:find({<<"scram">>, register}, Map));
252 {ok, Password} ->
253
:-(
Password
254 end.
255
256 -spec maybe_extract_scram_password({ok, binary()} | error) -> mongoose_scram:scram() | false.
257 maybe_extract_scram_password({ok, ScramSerialised}) ->
258
:-(
case mongoose_scram:deserialize(ScramSerialised) of
259 {ok, Scram} ->
260
:-(
Scram;
261 _ ->
262
:-(
false
263 end;
264 maybe_extract_scram_password(_) ->
265
:-(
false.
Line Hits Source