-module(nova_security_handler). -behaviour(cowboy_middleware). -export([ execute/2 ]). -include_lib("kernel/include/logger.hrl"). execute(Req, Env = #{secure := false}) -> {ok, Req, Env}; execute(Req = #{host := Host}, Env = #{secure := {Module, Function}}) -> UseStacktrace = persistent_term:get(nova_use_stacktrace, false), try Module:Function(Req) of Result -> handle_response(Result, Req, Env) catch Class:Reason:Stacktrace when UseStacktrace == true -> ?LOG_ERROR(#{msg => <<"Security handler crashed">>, class => Class, reason => Reason, stacktrace => Stacktrace}), Payload = #{status_code => 500, stacktrace => Stacktrace, class => Class, reason => Reason}, {ok, Req0, _Env} = nova_router:render_status_page(Host, 500, #{}, Req#{crash_info => Payload}, Env), Req1 = cowboy_req:reply(500, Req0), {stop, Req1}; Class:Reason -> ?LOG_ERROR(#{msg => <<"Security handler crashed">>, class => Class, reason => Reason}), Payload = #{status_code => 500, class => Class, reason => Reason}, {ok, Req0, _Env} = nova_router:render_status_page(Host, 500, #{}, Req#{crash_info => Payload}, Env), Req1 = cowboy_req:reply(500, Req0), {stop, Req1} end. handle_response({true, AuthData}, Req, Env) -> case maps:get(cowboy_handler, Env, undefined) of nova_ws_handler -> Args = maps:get(arguments, Env, #{}), {ok, Req, Env#{arguments => Args#{controller_data => #{auth_data => AuthData}}}}; _ -> {ok, Req#{auth_data => AuthData}, Env} end; handle_response(true, Req, Env) -> {ok, Req, Env}; handle_response({false, Headers}, Req, Env) -> handle_response({false, 401, Headers, false}, Req, Env); handle_response({false, StatusCode, Headers}, Req, Env) -> handle_response({false, StatusCode, Headers, false}, Req, Env); handle_response({false, StatusCode, Headers, Body}, Req = #{host := Host}, Env) -> {ok, Req1, _Env1} = case Body of false -> {ok, _Req0, _Env0} = nova_router:render_status_page(Host, StatusCode, #{}, Req, Env); _ -> Req0 = cowboy_req:set_resp_body(Body, Req), {ok, Req0, Env} end, Req2 = cowboy_req:set_resp_headers(Headers, Req1), Req3 = cowboy_req:reply(StatusCode, Req2), {stop, Req3}; handle_response({redirect, Route}, Req, _Env) -> Req0 = cowboy_req:set_resp_headers(#{<<"Location">> => list_to_binary(Route)}, Req), {stop, Req0}; handle_response(_, Req = #{host := Host}, Env) -> {ok, Req0, _Env0} = nova_router:render_status_page(Host, 401, #{}, Req, Env), Req1 = cowboy_req:reply(401, Req0), {stop, Req1}.