Current section
Files
Jump to
Current section
Files
src/ow_token_serv.erl
-module(ow_token_serv).
-behaviour(gen_server).
-export([
start_link/0,
new/1,
exchange/1
]).
%% gen_server callbacks
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
-define(MAX_BYTES, 16).
-type token() :: binary().
-record(state, {
tokens = [] :: [{pid(), token()}]
}).
%%----------------------------------------------------------------------------
%% @doc Starts the token server.
%% @end
%%----------------------------------------------------------------------------
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%%----------------------------------------------------------------------------
%% @doc Generates a new token for the given session PID.
%% @end
%%----------------------------------------------------------------------------
-spec new(pid()) -> token().
new(SessionPID) ->
gen_server:call(?MODULE, {new, SessionPID}).
%%----------------------------------------------------------------------------
%% @doc Exchanges a token for a new one if it matches an existing Session PID.
%% @end
%%----------------------------------------------------------------------------
-spec exchange(token()) -> {pid(), token()} | false | denied.
exchange(Token) ->
gen_server:call(?MODULE, {exchange, Token}).
%%===========================================================================
%% Callback implementation
%%===========================================================================
init([]) ->
{ok, #state{}}.
handle_call({new, ID}, _From, #state{tokens = Tokens} = State) ->
Token = create_token(),
Tokens1 = [{ID, Token} | Tokens],
{reply, Token, State#state{tokens = Tokens1}};
handle_call({exchange, Token}, _From, #state{tokens = Tokens} = State) ->
case lists:keyfind(Token, 2, Tokens) of
false ->
{reply, false, State};
{ID, Token} ->
% Token matches via pattern matching, give the user a new token
NewToken = create_token(),
Tokens1 = lists:keyreplace(ID, 1, Tokens, {ID, NewToken}),
{reply, {ID, NewToken}, State#state{tokens = Tokens1}};
{_, _} ->
{reply, denied, State}
end;
handle_call(_, _, State) ->
{reply, ignored, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%===========================================================================
%% Internal functions
%%===========================================================================
-spec create_token() -> token().
create_token() ->
crypto:strong_rand_bytes(?MAX_BYTES).