/src/middleware/ewgi_session/ewgi_session_cookie_store.erl
Erlang | 130 lines | 93 code | 13 blank | 24 comment | 1 complexity | 7f02cf5aa7f8b3318c7b7d636446c0bc 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 Cookie-based session store. 6%% Based on smak_auth_cookie by Hunter Morris. 7%% @end 8%% 9%% Licensed under the MIT license: 10%% http://www.opensource.org/licenses/mit-license.php 11 12-module(ewgi_session_cookie_store). 13-author('Hunter Morris <hunter.morris@smarkets.com>'). 14-author('Davide Marquês <nesrait@gmail.com>'). 15 16%% Session Store API 17-export([load_session/2, store_session/2, delete_session/2]). 18 19%% Usage examples 20-export([create_example/1, delete_example/1]). 21 22-import(ewgi_util_cookie, [cookie_headers/5, cookie_safe_encode/1, cookie_safe_decode/1]). 23 24-include("ewgi.hrl"). 25 26-define(DEFAULT_COOKIE_NAME, "session_id"). 27-define(ENCODER, ewgi_util_crypto). 28-define(MAX_LENGTH, 4096). 29 30%%==================================================================== 31%% Session Store API 32%%==================================================================== 33-spec load_session(ewgi_context(), list()) -> ewgi_context(). 34load_session(Ctx, [CookieName, _CookiePath, _SecureCookie, Timeout, IncludeIp, Key]=Args) -> 35 case ewgi_api:get_header_value("cookie", Ctx) of 36 undefined -> 37 ewgi_session:new_session(Ctx); 38 Cookies -> 39 CookieValues = ewgi_util_cookie:parse_cookie(Cookies), 40 case proplists:get_value(CookieName, CookieValues) of 41 undefined -> 42 ewgi_session:new_session(Ctx); 43 SessionCookie -> 44 case decode_cookie_contents(SessionCookie, Key) of 45 {error,_} = _Error -> 46 %% DISPLAY ERROR!? 47 ewgi_session:new_session(Ctx); 48 Session -> 49 case ewgi_session:init_session(Ctx, Session, Timeout, IncludeIp) of 50 invalid_session -> 51 %% DISPLAY ERROR!? 52 Ctx_2 = ?MODULE:delete_session(Ctx, Args), 53 ewgi_session:new_session(Ctx_2); 54 Ctx_2 -> 55 Ctx_2 56 end 57 end 58 end 59 end. 60 61-spec store_session(ewgi_context(), list()) -> ewgi_context(). 62store_session(Ctx, [CookieName, CookiePath, SecureCookie, _Timeout, IncludeIp, Key]) -> 63 Updated = ewgi_session:session_updated(Ctx), 64 if Updated -> 65 Session = ewgi_session:get_session(Ctx, IncludeIp), 66 case ?ENCODER:encode(Key, term_to_binary(Session), ?MAX_LENGTH) of 67 {error, _} = _Error -> 68 %% TODO: report this? To whom? 69 Ctx; 70 EncryptedVal -> 71 CookieVal = cookie_safe_encode(EncryptedVal), 72 cookie_headers(Ctx, CookieName, CookieVal, CookiePath, SecureCookie) 73 end; 74 true -> %% nothing to do! 75 Ctx 76 end. 77 78delete_session(Ctx, [CookieName, CookiePath, SecureCookie, _Timeout, _IncludeIp, _Key]) -> 79 cookie_headers(Ctx, CookieName, [], CookiePath, SecureCookie). 80 81%%==================================================================== 82%% example functions on how to use the session middleware 83%%==================================================================== 84-define(COOKIE_SIGNING_KEY, <<"ABCDEFGHIJKLMNOP">>). 85-define(COOKIE_PATH, "/"). 86-define(SECURE_COOKIE, false). 87-define(INCLUDE_IP, true). 88-define(SESSION_TIMEOUT, 15 * 60 * 1000). %% 15 minutes 89-define(COOKIE_STORE_ARGS, [ 90 "cookie_session_id", 91 ?COOKIE_PATH, 92 ?SECURE_COOKIE, 93 ?SESSION_TIMEOUT, 94 ?INCLUDE_IP, 95 ?COOKIE_SIGNING_KEY 96 ]). 97 98create_example(Ctx) -> 99 SessionApp = fun ewgi_session:session_create_app/1, 100 Ctx1 = ?MODULE:load_session(Ctx, ?COOKIE_STORE_ARGS), 101 Ctx2 = SessionApp(Ctx1), 102 ?MODULE:store_session(Ctx2, ?COOKIE_STORE_ARGS). 103 104delete_example(Ctx) -> 105 SessionApp = fun ewgi_session:session_delete_app/1, 106 Ctx1 = ?MODULE:load_session(Ctx, ?COOKIE_STORE_ARGS), 107 Ctx2 = SessionApp(Ctx1), 108 ?MODULE:store_session(Ctx2, ?COOKIE_STORE_ARGS). 109 110%%==================================================================== 111%% Internal functions 112%%==================================================================== 113-spec decode_cookie_contents(string(), list()) -> any() | {'error', any()}. 114decode_cookie_contents(Cookie0, Key) -> 115 case cookie_safe_decode(Cookie0) of 116 <<>> -> 117 {error, {decode_cookie_contents, no_data}}; 118 Cookie when is_binary(Cookie) -> 119 case ?ENCODER:decode(Key, Cookie) of 120 {error, _} = Error -> 121 Error; 122 Content when is_binary(Content) -> 123 case (catch(binary_to_term(Content))) of 124 {'EXIT', Error} -> 125 {error, {decode_cookie_contents, Error}}; 126 Session -> 127 Session 128 end 129 end 130 end.