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

[4.3] KSTE-5: look up blacklist number using view #6724

Open
wants to merge 5 commits into
base: 4.3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
141 changes: 83 additions & 58 deletions applications/stepswitch/src/stepswitch_inbound.erl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%%%-----------------------------------------------------------------------------
%%% @copyright (C) 2011-2021, 2600Hz
%%% @copyright (C) 2011-2022, 2600Hz
%%% @doc Handle route requests from carrier resources
%%% @end
%%%-----------------------------------------------------------------------------
Expand Down Expand Up @@ -221,27 +221,28 @@ maybe_block_call(_, JObj) ->

-spec block_call_routines(kz_json:object()) -> boolean().
block_call_routines(JObj) ->
Routines = [{fun should_block_anonymous/1, {<<"433">>, <<"Anonymity Disallowed">>}}
,{fun is_blacklisted/1, {<<"603">>, <<"Decline">>}}
Blacklist = get_blacklist(JObj),
Routines = [{fun should_block_anonymous/2, {<<"433">>, <<"Anonymity Disallowed">>}}
,{fun is_blacklisted/2, {<<"603">>, <<"Decline">>}}
],
lists:any(fun(Routine) -> block_call_routine(Routine, JObj) end, Routines).
lists:any(fun(Routine) -> block_call_routine(Routine, JObj, Blacklist) end, Routines).

-type block_call_fun() :: fun((kz_json:object()) -> boolean()).
-type block_call_fun() :: fun((kz_json:object(), kz_json:object()) -> boolean()).
-type block_call_resp() :: {kz_term:ne_binary(), kz_term:ne_binary()}.
-type block_call_arg() :: {block_call_fun(), block_call_resp()}.

-spec block_call_routine(block_call_arg(), kz_json:object()) -> boolean().
block_call_routine({Fun, {Code, Msg}}, JObj) ->
case Fun(JObj) of
-spec block_call_routine(block_call_arg(), kz_json:object(), kz_json:object()) -> boolean().
block_call_routine({Fun, {Code, Msg}}, JObj, Blacklist) ->
case Fun(JObj, Blacklist) of
'true' -> send_error_response(JObj, Code, Msg);
'false' -> 'false'
end.

-spec should_block_anonymous(kz_json:object()) -> boolean().
should_block_anonymous(JObj) ->
-spec should_block_anonymous(kz_json:object(), kz_json:object()) -> boolean().
should_block_anonymous(JObj, Blacklist) ->
kz_privacy:should_block_anonymous(JObj)
orelse (kz_privacy:is_anonymous(JObj)
andalso kz_json:is_true(<<"should_block_anonymous">>, get_blacklist(JObj))
andalso kz_json:is_true(<<"should_block_anonymous">>, Blacklist)
).

%%------------------------------------------------------------------------------
Expand Down Expand Up @@ -286,66 +287,90 @@ transition_port_in(NumberProps, _JObj) ->
%% @doc
%% @end
%%------------------------------------------------------------------------------
-spec is_blacklisted(kz_json:object()) -> boolean().
is_blacklisted(JObj) ->
is_number_blacklisted(get_blacklist(JObj), JObj).

-spec is_number_blacklisted(kz_json:object(), kz_json:object()) -> boolean().
is_number_blacklisted(Blacklist, JObj) ->
Number = kz_json:get_value(<<"Caller-ID-Number">>, JObj),
Normalized = knm_converters:normalize(Number),
case kz_json:get_value(Normalized, Blacklist) of
-spec is_blacklisted(kz_json:object(), kz_json:object()) -> boolean().
is_blacklisted(JObj, Blacklist) ->
Number = kz_json:get_value(<<"Caller-ID-Number">>, JObj, kz_privacy:anonymous_caller_id_number()),
case kz_json:get_value(<<"number">>, Blacklist) of
'undefined' -> 'false';
_ -> lager:info("~s(~s) is blacklisted", [Number, Normalized]),
'true'
Normalized ->
lager:info("~s(~s) is blacklisted", [Number, Normalized]),
'true'
end.

-spec get_blacklists(kz_term:ne_binary()) ->
{'ok', kz_term:ne_binaries()} |
{'error', any()}.
get_blacklists(AccountId) ->
-spec get_account_blacklist_ids(kz_term:ne_binary()) -> kz_term:ne_binaries().
get_account_blacklist_ids(AccountId) ->
case kzd_accounts:fetch(AccountId) of
{'error', _R}=E ->
{'error', _R} ->
lager:error("could not open account doc ~s : ~p", [AccountId, _R]),
E;
[];
{'ok', Doc} ->
case kzd_accounts:blacklists(Doc, []) of
[] -> {'error', 'undefined'};
[_|_]=Blacklists-> {'ok', Blacklists};
_ -> {'error', 'miss_configured'}
end
kzd_accounts:blacklists(Doc, [])
end.

-spec get_blacklist(kz_json:object()) -> kz_json:object().
-spec get_blacklist(kz_json:object()) -> kz_term:api_object().
get_blacklist(JObj) ->
AccountId = kz_json:get_ne_value(?CCV(<<"Account-ID">>), JObj),
case get_blacklists(AccountId) of
{'error', _R} -> kz_json:new();
{'ok', Blacklists} -> get_blacklist(AccountId, Blacklists)
end.
get_blacklist(AccountId, JObj, get_account_blacklist_ids(AccountId)).

-spec get_blacklist(kz_term:ne_binary(), kz_term:ne_binaries()) -> kz_json:object().
get_blacklist(AccountId, Blacklists) ->
-spec get_blacklist(kz_term:ne_binary(), kz_json:object(), kz_term:ne_binaries()) ->
kz_json:object().
get_blacklist(_AccountId, _JObj, []) ->
kz_json:new();
get_blacklist(AccountId, JObj, BlacklistIds) ->
BlacklistedNumber = get_blacklisted_number(AccountId, JObj, BlacklistIds),
BlockAnonymous = get_block_anonymous(AccountId, BlacklistIds),
kz_json:merge_jobjs(BlacklistedNumber, BlockAnonymous).

get_blacklisted_number(AccountId, JObj, BlacklistIds) ->
Number = kz_json:get_value(<<"Caller-ID-Number">>, JObj, kz_privacy:anonymous_caller_id_number()),
Normalized = knm_converters:normalize(Number),
ViewOptions = [{'key', Normalized}],
AccountDb = kz_util:format_account_id(AccountId, 'encoded'),
lists:foldl(fun(BlacklistId, Acc) ->
case kz_datamgr:open_cache_doc(AccountDb, BlacklistId) of
{'error', _R} ->
lager:error("could not open ~s in ~s: ~p", [BlacklistId, AccountDb, _R]),
Acc;
{'ok', Doc} ->
Numbers = kz_json:get_value(<<"numbers">>, Doc, kz_json:new()),
BlackList = maybe_set_block_anonymous(Numbers, kz_json:is_true(<<"should_block_anonymous">>, Doc)),
kz_json:merge_jobjs(Acc, BlackList)
end
end
,kz_json:new()
,Blacklists
).
case kz_datamgr:get_results(AccountDb, <<"blacklists/listing_by_number">>, ViewOptions) of
{'ok', []} ->
kz_json:new();
{'ok', JObjs} ->
merge_blacklisted_numbers(Normalized, BlacklistIds, JObjs, kz_json:new());
{'error', _Reason} ->
lager:error("could not fetch number blacklists from db ~s: ~p", [AccountId, _Reason]),
kz_json:new()
end.

-spec merge_blacklisted_numbers(kz_term:ne_binary(), kz_term:ne_binaries(), kz_json:objects(), kz_json:objects()) -> kz_json:object().
merge_blacklisted_numbers(_Normalized, _BlacklistIds, [], Acc) ->
Acc;
merge_blacklisted_numbers(Normalized, BlacklistIds, [JObj | _]=JObjs, Acc) ->
case lists:member(kz_doc:id(JObj), BlacklistIds) of
'true' ->
maybe_set_block_anonymous(Normalized, BlacklistIds, JObjs, kz_json:set_value(<<"number">>, Normalized, Acc));
'false' ->
merge_blacklisted_numbers(Normalized, BlacklistIds, JObjs, Acc)
end.

-spec maybe_set_block_anonymous(kz_term:ne_binary(), kz_term:ne_binaries(), kz_json:objects(), kz_json:objects()) -> kz_json:object().
maybe_set_block_anonymous(Normalized, BlacklistIds, [JObj | JObjs], Acc) ->
case kz_json:is_true(<<"value">>, JObj) of
'true' ->
kz_json:set_value(<<"should_block_anonymous">>, 'true', Acc);
'false' ->
merge_blacklisted_numbers(Normalized, BlacklistIds, JObjs, Acc)
end.

-spec maybe_set_block_anonymous(kz_json:object(), boolean()) -> kz_json:object().
maybe_set_block_anonymous(JObj, 'false') -> JObj;
maybe_set_block_anonymous(JObj, 'true') ->
kz_json:set_value(<<"should_block_anonymous">>, 'true', JObj).
get_block_anonymous(AccountId, BlacklistIds) ->
ViewOptions = [{'key', 'true'}],
AccountDb = kz_util:format_account_id(AccountId, 'encoded'),
case kz_datamgr:get_results(AccountDb, <<"blacklists/should_block_anonymous">>, ViewOptions) of
{'ok', []} ->
kz_json:new();
{'ok', JObjs} ->
case lists:any(fun(JObj) -> lists:member(kz_doc:id(JObj), BlacklistIds) end, JObjs) of
'true' -> kz_json:set_value(<<"should_block_anonymous">>, 'true', kz_json:new());
'false' -> kz_json:new()
end;
{'error', _Reason} ->
lager:error("could not fetch should_block_anonymous blacklists from db ~s: ~p", [AccountId, _Reason]),
kz_json:new()
end.

-spec send_error_response(kz_json:object(), kz_term:ne_binary(), kz_term:ne_binary()) -> 'true'.
send_error_response(JObj, Code, Message) ->
Expand Down
19 changes: 19 additions & 0 deletions core/kazoo_apps/priv/couchdb/account/blacklists.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@
"views": {
"crossbar_listing": {
"map": "function(doc) { if (doc.pvt_type != 'blacklist' || doc.pvt_deleted) return; emit(doc._id, {'id': doc._id, 'name': doc.name, 'flags': doc.flags || [] }); }"
},
"listing_by_number": {
"map": [
"function(doc) {",
" if (doc.pvt_type != 'blacklist' || doc.pvt_deleted) return;",
" for (var num in doc.numbers) {",
" if (!doc.numbers.hasOwnProperty(num)) continue;",
" emit(num, doc.should_block_anonymous);",
" }",
"}"
]
},
"should_block_anonymous": {
"map": [
"function(doc) {",
" if (doc.pvt_type != 'blacklist' || doc.pvt_deleted) return;",
" emit(doc.should_block_anonymous);",
"}"
]
}
}
}