feat(api): optional auth for public search and studio week. Refs EventHub/EventHubFront#58
This commit is contained in:
@@ -115,13 +115,13 @@ calendar_update_schema() ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
|
|
||||||
get_calendar(Req) ->
|
get_calendar(Req) ->
|
||||||
case handler_utils:auth_user(Req) of
|
case handler_utils:auth_user_optional(Req) of
|
||||||
{ok, UserId, Req1} ->
|
{ok, UserId, Req1} ->
|
||||||
CalendarId = cowboy_req:binding(id, Req1),
|
CalendarId = cowboy_req:binding(id, Req1),
|
||||||
case logic_calendar:get_calendar(UserId, CalendarId) of
|
case logic_calendar:get_calendar(UserId, CalendarId) of
|
||||||
{ok, Calendar} ->
|
{ok, Calendar} ->
|
||||||
Json0 = handler_utils:calendar_to_json(Calendar),
|
Json0 = handler_utils:calendar_to_json(Calendar),
|
||||||
Following = logic_calendar_follow:is_following(UserId, CalendarId),
|
Following = UserId =/= <<>> andalso logic_calendar_follow:is_following(UserId, CalendarId),
|
||||||
handler_utils:send_json(Req1, 200, Json0#{following => Following});
|
handler_utils:send_json(Req1, 200, Json0#{following => Following});
|
||||||
{error, access_denied} ->
|
{error, access_denied} ->
|
||||||
handler_utils:send_error(Req1, 403, <<"Access denied">>);
|
handler_utils:send_error(Req1, 403, <<"Access denied">>);
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ has_user_binding(Req) ->
|
|||||||
cowboy_req:binding(user_id, Req) =/= undefined.
|
cowboy_req:binding(user_id, Req) =/= undefined.
|
||||||
|
|
||||||
list_specialists(Req) ->
|
list_specialists(Req) ->
|
||||||
case handler_utils:auth_user(Req) of
|
case handler_utils:auth_user_optional(Req) of
|
||||||
{ok, UserId, Req1} ->
|
{ok, UserId, Req1} ->
|
||||||
CalendarId = cowboy_req:binding(id, Req1),
|
CalendarId = cowboy_req:binding(id, Req1),
|
||||||
case logic_calendar_specialist:list(UserId, CalendarId) of
|
case logic_calendar_specialist:list(UserId, CalendarId) of
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ create_event(Req) ->
|
|||||||
%% @doc GET /v1/calendars/:calendar_id/events — список событий.
|
%% @doc GET /v1/calendars/:calendar_id/events — список событий.
|
||||||
-spec list_events(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
|
-spec list_events(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
|
||||||
list_events(Req) ->
|
list_events(Req) ->
|
||||||
case handler_utils:auth_user(Req) of
|
case handler_utils:auth_user_optional(Req) of
|
||||||
{ok, UserId, Req1} ->
|
{ok, UserId, Req1} ->
|
||||||
CalendarId = cowboy_req:binding(calendar_id, Req1),
|
CalendarId = cowboy_req:binding(calendar_id, Req1),
|
||||||
Qs = cowboy_req:parse_qs(Req1),
|
Qs = cowboy_req:parse_qs(Req1),
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ handle(Req, _Opts) ->
|
|||||||
%% @doc GET /v1/search — полнотекстовый поиск с фильтрами.
|
%% @doc GET /v1/search — полнотекстовый поиск с фильтрами.
|
||||||
-spec search(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
|
-spec search(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
|
||||||
search(Req) ->
|
search(Req) ->
|
||||||
case handler_utils:auth_user(Req) of
|
case handler_utils:auth_user_optional(Req) of
|
||||||
{ok, UserId, Req1} ->
|
{ok, UserId, Req1} ->
|
||||||
Qs = cowboy_req:parse_qs(Req1),
|
Qs = cowboy_req:parse_qs(Req1),
|
||||||
Type = proplists:get_value(<<"type">>, Qs, undefined),
|
Type = proplists:get_value(<<"type">>, Qs, undefined),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
-export([
|
-export([
|
||||||
auth_admin/1,
|
auth_admin/1,
|
||||||
auth_user/1,
|
auth_user/1,
|
||||||
|
auth_user_optional/1,
|
||||||
send_json/3,
|
send_json/3,
|
||||||
send_json/4,
|
send_json/4,
|
||||||
send_error/3,
|
send_error/3,
|
||||||
@@ -75,6 +76,17 @@ is_superadmin(Req) ->
|
|||||||
auth_user(Req) ->
|
auth_user(Req) ->
|
||||||
handler_auth:authenticate(Req).
|
handler_auth:authenticate(Req).
|
||||||
|
|
||||||
|
%% @doc Как auth_user/1, но без заголовка Authorization — гость (`<<>>`).
|
||||||
|
%% Невалидный Bearer по-прежнему 401.
|
||||||
|
-spec auth_user_optional(cowboy_req:req()) ->
|
||||||
|
{ok, binary(), cowboy_req:req()} | {error, integer(), binary(), cowboy_req:req()}.
|
||||||
|
auth_user_optional(Req) ->
|
||||||
|
case cowboy_req:header(<<"authorization">>, Req) of
|
||||||
|
undefined -> {ok, <<>>, Req};
|
||||||
|
<<>> -> {ok, <<>>, Req};
|
||||||
|
_ -> auth_user(Req)
|
||||||
|
end.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% HTTP‑ответы
|
%%% HTTP‑ответы
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ test() ->
|
|||||||
CalId = api_test_runner:create_calendar(Token, #{title => <<"TestCal">>}),
|
CalId = api_test_runner:create_calendar(Token, #{title => <<"TestCal">>}),
|
||||||
|
|
||||||
test_get_calendar(Token, CalId),
|
test_get_calendar(Token, CalId),
|
||||||
test_get_calendar_unauthorized(CalId),
|
test_get_calendar_guest(Token, CalId),
|
||||||
test_get_calendar_not_found(Token),
|
test_get_calendar_not_found(Token),
|
||||||
test_update_calendar(Token, CalId),
|
test_update_calendar(Token, CalId),
|
||||||
test_update_calendar_settings(Token, CalId),
|
test_update_calendar_settings(Token, CalId),
|
||||||
@@ -39,12 +39,15 @@ test_get_calendar(Token, CalId) ->
|
|||||||
?assert(maps:is_key(<<"title">>, Cal)),
|
?assert(maps:is_key(<<"title">>, Cal)),
|
||||||
ct:pal(" OK: ~s", [maps:get(<<"title">>, Cal)]).
|
ct:pal(" OK: ~s", [maps:get(<<"title">>, Cal)]).
|
||||||
|
|
||||||
test_get_calendar_unauthorized(CalId) ->
|
test_get_calendar_guest(Token, CalId) ->
|
||||||
ct:pal(" TEST: Get calendar without token (401)"),
|
ct:pal(" TEST: Guest GET commercial calendar (200), personal (403)"),
|
||||||
Path = <<"/v1/calendars/", CalId/binary>>,
|
Path = <<"/v1/calendars/", CalId/binary>>,
|
||||||
Resp = api_test_runner:client_request(get, Path, <<>>),
|
{ok, 200, _, Body} = api_test_runner:client_request(get, Path, <<>>),
|
||||||
?assertMatch({ok, 401, _, _}, Resp),
|
#{<<"id">> := CalId} = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||||
ct:pal(" OK: got 401").
|
PersonalId = api_test_runner:existing_personal_calendar_id(Token),
|
||||||
|
PPath = <<"/v1/calendars/", PersonalId/binary>>,
|
||||||
|
{ok, 403, _, _} = api_test_runner:client_request(get, PPath, <<>>),
|
||||||
|
ct:pal(" OK: guest commercial 200, personal 403").
|
||||||
|
|
||||||
test_get_calendar_not_found(Token) ->
|
test_get_calendar_not_found(Token) ->
|
||||||
ct:pal(" TEST: Get non-existent calendar (404)"),
|
ct:pal(" TEST: Get non-existent calendar (404)"),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
%%% - поиск с фильтрацией по датам (from/to)
|
%%% - поиск с фильтрацией по датам (from/to)
|
||||||
%%% - геопоиск (lat, lon, radius)
|
%%% - геопоиск (lat, lon, radius)
|
||||||
%%% - пагинацию результатов
|
%%% - пагинацию результатов
|
||||||
%%% - ошибку 401 без токена
|
%%% - публичный поиск без токена (200)
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(user_search_tests).
|
-module(user_search_tests).
|
||||||
@@ -120,7 +120,9 @@ test_search_pagination(Token) ->
|
|||||||
ct:pal(" OK").
|
ct:pal(" OK").
|
||||||
|
|
||||||
test_search_unauthorized() ->
|
test_search_unauthorized() ->
|
||||||
ct:pal(" TEST: Search without token"),
|
ct:pal(" TEST: Search without token (public)"),
|
||||||
Resp = api_test_runner:client_request(get, <<"/v1/search?q=test">>, <<>>),
|
Resp = api_test_runner:client_request(get, <<"/v1/search?q=test">>, <<>>),
|
||||||
?assertMatch({ok, 401, _, _}, Resp),
|
{ok, 200, _, Body} = Resp,
|
||||||
ct:pal(" OK: got 401").
|
Decoded = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||||
|
?assert(is_map(Decoded)),
|
||||||
|
ct:pal(" OK: guest search 200").
|
||||||
Reference in New Issue
Block a user