PageRenderTime 30ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/middleware/ewgi_session/ewgi_session.erl

http://github.com/skarab/ewgi
Erlang | 169 lines | 112 code | 21 blank | 36 comment | 3 complexity | 7b4359743e099a16e2c263d26b035497 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. %% @author Hunter Morris <hunter.morris@smarkets.com>
  2. %% @author Davide Marquęs <nesrait@gmail.com>
  3. %% @copyright 2009 Smarkets Limited.
  4. %%
  5. %% @doc Session middleware with pluggable storage mechanisms.
  6. %% Based on smak_auth_cookie by Hunter Morris.
  7. %%
  8. %% The loading of session data is done when the middleware is
  9. %% initialized and the storage of session data is only done(!)
  10. %% after calling the middleware downstream.
  11. %%
  12. %% In case of errors in the downstream middleware the updates
  13. %% to session data are not persisted.
  14. %% @end
  15. %%
  16. %% Licensed under the MIT license:
  17. %% http://www.opensource.org/licenses/mit-license.php
  18. -module(ewgi_session).
  19. -author('Hunter Morris <hunter.morris@smarkets.com>').
  20. -author('Davide Marquęs <nesrait@gmail.com>').
  21. %% Session API
  22. -export([new_session/1, delete_session/1]).
  23. %% Proplists-looking functions
  24. -export([get_all_values/1, get_value/2, get_value/3, set_value/3, delete/2]).
  25. %% Functions used by the session store modules
  26. -export([init_session/4, session_updated/1, get_session/2]).
  27. %% Util functions used by the store examples
  28. -export([session_create_app/1, session_delete_app/1]).
  29. -include("ewgi.hrl").
  30. -include("session.hrl").
  31. -define(SESSION_STATE, "ewgi.session.session_state").
  32. -define(SESSION_DATA, "ewgi.session.session_data").
  33. %%====================================================================
  34. %% Session API
  35. %%====================================================================
  36. new_session(Ctx) ->
  37. update_session_data(Ctx, []).
  38. delete_session(Ctx) ->
  39. update_session_data(Ctx, []).
  40. %%====================================================================
  41. %% Functions that deal with the already loaded session_data
  42. %%====================================================================
  43. get_all_values(Ctx) ->
  44. ewgi_api:find_data(?SESSION_DATA, Ctx).
  45. get_value(Key, Ctx) ->
  46. get_value(Key, Ctx, undefined).
  47. get_value(Key, Ctx, Default) ->
  48. case ewgi_api:find_data(?SESSION_DATA, Ctx) of
  49. undefined ->
  50. Default;
  51. Data ->
  52. proplists:get_value(Key, Data, Default)
  53. end.
  54. set_value(Key, Value, Ctx) ->
  55. Data = ewgi_api:find_data(?SESSION_DATA, Ctx),
  56. Data1 =
  57. case proplists:is_defined(Key, Data) of
  58. true ->
  59. Rest = proplists:delete(Key, Data),
  60. [{Key, Value}|Rest];
  61. false ->
  62. [{Key, Value}|Data]
  63. end,
  64. update_session_data(Ctx, Data1).
  65. delete(Key, Ctx) ->
  66. Data = ewgi_api:find_data(?SESSION_DATA, Ctx),
  67. case proplists:is_defined(Key, Data) of
  68. true ->
  69. Data1 = proplists:delete(Key, Data),
  70. update_session_data(Ctx, Data1);
  71. false ->
  72. Ctx
  73. end.
  74. %%====================================================================
  75. %% Session creation and validation
  76. %%====================================================================
  77. session_updated(Ctx) ->
  78. updated =:= ewgi_api:find_data(?SESSION_STATE, Ctx).
  79. init_session(Ctx, Session, Timeout, IncludeIp) when is_record(Session, session) ->
  80. case validate_session(Ctx, Session, Timeout, IncludeIp) of
  81. {error, _Reason} ->
  82. invalid_session;
  83. ValidSession ->
  84. SessionData = ValidSession#session.data,
  85. Ctx1 = ewgi_api:store_data(?SESSION_DATA, SessionData, Ctx),
  86. ewgi_api:store_data(?SESSION_STATE, loaded, Ctx1)
  87. end;
  88. init_session(_, _, _, _) ->
  89. invalid_session.
  90. validate_session(Ctx, Session, Timeout, IncludeIp) ->
  91. ExpireTime = Session#session.timestamp + Timeout,
  92. Expired = ewgi_util_calendar:now_utc_ts_ms() >= ExpireTime,
  93. if Expired ->
  94. {error, session_timeout};
  95. true ->
  96. case IncludeIp of
  97. false ->
  98. Session;
  99. true ->
  100. Addr = ewgi_api:remote_addr(Ctx),
  101. SavedAddr = Session#session.ip_address,
  102. if SavedAddr =:= Addr ->
  103. Session;
  104. true ->
  105. error_logger:error_msg("Saved address: ~p, New address: ~p", [SavedAddr, Addr]),
  106. {error, invalid_ip_address}
  107. end
  108. end
  109. end.
  110. get_session(Ctx, IncludeIp) ->
  111. SessionData = ewgi_api:find_data(?SESSION_DATA, Ctx),
  112. Timestamp = ewgi_util_calendar:now_utc_ts_ms(),
  113. Session0 = #session{data=SessionData, timestamp=Timestamp},
  114. if IncludeIp ->
  115. Addr = ewgi_api:remote_addr(Ctx),
  116. Session0#session{ip_address=Addr};
  117. true ->
  118. Session0
  119. end.
  120. %%====================================================================
  121. %% example functions on how to use the session middleware
  122. %%====================================================================
  123. session_create_app(Ctx) ->
  124. case ?MODULE:get_value(name, Ctx) of
  125. undefined ->
  126. Name = "Bill",
  127. Body = ["Hello stranger! I'll call you ", Name, " from now on! (please refresh the page)"],
  128. Ctx1 = ?MODULE:set_value(name, "Bill", Ctx);
  129. Name ->
  130. Body = ["Hello ", Name, "! Nice to see you again! "
  131. "(no point in reloading again, delete the session by "
  132. "adding /delete to the current url)"],
  133. Ctx1 = Ctx
  134. end,
  135. Headers = [{"Content-type", "text/plain"}] ++ ewgi_api:response_headers(Ctx1),
  136. Ctx2 = ewgi_api:response_headers(Headers, Ctx1),
  137. Ctx3 = ewgi_api:response_status({200, "OK"}, Ctx2),
  138. ewgi_api:response_message_body(Body, Ctx3).
  139. session_delete_app(Ctx) ->
  140. Ctx1 = ?MODULE:delete_session(Ctx),
  141. ewgi_api:response_message_body("Deleted session!",
  142. ewgi_api:response_status({200, "OK"}, Ctx1)).
  143. %%====================================================================
  144. %% Internal functions
  145. %%====================================================================
  146. update_session_data(Ctx, SessionData) ->
  147. Ctx1 = ewgi_api:store_data(?SESSION_DATA, SessionData, Ctx),
  148. ewgi_api:store_data(?SESSION_STATE, updated, Ctx1).