Skip to content

Commit

Permalink
GDPR retrieve roster (clean) (#2286)
Browse files Browse the repository at this point in the history
* Implement personal data retrieval for mod_roster

* Enable GDPR retrieve roster test

* Disable all big tests except for GDPR

* Return roster personal data in new format

* Re-enable all tests

* Retrieve personal data from all roster backends
  • Loading branch information
fenek authored May 10, 2019
1 parent de52634 commit f1f4cc0
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 5 deletions.
12 changes: 8 additions & 4 deletions big_tests/tests/gdpr_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ groups() ->
[
{retrieve_personal_data, [], [
retrieve_vcard,
%retrieve_roster,
retrieve_roster,
%retrieve_mam,
%retrieve_offline,
%retrieve_private_xml,
Expand All @@ -72,6 +72,7 @@ groups() ->
{retrieve_personal_data_with_mods_disabled, [], [
retrieve_vcard,
retrieve_logs,
retrieve_roster,
retrieve_all_pubsub_data
]},
{retrieve_negative, [], [
Expand Down Expand Up @@ -188,13 +189,16 @@ retrieve_vcard(Config) ->
retrieve_roster(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
escalus_story:make_all_clients_friends([Alice, Bob]),
ExpectedHeader = ["jid", "name", "groups"], % TODO
BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)),
BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)),
ExpectedHeader = ["jid", "name", "subscription",
"ask", "groups", "askmessage", "xs"],
ExpectedItems = [
#{ "jid" => escalus_client:short_jid(Bob) }
#{ "jid" => [{contains, BobU}, {contains, BobS}] }
],
maybe_stop_and_unload_module(mod_roster, mod_roster_backend, Config),
retrieve_and_validate_personal_data(
Alice, Config, "roster", ExpectedHeader, ExpectedItems)
Alice, Config, "roster", ExpectedHeader, ExpectedItems)
end).

retrieve_mam(_Config) ->
Expand Down
7 changes: 6 additions & 1 deletion src/admin_extra/service_admin_extra_gdpr.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-module(service_admin_extra_gdpr).

-include("ejabberd_commands.hrl").
-include("mongoose_logger.hrl").

-export([commands/0,
retrieve_all/3]).
Expand Down Expand Up @@ -69,7 +70,11 @@ try_get_data_from_module(Module, Username, Domain) ->
[{_, _, []}] -> [];
Val -> Val
catch
_:_ -> []
C:R ->
?WARNING_MSG("event=cannot_retrieve_personal_data,"
"module=~p,class=~p,reason=~p,stacktrace=~p",
[Module, C, R, erlang:get_stacktrace()]),
[]
end.

-spec to_csv_file(CsvFilename :: binary(), gdpr:schema(), gdpr:entities(), file:name()) -> ok.
Expand Down
47 changes: 47 additions & 0 deletions src/mod_roster.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
-xep([{xep, 83}, {version, "1.0"}]).
-xep([{xep, 93}, {version, "1.2"}]).
-behaviour(gen_mod).
-behaviour(gdpr).

-export([start/2,
stop/1,
Expand Down Expand Up @@ -68,6 +69,8 @@

-export([remove_test_user/2, transaction/2, process_subscription_transaction/6]). % for testing

-export([get_personal_data/2]).

-include("mongoose.hrl").
-include("jlib.hrl").
-include("mod_roster.hrl").
Expand Down Expand Up @@ -156,6 +159,50 @@
Item :: term(),
Result :: error | roster().

%%--------------------------------------------------------------------
%% gdpr callback
%%--------------------------------------------------------------------

-spec get_personal_data(jid:user(), jid:server()) ->
[{gdpr:data_group(), gdpr:schema(), gdpr:entries()}].
get_personal_data(Username, Server) ->
LUser = jid:nodeprep(Username),
LServer = jid:nameprep(Server),
Schema = ["jid", "name", "subscription", "ask", "groups", "askmessage", "xs"],
Records =
lists:flatmap(fun(B) ->
try B:get_roster(LUser, LServer) of
Entries when is_list(Entries) -> Entries;
_ -> []
catch
C:R ->
log_get_personal_data_warning(B, C, R, erlang:get_stacktrace()),
[]
end
end, mongoose_lib:find_behaviour_implementations(mod_roster)),
SerializedRecords = lists:map(fun roster_record_to_gdpr_entry/1, Records),
[{roster, Schema, SerializedRecords}].

roster_record_to_gdpr_entry(#roster{ jid = JID, name = Name,
subscription = Subscription, ask = Ask, groups = Groups,
askmessage = AskMessage, xs = XS }) ->
[
jid:to_binary(JID),
Name,
atom_to_binary(Subscription, utf8),
atom_to_binary(Ask, utf8),
string:join([ unicode:characters_to_list(G) || G <- Groups ], ", "),
AskMessage,
<< <<(exml:to_binary(X))>> || X <- XS >>
].

log_get_personal_data_warning(Backend, Class, Reason, StackTrace) ->
?WARNING_MSG("event=cannot_retrieve_personal_data,backend=~p,class=~p,reason=~p,stacktrace=~p",
[Backend, Class, Reason, StackTrace]).

%%--------------------------------------------------------------------
%% mod_roster's callbacks
%%--------------------------------------------------------------------

start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
Expand Down
1 change: 1 addition & 0 deletions src/mod_roster_mnesia.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

-include("mod_roster.hrl").
-include("jlib.hrl").

-behaviour(mod_roster).

%% API
Expand Down

0 comments on commit f1f4cc0

Please sign in to comment.