<commit_before>%%%===================================================================
%%% @copyright (C) 2012, Erlang Solutions Ltd.
%%% @doc Module supporting various authentication mechanisms
%%% @end
%%%===================================================================
-module(escalus_auth).

%% Public APi
-export([auth_plain/2,
         auth_digest_md5/2,
         auth_sasl_anon/2,
         auth_sasl_external/2,
         auth_sasl_scram_sha1/2,
         auth_sasl_oauth/2]).

%% Useful helpers for writing own mechanisms
-export([get_challenge/2,
         wait_for_success/2]).

-include_lib("exml/include/exml.hrl").
-include("no_binary_to_integer.hrl").

%%--------------------------------------------------------------------
%% Public API
%%--------------------------------------------------------------------

auth_plain(Conn, Props) ->
    Username = get_property(username, Props),
    Password = get_property(password, Props),
    Payload = <<0:8,Username/binary,0:8,Password/binary>>,
    Stanza = escalus_stanza:auth(<<"PLAIN">>, [base64_cdata(Payload)]),
    ok = escalus_connection:send(Conn, Stanza),
    wait_for_success(Username, Conn).

auth_digest_md5(Conn, Props) ->
    ok = escalus_connection:send(Conn, escalus_stanza:auth(<<"DIGEST-MD5">>)),
    ChallengeData = get_challenge(Conn, challenge1),
    Response = md5_digest_response(ChallengeData, Props),
    ResponseStanza1 = escalus_stanza:auth_response([Response]),
    ok = escalus_connection:send(Conn, ResponseStanza1),
    [{<<"rspauth">>, _}] = get_challenge(Conn, challenge2), %% TODO: validate
    ResponseStanza2 = escalus_stanza:auth_response(),
    ok = escalus_connection:send(Conn, ResponseStanza2),
    wait_for_success(get_property(username, Props), Conn).

auth_sasl_scram_sha1(Conn, Props) ->
    Username = get_property(username, Props),
    Nonce = base64:encode(crypto:rand_bytes(16)),
    ClientFirstMessageBare = csvkv:format([{<<"n">>, Username},
                                           {<<"r">>, Nonce}],
                                          false),
    GS2Header = <<"n,,">>,
    Payload = <<GS2Header/binary,ClientFirstMessageBare/binary>>,
    Stanza = escalus_stanza:auth(<<"SCRAM-SHA-1">>, [base64_cdata(Payload)]),

    ok = escalus_connection:send(Conn, Stanza),

    {Response,
     SaltedPassword,
     AuthMessage} = scram_sha1_response(Conn, GS2Header,
                                        ClientFirstMessageBare, Props),
    ResponseStanza = escalus_stanza:auth_response([Response]),


    ok = escalus_connection:send(Conn, ResponseStanza),

    AuthReply = escalus_connection:get_stanza(Conn, auth_reply),
    case AuthReply of
        #xmlel{name = <<"success">>, children = [CData]} ->
            Unescaped = exml:unescape_cdata(CData),
            V = get_property(<<"v">>, csvkv:parse(base64:decode(Unescaped))),
            Decoded = base64:decode(V),
            ok = scram_sha1_validate_server(SaltedPassword, AuthMessage,
                                            Decoded);
        #xmlel{name = <<"failure">>} ->
            throw({auth_failed, Username, AuthReply})
    end.


auth_sasl_anon(Conn, Props) ->
    Stanza = escalus_stanza:auth(<<"ANONYMOUS">>),
    ok = escalus_connection:send(Conn, Stanza),
    wait_for_success(get_property(username, Props), Conn).

auth_sasl_external(Conn, Props) ->
    {server, ThisServer} = get_property(endpoint, Props),
    Stanza = escalus_stanza:auth(<<"EXTERNAL">>, [base64_cdata(ThisServer)]),
    ok = escalus_connection:send(Conn, Stanza),
    wait_for_success(ThisServer, Conn).

auth_sasl_oauth(Conn, Props) ->
    Token = get_property(oauth_token, Props),
    Children = [#xmlcdata{content = base64:encode(Token)}],
    Stanza = escalus_stanza:auth(<<"X-OAUTH">>, Children),
    ok = escalus_connection:send(Conn, Stanza),
    AuthReply = escalus_connection:get_stanza(Conn, auth_reply),
    NewProps = case AuthReply of
                   #xmlel{name = <<"success">>, children = ChildrenRecvd} ->
                       ([ {oauth_returned_token, base64:decode(exml:unescape_cdata(CData))}
                         || CData <- ChildrenRecvd ]
                        ++ Props);
                   #xmlel{name = <<"failure">>} -><commit_msg>Fix whitespace<commit_after>%%%===================================================================
%%% @copyright (C) 2012, Erlang Solutions Ltd.
%%% @doc Module supporting various authentication mechanisms
%%% @end
%%%===================================================================
-module(escalus_auth).

%% Public APi
-export([auth_plain/2,
         auth_digest_md5/2,
         auth_sasl_anon/2,
         auth_sasl_external/2,
         auth_sasl_scram_sha1/2,
         auth_sasl_oauth/2]).

%% Useful helpers for writing own mechanisms
-export([get_challenge/2,
         wait_for_success/2]).

-include_lib("exml/include/exml.hrl").
-include("no_binary_to_integer.hrl").

%%--------------------------------------------------------------------
%% Public API
%%--------------------------------------------------------------------

auth_plain(Conn, Props) ->
    Username = get_property(username, Props),
    Password = get_property(password, Props),
    Payload = <<0:8,Username/binary,0:8,Password/binary>>,
    Stanza = escalus_stanza:auth(<<"PLAIN">>, [base64_cdata(Payload)]),
    ok = escalus_connection:send(Conn, Stanza),
    wait_for_success(Username, Conn).

auth_digest_md5(Conn, Props) ->
    ok = escalus_connection:send(Conn, escalus_stanza:auth(<<"DIGEST-MD5">>)),
    ChallengeData = get_challenge(Conn, challenge1),
    Response = md5_digest_response(ChallengeData, Props),
    ResponseStanza1 = escalus_stanza:auth_response([Response]),
    ok = escalus_connection:send(Conn, ResponseStanza1),
    [{<<"rspauth">>, _}] = get_challenge(Conn, challenge2), %% TODO: validate
    ResponseStanza2 = escalus_stanza:auth_response(),
    ok = escalus_connection:send(Conn, ResponseStanza2),
    wait_for_success(get_property(username, Props), Conn).

auth_sasl_scram_sha1(Conn, Props) ->
    Username = get_property(username, Props),
    Nonce = base64:encode(crypto:rand_bytes(16)),
    ClientFirstMessageBare = csvkv:format([{<<"n">>, Username},
                                           {<<"r">>, Nonce}],
                                          false),
    GS2Header = <<"n,,">>,
    Payload = <<GS2Header/binary,ClientFirstMessageBare/binary>>,
    Stanza = escalus_stanza:auth(<<"SCRAM-SHA-1">>, [base64_cdata(Payload)]),

    ok = escalus_connection:send(Conn, Stanza),

    {Response,
     SaltedPassword,
     AuthMessage} = scram_sha1_response(Conn, GS2Header,
                                        ClientFirstMessageBare, Props),
    ResponseStanza = escalus_stanza:auth_response([Response]),


    ok = escalus_connection:send(Conn, ResponseStanza),

    AuthReply = escalus_connection:get_stanza(Conn, auth_reply),
    case AuthReply of
        #xmlel{name = <<"success">>, children = [CData]} ->
            Unescaped = exml:unescape_cdata(CData),
            V = get_property(<<"v">>, csvkv:parse(base64:decode(Unescaped))),
            Decoded = base64:decode(V),
            ok = scram_sha1_validate_server(SaltedPassword, AuthMessage,
                                            Decoded);
        #xmlel{name = <<"failure">>} ->
            throw({auth_failed, Username, AuthReply})
    end.


auth_sasl_anon(Conn, Props) ->
    Stanza = escalus_stanza:auth(<<"ANONYMOUS">>),
    ok = escalus_connection:send(Conn, Stanza),
    wait_for_success(get_property(username, Props), Conn).

auth_sasl_external(Conn, Props) ->
    {server, ThisServer} = get_property(endpoint, Props),
    Stanza = escalus_stanza:auth(<<"EXTERNAL">>, [base64_cdata(ThisServer)]),
    ok = escalus_connection:send(Conn, Stanza),
    wait_for_success(ThisServer, Conn).

auth_sasl_oauth(Conn, Props) ->
    Token = get_property(oauth_token, Props),
    Children = [#xmlcdata{content = base64:encode(Token)}],
    Stanza = escalus_stanza:auth(<<"X-OAUTH">>, Children),
    ok = escalus_connection:send(Conn, Stanza),
    AuthReply = escalus_connection:get_stanza(Conn, auth_reply),
    NewProps = case AuthReply of
                   #xmlel{name = <<"success">>, children = ChildrenRecvd} ->
                       ([ {oauth_returned_token, base64:decode(exml:unescape_cdata(CData))}
                          || CData <- ChildrenRecvd ]
                        ++ Props);
                   #xmlel{name = <<"failure">>} ->
{'fraction_non_alphanumeric': 0.19688017017253603, 'fraction_numerical': 0.01229023871425195, 'mean_word_length': 3.012802275960171, 'pattern_counts': {'":': 0, '<': 73, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 18}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '194678', 'n_tokens_mistral': 2743, 'n_tokens_neox': 2629, 'n_words': 401}