Current section

Files

Jump to
oidcc src oidcc_session_mgr.erl
Raw

src/oidcc_session_mgr.erl

-module(oidcc_session_mgr).
%%
%% Copyright 2016 SCC/KIT
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0 (see also the LICENSE file)
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
-author("Bas Wegh, Bas.Wegh<at>kit.edu").
-behaviour(gen_server).
%% API.
-export([start_link/0]).
-export([stop/0]).
-export([new_session/1]).
-export([get_session/1]).
-export([close_all_sessions/0]).
-export([get_session_list/0]).
-export([session_terminating/1]).
%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-record(state, {
sessions = []
}).
%% API.
-spec start_link() -> {ok, pid()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-spec stop() -> ok.
stop() ->
gen_server:cast(?MODULE, stop).
-spec new_session(binary()) -> {ok, pid()}.
new_session(ProviderId) ->
gen_server:call(?MODULE, {new_session, ProviderId}).
-spec get_session(ID :: uuid:uuid()) -> {ok, pid()}.
get_session(ID) ->
gen_server:call(?MODULE, {get_session, ID}).
-spec session_terminating(ID :: binary()) -> ok.
session_terminating(ID) ->
gen_server:call(?MODULE, {delete_session, ID}).
-spec close_all_sessions() -> ok.
close_all_sessions() ->
gen_server:call(?MODULE, close_all_sessions).
-spec get_session_list() -> {ok, Sessions::list()}.
get_session_list() ->
gen_server:call(?MODULE, get_session_list).
%% gen_server.
init([]) ->
{ok, #state{}}.
handle_call({new_session, ProviderId}, _From, State) ->
{ok, Pid, NewState} = create_new_session(ProviderId, State),
{reply, {ok, Pid}, NewState};
handle_call({get_session, Id}, _From, State) ->
Result = lookup_session(Id, State),
{reply, Result, State};
handle_call({delete_session, ID}, _From, State) ->
{ok, NewState} = delete_session(ID, State),
{reply, ok, NewState};
handle_call(close_all_sessions, _From, State) ->
{ok, NewState} = delete_sessions(State),
{reply, ok, NewState};
handle_call(get_session_list, _From, State) ->
SessionList = session_list(State),
{reply, {ok, SessionList}, State};
handle_call(_Request, _From, State) ->
{reply, ignored, State}.
handle_cast(stop, State) ->
{ok, NewState} = delete_sessions(State),
{stop, normal, NewState};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
delete_sessions(#state{sessions = Sessions} = State) ->
{ok, NewState} = delete_sessions(Sessions, State),
{ok, NewState}.
delete_sessions([], State) ->
{ok, State#state{sessions = []}};
delete_sessions([{_Id, Pid}|T], State) ->
oidcc_session:close(Pid),
delete_sessions(T, State).
set_session_for_id(ID, Pid, #state{sessions = Sessions} = State) ->
{ok, State#state{sessions = [ {ID, Pid} | Sessions]}}.
delete_session(Id, #state{sessions = Sessions} = State) ->
NewSessions = lists:keydelete(Id, 1, Sessions),
{ok, State#state{sessions = NewSessions}}.
get_unique_id(#state{sessions = List}) ->
get_unique_id(List);
get_unique_id(List) ->
ID = random_string(64),
repeat_id_gen_if_needed(ID, lists:keyfind(ID, 1, List), List).
repeat_id_gen_if_needed(ID, false, _) ->
ID;
repeat_id_gen_if_needed(_, _, List) ->
get_unique_id(List).
lookup_session(Id, #state{sessions=Sessions}) ->
case lists:keyfind(Id, 1, Sessions) of
{Id, Pid} ->
{ok, Pid};
_ ->
{error, not_found}
end.
session_list(#state{sessions=Sessions}) ->
Sessions.
create_new_session(ProviderId, State) ->
ID = get_unique_id(State),
create_new_session(ID, ProviderId, State).
create_new_session(ID, ProviderId, State) ->
Pid = start_session(ID, ProviderId),
{ok, NewState} = set_session_for_id(ID, Pid, State),
{ok, Pid, NewState}.
start_session(Id, ProviderId) ->
Nonce = random_string(128),
{ok, Pid} = oidcc_session_sup:new_session(Id, Nonce, ProviderId),
Pid.
random_string(Length) ->
base64url:encode(crypto:strong_rand_bytes(Length)).