Skip to content

Instantly share code, notes, and snippets.

@inklesspen
Created July 6, 2009 19:28
Show Gist options
  • Save inklesspen/141632 to your computer and use it in GitHub Desktop.
Save inklesspen/141632 to your computer and use it in GitHub Desktop.
%% @author Jon Rosebaugh <jon@inklesspen.com>
%% @copyright YYYY author.
%% @doc TEMPLATE.
-module(sessions).
-author('Jon Rosebaugh <jon@inklesspen.com>').
-export([create_tables/0, load_session/1, save_session/1]).
% These should probably be turned into arguments to the module at some point.
-define (COOKIE_NAME, "erl_session_id").
-define (SECRET, <<"foo">>).
-define (COOKIE_LIFETIME, 60*60*24*7).
-record (session_store, {session_id, props}).
create_tables() ->
mnesia:create_table(session_store, [{disc_copies, [node()]}, {attributes, record_info(fields, session_store)}]).
load_session(Req) ->
Cookie = Req:get_cookie_value(?COOKIE_NAME),
case Cookie of
undefined ->
% No session stored. Return empty proplist.
make_new_session(not_found_in_cookie);
_ ->
% We may have a stored session.
% First, validate the cookie. crypto should be started; we're going to assume it has been.
{StoredHexHash, StoredId} = lists:split(40, Cookie),
StoredHash = hexstr_to_bin(StoredHexHash),
ComputedHash = crypto:sha_mac(?SECRET, StoredId),
case ComputedHash of
StoredHash ->
% stored id is valid. look it up.
FoundSession = load_session_from_mnesia(StoredId),
case FoundSession of
not_found ->
make_new_session(not_found_in_mnesia); % couldn't find it, make a new one
_ ->
FoundSession
end;
_ ->
% stored id is not valid. return an empty proplist
make_new_session(invalid)
end
end.
save_session(Session) ->
% retrieve the session id, update the updated_at timestamp
Meta = proplists:get_value(meta, Session),
SessionId = proplists:get_value(session_id, Meta),
Meta1 = lists:keystore(updated_at, 1, Meta, {updated_at, now()}),
Session1 = lists:keystore(meta, 1, Session, {meta, Meta1}),
save_session_to_mnesia(SessionId, Session1),
% generate the cookie value
ComputedHash = crypto:sha_mac(?SECRET, SessionId),
HexHash = bin_to_hexstr(ComputedHash),
CookieValue = HexHash++SessionId,
Cookie = mochiweb_cookies:cookie(?COOKIE_NAME, CookieValue, [{path, "/"}, {max_age, ?COOKIE_LIFETIME}]),
Cookie.
load_session_from_mnesia(StoredId) ->
F = fun() ->
R = mnesia:read(session_store, StoredId),
case R of
[] -> not_found;
[U] -> U
end
end,
{atomic, Session} = mnesia:transaction(F),
Session.
save_session_to_mnesia(SessionId, Session) ->
R = #session_store{session_id=SessionId, props=Session},
F = fun() ->
mnesia:write(R)
end,
mnesia:transaction(F).
make_new_session(_Reason) ->
[{meta, [{session_id, generate_session_id()}, {created_at, now()}]}].
generate_session_id() ->
bin_to_hexstr(crypto:sha(term_to_binary({make_ref(), crypto:rand_bytes(10), now()}))).
% Taken from http://necrobious.blogspot.com/2008/03/binary-to-hex-string-back-to-binary-in.html#c7118319204757807557
bin_to_hexstr(Bin) ->
lists:flatten([io_lib:format("~2.16.0B", [X]) || X <- binary_to_list(Bin)]).
hexstr_to_bin(S) ->
hexstr_to_bin(S, []).
hexstr_to_bin([], Acc) ->
list_to_binary(lists:reverse(Acc));
hexstr_to_bin([X,Y|T], Acc) ->
{ok, [V], []} = io_lib:fread("~16u", [X,Y]),
hexstr_to_bin(T, [V | Acc]).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment