PageRenderTime 245ms CodeModel.GetById 80ms app.highlight 16ms RepoModel.GetById 145ms 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
  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
 19-module(ewgi_session).
 20-author('Hunter Morris <hunter.morris@smarkets.com>').
 21-author('Davide Marquęs <nesrait@gmail.com>').
 22
 23%% Session API
 24-export([new_session/1, delete_session/1]).
 25%% Proplists-looking functions
 26-export([get_all_values/1, get_value/2, get_value/3, set_value/3, delete/2]).
 27
 28%% Functions used by the session store modules
 29-export([init_session/4, session_updated/1, get_session/2]).
 30
 31%% Util functions used by the store examples
 32-export([session_create_app/1, session_delete_app/1]).
 33
 34-include("ewgi.hrl").
 35-include("session.hrl").
 36
 37-define(SESSION_STATE, "ewgi.session.session_state").
 38-define(SESSION_DATA, "ewgi.session.session_data").
 39
 40%%====================================================================
 41%% Session API
 42%%====================================================================
 43new_session(Ctx) ->
 44    update_session_data(Ctx, []).
 45
 46delete_session(Ctx) ->
 47    update_session_data(Ctx, []).
 48
 49%%====================================================================
 50%% Functions that deal with the already loaded session_data
 51%%====================================================================
 52get_all_values(Ctx) ->
 53    ewgi_api:find_data(?SESSION_DATA, Ctx).
 54
 55get_value(Key, Ctx) ->
 56	get_value(Key, Ctx, undefined).
 57	
 58get_value(Key, Ctx, Default) ->
 59	case ewgi_api:find_data(?SESSION_DATA, Ctx) of
 60		undefined ->
 61			Default;
 62		Data ->
 63			proplists:get_value(Key, Data, Default)
 64	end.
 65
 66set_value(Key, Value, Ctx) ->
 67    Data = ewgi_api:find_data(?SESSION_DATA, Ctx),
 68    Data1 =
 69	case proplists:is_defined(Key, Data) of
 70	    true ->
 71		Rest = proplists:delete(Key, Data),
 72		[{Key, Value}|Rest];
 73	    false ->
 74		[{Key, Value}|Data]
 75	end,
 76    update_session_data(Ctx, Data1).
 77
 78delete(Key, Ctx) ->
 79    Data = ewgi_api:find_data(?SESSION_DATA, Ctx),
 80    case proplists:is_defined(Key, Data) of
 81	true ->
 82	    Data1 = proplists:delete(Key, Data),
 83	    update_session_data(Ctx, Data1);
 84	false ->
 85	    Ctx
 86    end.
 87
 88%%====================================================================
 89%% Session creation and validation
 90%%====================================================================
 91session_updated(Ctx) ->
 92    updated =:= ewgi_api:find_data(?SESSION_STATE, Ctx).
 93
 94init_session(Ctx, Session, Timeout, IncludeIp) when is_record(Session, session) ->
 95    case validate_session(Ctx, Session, Timeout, IncludeIp) of
 96	{error, _Reason} ->
 97	    invalid_session;
 98	ValidSession ->
 99	    SessionData = ValidSession#session.data,
100	    Ctx1 = ewgi_api:store_data(?SESSION_DATA, SessionData, Ctx),
101	    ewgi_api:store_data(?SESSION_STATE, loaded, Ctx1)
102    end;
103init_session(_, _, _, _) ->
104    invalid_session.
105
106validate_session(Ctx, Session, Timeout, IncludeIp) ->
107    ExpireTime = Session#session.timestamp + Timeout,
108    Expired = ewgi_util_calendar:now_utc_ts_ms() >= ExpireTime,
109    if Expired ->
110	    {error, session_timeout};
111       true ->
112	    case IncludeIp of
113		false ->
114		    Session;
115		true ->
116		    Addr = ewgi_api:remote_addr(Ctx),
117		    SavedAddr = Session#session.ip_address,
118		    if SavedAddr =:= Addr ->
119			    Session;
120		       true ->
121			    error_logger:error_msg("Saved address: ~p, New address: ~p", [SavedAddr, Addr]),
122			    {error, invalid_ip_address}
123		    end
124	    end
125    end.
126
127get_session(Ctx, IncludeIp) ->
128    SessionData = ewgi_api:find_data(?SESSION_DATA, Ctx),
129    Timestamp = ewgi_util_calendar:now_utc_ts_ms(),
130    Session0 = #session{data=SessionData, timestamp=Timestamp},
131    if IncludeIp ->
132	    Addr = ewgi_api:remote_addr(Ctx),
133	    Session0#session{ip_address=Addr};
134       true ->
135	    Session0
136    end.
137
138%%====================================================================
139%% example functions on how to use the session middleware
140%%====================================================================
141session_create_app(Ctx) ->
142    case ?MODULE:get_value(name, Ctx) of
143	undefined ->
144	    Name = "Bill",
145	    Body = ["Hello stranger! I'll call you ", Name, " from now on! (please refresh the page)"],
146	    Ctx1 = ?MODULE:set_value(name, "Bill", Ctx);
147	Name ->
148	    Body = ["Hello ", Name, "! Nice to see you again! "
149		    "(no point in reloading again, delete the session by "
150		    "adding /delete to the current url)"],
151	    Ctx1 = Ctx
152    end,
153    Headers = [{"Content-type", "text/plain"}] ++ ewgi_api:response_headers(Ctx1),
154    Ctx2 = ewgi_api:response_headers(Headers, Ctx1),
155    Ctx3 = ewgi_api:response_status({200, "OK"}, Ctx2),
156    ewgi_api:response_message_body(Body, Ctx3).
157
158session_delete_app(Ctx) ->
159    Ctx1 = ?MODULE:delete_session(Ctx),
160    ewgi_api:response_message_body("Deleted session!",
161				   ewgi_api:response_status({200, "OK"}, Ctx1)).
162
163%%====================================================================
164%% Internal functions
165%%====================================================================
166update_session_data(Ctx, SessionData) ->
167    Ctx1 = ewgi_api:store_data(?SESSION_DATA, SessionData, Ctx),
168    ewgi_api:store_data(?SESSION_STATE, updated, Ctx1).
169