<commit_before>-module(tts_credential).
-behaviour(gen_server).
-include("tts.hrl").

%% API.
-export([start_link/0]).
-export([
         get_list/1,
         request/4,
         revoke/2,
         security_incident/1,
         security_incident/2
        ]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).


-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

get_list(UserId) ->
    get_credential_list(UserId).

request(ServiceId, UserInfo, Token, Params) ->
    {ok, Pid} = tts_cred_sup:new_worker(),
    Result = tts_cred_worker:request(ServiceId,UserInfo,Params,Pid),
    handle_request_result(Result,ServiceId,UserInfo,Token).

revoke(ServiceId, UserInfo) ->
    #{uid := UserId } = UserInfo,
    case get_credential_state(UserId, ServiceId) of
        {ok, CredState} -> 
            {ok, Pid} = tts_cred_sup:new_worker(),
            Result = tts_cred_worker:revoke(ServiceId,UserInfo,CredState,Pid),
            handle_revoke_result(Result,ServiceId,UserInfo,CredState);
        Other -> Other
    end.

security_incident( UserInfo) ->
    %TODO: make this run in parallel
    #{uid := UserId } = UserInfo,
    {ok, List} = get_credential_states(UserId),
    Incident = fun({ServiceId, CredState},ResultList) ->
                       {ok, Pid} = tts_cred_sup:new_worker(),
                       Result = tts_cred_worker:security_incident(ServiceId,UserInfo,CredState,Pid),
                       [{Result,ServiceId,CredState} | ResultList]
               end,
    ResultList = lists:foldl(Incident,[],List),
    HandleResult = fun({Result,ServiceId,CredState},_) ->
                           handle_incident_result(Result,ServiceId,UserInfo,CredState)
                   end,
    lists:foldl(HandleResult,[],ResultList),
    ok.


security_incident(ServiceId, UserInfo) ->
    #{uid := UserId } = UserInfo,
    case get_credential_state(UserId, ServiceId) of
        {ok, CredState} -> 
            {ok, Pid} = tts_cred_sup:new_worker(),
            Result = tts_cred_worker:security_incident(ServiceId,UserInfo,CredState,Pid),
            handle_incident_result(Result,ServiceId,UserInfo,CredState);
        Other -> Other
    end.

handle_request_result({ok,#{credential := Cred} = CredMap},ServiceId,
                      #{uid := UserId},_Token) ->
    %TODO: ensure the user has no credential there (what about REST?)
    ok = store_credential_if_valid(UserId,ServiceId,CredMap),     
    {ok,Cred};
handle_request_result({error,_},_ServiceId,_UserInfo,_Token) ->
    ok.


handle_revoke_result({ok,#{result := Result}},ServiceId, #{uid := UserId}, CredState) ->
    ok = remove_credential(UserId,ServiceId,CredState),     
    {ok, Result};
handle_revoke_result({error,_},_ServiceId,_UserInfo,_CredState) ->
    ok.

handle_incident_result({ok,#{result := Result}},ServiceId, #{uid := UserId}, CredState) ->
    ok = remove_credential(UserId,ServiceId,CredState),     
    {ok, Result};
handle_incident_result({error,_},_ServiceId,_UserInfo,_CredState) ->
    ok.

store_credential_if_valid(_,_,#{error := _}) ->
    % if the credential contains the error key, do not store it as there 
    % has been an error during creation
    ok;
store_credential_if_valid(UserId,ServiceId,#{state := CredState}) ->
    sync_store_credential(UserId,ServiceId,CredState).

sync_store_credential(UserId,ServiceId,CredState) ->
    gen_server:call(?MODULE,{store_credential, UserId, ServiceId, CredState}).

-record(state, {
}).


init([]) ->
	{ok, #state{}}.

handle_call({store_credential, UserId, ServiceId, CredState}, _From, State) ->
    ok = store_credential(UserId,ServiceId,CredState),
    {reply, ok, State};
handle_call(_Request, _From, 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}.

% functions with data access
get_credential_list(UserId) ->
    {ok, List} = get_credential_states(UserId),
    CredList = [ ServiceId || {ServiceId, _CredState} <- List ], 
    {ok, CredList}.

get_credential_states(UserId) ->
    case tts_data:credential_get(UserId) of
        {error, _ } -> 
            {ok, []};
        {ok, List} ->
            {ok, List}
    end.



get_credential_state(UserId,ServiceId) ->
    case tts_data:credential_get(UserId) of
        {error, _ } = Error ->  Error;
        {ok, List} ->
            case lists:keyfind(ServiceId,1,List) of
                false -> {error, not_found};
                {ServiceId, CredState} -> {ok, CredState}
            end
    end.


store_credential(UserId,ServiceId,CredentialState) ->
     tts_data:credential_add(UserId,ServiceId,CredentialState).

remove_credential(UserId,ServiceId,CredentialState) ->
     tts_data:credential_remove(UserId,ServiceId,CredentialState).
<commit_msg>make decission if log should be returned or not<commit_after>-module(tts_credential).
-behaviour(gen_server).
-include("tts.hrl").

%% API.
-export([start_link/0]).
-export([
         get_list/1,
         request/4,
         revoke/2,
         security_incident/1,
         security_incident/2
        ]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).


-spec start_link() -> {ok, pid()}.
start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

get_list(UserId) ->
    get_credential_list(UserId).

request(ServiceId, UserInfo, Token, Params) ->
    {ok, Pid} = tts_cred_sup:new_worker(),
    Result = tts_cred_worker:request(ServiceId,UserInfo,Params,Pid),
    handle_request_result(Result,ServiceId,UserInfo,Token).

revoke(ServiceId, UserInfo) ->
    #{uid := UserId } = UserInfo,
    case get_credential_state(UserId, ServiceId) of
        {ok, CredState} -> 
            {ok, Pid} = tts_cred_sup:new_worker(),
            Result = tts_cred_worker:revoke(ServiceId,UserInfo,CredState,Pid),
            handle_revoke_result(Result,ServiceId,UserInfo,CredState);
        Other -> Other
    end.

security_incident( UserInfo) ->
    %TODO: make this run in parallel
    #{uid := UserId } = UserInfo,
    {ok, List} = get_credential_states(UserId),
    Incident = fun({ServiceId, CredState},ResultList) ->
                       {ok, Pid} = tts_cred_sup:new_worker(),
                       Result = tts_cred_worker:security_incident(ServiceId,UserInfo,CredState,Pid),
                       [{Result,ServiceId,CredState} | ResultList]
               end,
    ResultList = lists:foldl(Incident,[],List),
    HandleResult = fun({Result,ServiceId,CredState},_) ->
                           handle_incident_result(Result,ServiceId,UserInfo,CredState)
                   end,
    lists:foldl(HandleResult,[],ResultList),
    ok.


security_incident(ServiceId, UserInfo) ->
    #{uid := UserId } = UserInfo,
    case get_credential_state(UserId, ServiceId) of
        {ok, CredState} -> 
            {ok, Pid} = tts_cred_sup:new_worker(),
            Result = tts_cred_worker:security_incident(ServiceId,UserInfo,CredState,Pid),
            handle_incident_result(Result,ServiceId,UserInfo,CredState);
        Other -> Other
    end.

handle_request_result({ok,#{credential := Cred} = CredMap, Log},ServiceId,
                      #{uid := UserId},_Token) ->
    %TODO: ensure the user has no credential there (what about REST?)
    %TODO: write logs to file and pass the info to the user, so admins know
    %about it
    ok = store_credential_if_valid(UserId,ServiceId,CredMap),     
    case ?CONFIG(debug_mode) of
        true -> {ok,Cred,Log};
        _ -> {ok,Cred,[]}
    end;
handle_request_result({error,_,Log},_ServiceId,_UserInfo,_Token) ->
    Cred = false,
    case ?CONFIG(debug_mode) of
        true -> {ok,Cred,Log};
        _ -> {ok,Cred,[]}
    end.


handle_revoke_result({ok,#{result := Result}},ServiceId, #{uid := UserId}, CredState) ->
    ok = remove_credential(UserId,ServiceId,CredState),     
    {ok, Result};
handle_revoke_result({error,_},_ServiceId,_UserInfo,_CredState) ->
    ok.

handle_incident_result({ok,#{result := Result}},ServiceId, #{uid := UserId}, CredState) ->
    ok = remove_credential(UserId,ServiceId,CredState),     
    {ok, Result};
handle_incident_result({error,_},_ServiceId,_UserInfo,_CredState) ->
    ok.

store_credential_if_valid(_,_,#{error := _}) ->
    % if the credential contains the error key, do not store it as there 
    % has been an error during creation
    ok;
store_credential_if_valid(UserId,ServiceId,#{state := CredState}) ->
    sync_store_credential(UserId,ServiceId,CredState).

sync_store_credential(UserId,ServiceId,CredState) ->
    gen_server:call(?MODULE,{store_credential, UserId, ServiceId, CredState}).

-record(state, {
}).


init([]) ->
	{ok, #state{}}.

handle_call({store_credential, UserId, ServiceId, CredState}, _From, State) ->
    ok = store_credential(UserId,ServiceId,CredState),
    {reply, ok, State};
handle_call(_Request, _From, 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}.

% functions with data access
get_credential_list(UserId) ->
    {ok, List} = get_credential_states(UserId),
    CredList = [ ServiceId || {ServiceId, _CredState} <- List ], 
    {ok, CredList}.

get_credential_states(UserId) ->
    case tts_data:credential_get(UserId) of
        {error, _ } -> 
            {ok, []};
        {ok, List} ->
            {ok, List}
    end.



get_credential_state(UserId,ServiceId) ->
    case tts_data:credential_get(UserId) of
        {error, _ } = Error ->  Error;
        {ok, List} ->
            case lists:keyfind(ServiceId,1,List) of
                false -> {error, not_found};
                {ServiceId, CredState} -> {ok, CredState}
            end
    end.


store_credential(UserId,ServiceId,CredentialState) ->
     tts_data:credential_add(UserId,ServiceId,CredentialState).

remove_credential(UserId,ServiceId,CredentialState) ->
     tts_data:credential_remove(UserId,ServiceId,CredentialState).

{'fraction_non_alphanumeric': 0.14204215818990523, 'fraction_numerical': 0.0025140204989363758, 'mean_word_length': 3.367820945945946, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3706175', 'n_tokens_mistral': 3477, 'n_tokens_neox': 3437, 'n_words': 582}