./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 164 #section{
56 items = #{<<"bucket_type">> => #option{type = binary,
57 validate = non_empty}},
58 defaults = #{<<"bucket_type">> => <<"users">>},
59 format_items = map
60 }.
61
62 -spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean().
63
:-(
supports_sasl_module(_HostType, cyrsasl_plain) -> true;
64
:-(
supports_sasl_module(HostType, cyrsasl_digest) -> not mongoose_scram:enabled(HostType);
65
:-(
supports_sasl_module(HostType, Mechanism) -> mongoose_scram:enabled(HostType, Mechanism).
66
67 -spec supported_features() -> [atom()].
68
:-(
supported_features() -> [dynamic_domains].
69
70 -spec set_password(mongooseim:host_type(), jid:luser(), jid:lserver(), binary())
71 -> ok | {error, not_allowed | invalid_jid}.
72 set_password(HostType, LUser, LServer, Password) ->
73
:-(
case prepare_password(HostType, Password) of
74 false ->
75
:-(
{error, invalid_password};
76 Password ->
77
:-(
User = mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser),
78
:-(
do_set_password(User, HostType, LUser, LServer, Password);
79 {<<>>, Scram} ->
80
:-(
User = mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser),
81
:-(
do_set_password(User, HostType, LUser, LServer, {<<>>, Scram})
82 end.
83
84 -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()}
85 | {error, any()}.
86 authorize(Creds) ->
87
:-(
ejabberd_auth:authorize_with_check_password(?MODULE, Creds).
88
89 -spec check_password(mongooseim:host_type(), jid:luser(), jid:lserver(), binary()) -> boolean().
90 check_password(HostType, LUser, LServer, Password) ->
91
:-(
case do_get_password(HostType, LUser, LServer) of
92 false ->
93
:-(
false;
94 Scram when is_record(Scram, scram) orelse is_map(Scram)->
95
:-(
mongoose_scram:check_password(Password, Scram);
96 Password when is_binary(Password) ->
97
:-(
Password /= <<"">>;
98 _ ->
99
:-(
false
100 end.
101
102 -spec check_password(mongooseim:host_type(),
103 jid:luser(),
104 jid:lserver(),
105 binary(),
106 binary(),
107 fun()) -> boolean().
108 check_password(HostType, LUser, LServer, Password, Digest, DigestGen) ->
109
:-(
case do_get_password(HostType, LUser, LServer) of
110 false ->
111
:-(
false;
112 Scram when is_record(Scram, scram) orelse is_map(Scram) ->
113
:-(
mongoose_scram:check_digest(Scram, Digest, DigestGen, Password);
114 PassRiak when is_binary(PassRiak) ->
115
:-(
ejabberd_auth:check_digest(Digest, DigestGen, Password, PassRiak)
116 end.
117
118 -spec try_register(HostType :: mongooseim:host_type(),
119 User :: jid:luser(),
120 Server :: jid:lserver(),
121 Password :: binary()
122 ) -> ok | {error, term()}.
123 try_register(HostType, LUser, LServer, Password) ->
124
:-(
try_register_if_does_not_exist(HostType, LUser, LServer, Password).
125
126 -spec get_registered_users(mongooseim:host_type(), jid:lserver(), list()) ->
127 [jid:simple_bare_jid()].
128 get_registered_users(HostType, LServer, _Opts) ->
129
:-(
case mongoose_riak:list_keys(bucket_type(HostType, LServer)) of
130 {ok, Users} ->
131
:-(
[{User, LServer} || User <- Users];
132 _ ->
133
:-(
[]
134 end.
135
136 -spec get_registered_users_number(mongooseim:host_type(), jid:lserver(), list()) ->
137 non_neg_integer().
138 get_registered_users_number(HostType, LServer, Opts) ->
139
:-(
length(get_registered_users(HostType, LServer, Opts)).
140
141 -spec get_password(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
142 ejabberd_auth:passterm() | false.
143 get_password(HostType, LUser, LServer) ->
144
:-(
case do_get_password(HostType, LUser, LServer) of
145 false ->
146
:-(
false;
147 Scram when is_map(Scram) ->
148
:-(
Scram;
149 #scram{} = Scram ->
150
:-(
mongoose_scram:scram_record_to_map(Scram);
151 Password ->
152
:-(
Password
153 end.
154
155 -spec get_password_s(mongooseim:host_type(), jid:luser(), jid:lserver()) -> binary().
156 get_password_s(HostType, LUser, LServer) ->
157
:-(
case get_password(HostType, LUser, LServer) of
158 Password when is_binary(Password) ->
159
:-(
Password;
160 _ ->
161
:-(
<<"">>
162 end.
163
164 -spec does_user_exist(mongooseim:host_type(), jid:luser(), jid:lserver()) -> boolean().
165 does_user_exist(HostType, LUser, LServer) ->
166
:-(
case mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser) of
167 {ok, _} ->
168
:-(
true;
169 {error, {notfound, map}} ->
170
:-(
false
171 end.
172
173 -spec remove_user(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
174 ok | {error, not_allowed}.
175 remove_user(HostType, LUser, LServer) ->
176
:-(
case mongoose_riak:delete(bucket_type(HostType, LServer), LUser) of
177
:-(
ok -> ok;
178 Error ->
179
:-(
?LOG_WARNING(#{what => remove_user_failed, reason => Error,
180
:-(
user => LUser, server => LServer}),
181
:-(
{error, not_allowed}
182 end.
183
184 -spec bucket_type(mongooseim:host_type(), jid:lserver()) -> {binary(), jid:lserver()}.
185 bucket_type(HostType, LServer) ->
186
:-(
BucketType = mongoose_config:get_opt([{auth, HostType}, riak, bucket_type]),
187
:-(
{BucketType, LServer}.
188
189 %% -----------------------------------------------------------------------------
190 %% Internal functions
191 %% -----------------------------------------------------------------------------
192
193 try_register_if_does_not_exist(_, LUser, LServer, _)
194 when LUser =:= error; LServer =:= error ->
195
:-(
{error, invalid_jid};
196 try_register_if_does_not_exist(HostType, LUser, LServer, PasswordIn) ->
197
:-(
case does_user_exist(HostType, LUser, LServer) of
198 false ->
199
:-(
Password = prepare_password(HostType, PasswordIn),
200
:-(
try_register_with_password(HostType, LUser, LServer, Password);
201 true ->
202
:-(
{error, exists}
203 end.
204
205 try_register_with_password(HostType, LUser, LServer, Password) ->
206
:-(
Now = integer_to_binary(os:system_time(second)),
207
:-(
Ops = [{{<<"created">>, register},
208
:-(
fun(R) -> riakc_register:set(Now, R) end},
209 set_password_map_op(Password)],
210
:-(
UserMap = mongoose_riak:create_new_map(Ops),
211
:-(
mongoose_riak:update_type(bucket_type(HostType, LServer), LUser, riakc_map:to_op(UserMap)).
212
213 do_get_password(HostType, LUser, LServer) ->
214
:-(
case mongoose_riak:fetch_type(bucket_type(HostType, LServer), LUser) of
215 {ok, Map} ->
216
:-(
case extract_password(Map) of
217 false ->
218
:-(
?LOG_WARNING(#{what => scram_serialisation_incorrect,
219
:-(
user => LUser, server => LServer});
220
:-(
Pwd -> Pwd
221 end;
222 _ ->
223
:-(
false
224 end.
225
226 do_set_password({ok, Map}, HostType, LUser, LServer, Password) ->
227
:-(
Ops = [set_password_map_op(Password)],
228
:-(
UpdateMap = mongoose_riak:update_map(Map, Ops),
229
:-(
mongoose_riak:update_type(bucket_type(HostType, LServer), LUser, riakc_map:to_op(UpdateMap)).
230
231 prepare_password(HostType, Iterations, Password) when is_integer(Iterations) ->
232
:-(
Scram = mongoose_scram:password_to_scram(HostType, Password, Iterations),
233
:-(
PassDetails = mongoose_scram:serialize(Scram),
234
:-(
{<<"">>, PassDetails}.
235
236 prepare_password(HostType, Password) ->
237
:-(
case mongoose_scram:enabled(HostType) of
238 true ->
239
:-(
prepare_password(HostType, mongoose_scram:iterations(HostType), Password);
240 _ ->
241
:-(
Password
242 end.
243
244 set_password_map_op({_, Scram}) ->
245
:-(
{{<<"scram">>, register}, fun(R) -> riakc_register:set(Scram, R) end};
246 set_password_map_op(Password) ->
247
:-(
{{<<"password">>, register}, fun(R) -> riakc_register:set(Password, R) end}.
248
249 extract_password(Map) ->
250
:-(
case riakc_map:find({<<"password">>, register}, Map) of
251 error ->
252
:-(
maybe_extract_scram_password(riakc_map:find({<<"scram">>, register}, Map));
253 {ok, Password} ->
254
:-(
Password
255 end.
256
257 -spec maybe_extract_scram_password({ok, binary()} | error) -> mongoose_scram:scram() | false.
258 maybe_extract_scram_password({ok, ScramSerialised}) ->
259
:-(
case mongoose_scram:deserialize(ScramSerialised) of
260 {ok, Scram} ->
261
:-(
Scram;
262 _ ->
263
:-(
false
264 end;
265 maybe_extract_scram_password(_) ->
266
:-(
false.
Line Hits Source