Current section

Files

Jump to
cloudi_service_oauth1 src cloudi_service_oauth1_db_riak.erl
Raw

src/cloudi_service_oauth1_db_riak.erl

%-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-
% ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod:
%%%
%%%------------------------------------------------------------------------
%%% @doc
%%% ==CloudI OAuth v1.0 Riak DB Interface==
%%% @end
%%%
%%% MIT License
%%%
%%% Copyright (c) 2014-2020 Michael Truog <mjtruog at protonmail dot com>
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a
%%% copy of this software and associated documentation files (the "Software"),
%%% to deal in the Software without restriction, including without limitation
%%% the rights to use, copy, modify, merge, publish, distribute, sublicense,
%%% and/or sell copies of the Software, and to permit persons to whom the
%%% Software is furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
%%% DEALINGS IN THE SOFTWARE.
%%%
%%% @author Michael Truog <mjtruog at protonmail dot com>
%%% @copyright 2014-2020 Michael Truog
%%% @version 2.0.1 {@date} {@time}
%%%------------------------------------------------------------------------
-module(cloudi_service_oauth1_db_riak).
-author('mjtruog at protonmail dot com').
-behaviour(cloudi_service_oauth1_db).
%% external interface
-export([initialize/3,
signature_methods/5,
tokens_clean/2,
token_request_check/6,
token_request_store/14,
token_request_find/4,
token_request_update/5,
token_request_verify/10,
token_request_delete/4,
token_access_store/13,
token_access_verify/9]).
-define(RIAK_BUCKET_CONFIGURATION, "oauth_configuration").
-define(RIAK_BUCKET_TOKEN_REQUEST, "oauth_token_request").
-define(RIAK_BUCKET_TOKEN_ACCESS, "oauth_token_access").
%%%------------------------------------------------------------------------
%%% External interface functions
%%%------------------------------------------------------------------------
-spec initialize(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Debug :: boolean()) ->
ok | {error, any()}.
initialize(Dispatcher, Database, Debug) ->
if
Debug =:= true ->
riak_configuration_rfc5849_test_data(Dispatcher, Database);
Debug =:= false ->
ok
end.
-spec signature_methods(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Realm :: string() | binary() | null,
ConsumerKey :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
{ok, {PLAINTEXT :: binary() | null,
HMAC_SHA1 :: binary() | null,
RSA_SHA1 :: binary() | null}, CallbackRegex :: binary()} |
{error, any()}.
signature_methods(Dispatcher, Database, Realm, ConsumerKey, Timeout) ->
riak_signature_methods(Dispatcher, Database, Realm, ConsumerKey, Timeout).
-spec tokens_clean(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name()) ->
ok | {error, any()}.
tokens_clean(Dispatcher, Database) ->
riak_tokens_clean(Dispatcher, Database).
-spec token_request_check(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Realm :: string() | binary() | null,
ConsumerKey :: string() | binary(),
NonceRequest :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
ok | {error, any()}.
token_request_check(Dispatcher, Database,
Realm, ConsumerKey, NonceRequest, Timeout) ->
riak_token_request_check(Dispatcher, Database,
Realm, ConsumerKey, NonceRequest, Timeout).
-spec token_request_store(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Realm :: string() | binary() | null,
ConsumerKey :: string() | binary(),
SignatureMethod :: string() | binary(),
ClientSharedSecret :: string() | binary(),
Timestamp :: string() | binary(),
NonceRequest :: string() | binary(),
TokenRequest :: string() | binary(),
TokenRequestSecret :: string() | binary(),
CallbackURL :: string() | binary(),
CallbackQS :: string() | binary(),
ExpirationSeconds :: pos_integer(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
ok | {error, any()}.
token_request_store(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, TokenRequest, TokenRequestSecret,
CallbackURL, CallbackQS, ExpirationSeconds, Timeout) ->
riak_token_request_store(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
ClientSharedSecret, Timestamp,
NonceRequest, TokenRequest, TokenRequestSecret,
CallbackURL, CallbackQS,
ExpirationSeconds, Timeout).
-spec token_request_find(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
TokenRequest :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
{ok, CallbackURL :: binary(), CallbackQS :: binary()} |
{error, any()}.
token_request_find(Dispatcher, Database, TokenRequest, Timeout) ->
riak_token_request_find(Dispatcher, Database, TokenRequest, Timeout).
-spec token_request_update(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
TokenRequest :: string() | binary(),
Verifier :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
ok | {error, any()}.
token_request_update(Dispatcher, Database, TokenRequest, Verifier, Timeout) ->
riak_token_request_update(Dispatcher, Database, TokenRequest,
Verifier, Timeout).
-spec token_request_verify(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Realm :: string() | binary() | null,
ConsumerKey :: string() | binary(),
SignatureMethod :: string() | binary(),
Timestamp :: string() | binary(),
NonceAccess :: string() | binary(),
TokenRequest :: string() | binary(),
Verifier :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
{ok, ClientSharedSecret :: binary(), NonceRequest :: binary(),
TokenRequestSecret :: binary()} |
{error, any()}.
token_request_verify(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
Timestamp, NonceRequest,
TokenRequest, Verifier, Timeout) ->
riak_token_request_verify(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
Timestamp, NonceRequest,
TokenRequest, Verifier, Timeout).
-spec token_request_delete(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
TokenRequest :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
ok | {error, any()}.
token_request_delete(Dispatcher, Database, TokenRequest, Timeout) ->
riak_token_request_delete(Dispatcher, Database, TokenRequest, Timeout).
-spec token_access_store(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Realm :: string() | binary() | null,
ConsumerKey :: string() | binary(),
SignatureMethod :: string() | binary(),
ClientSharedSecret :: string() | binary(),
Timestamp :: string() | binary(),
NonceRequest :: string() | binary(),
NonceAccess :: string() | binary(),
TokenAccess :: string() | binary(),
TokenAccessSecret :: string() | binary(),
ExpirationSeconds :: pos_integer(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
ok | {error, any()}.
token_access_store(Dispatcher, Database,
Realm, ConsumerKey,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess,
TokenAccess, TokenAccessSecret,
ExpirationSeconds, Timeout) ->
riak_token_access_store(Dispatcher, Database,
Realm, ConsumerKey,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess,
TokenAccess, TokenAccessSecret,
ExpirationSeconds, Timeout).
-spec token_access_verify(Dispatcher :: cloudi_service:dispatcher(),
Database :: cloudi_service:service_name(),
Realm :: string() | binary() | null,
ConsumerKey :: string() | binary(),
SignatureMethod :: string() | binary(),
Timestamp :: string() | binary(),
Nonce :: string() | binary(),
TokenAccess :: string() | binary(),
Timeout :: cloudi_service:timeout_milliseconds()) ->
{ok, ClientSharedSecret :: binary(), TokenAccessSecret :: binary()} |
{error, any()}.
token_access_verify(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
Timestamp, Nonce, TokenAccess, Timeout) ->
riak_token_access_verify(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
Timestamp, Nonce, TokenAccess, Timeout).
%%%------------------------------------------------------------------------
%%% Private functions
%%%------------------------------------------------------------------------
riak_configuration_rfc5849_test_data(Dispatcher, Database) ->
% from http://tools.ietf.org/html/rfc5849#section-1.2
% (i.e., printer.example.com client with
% photos.example.net server)
Name = Database ++ ?RIAK_BUCKET_CONFIGURATION,
Key = consumer_key(<<"dpf43f3p2l4k3l03">>, <<"photos">>),
Value = configuration_to_json("dpf43f3p2l4k3l03", "photos",
null, "kd94hf93k423kf44", null,
"https?://printer.example.com/ready"),
case cloudi_service_db_riak:new(Dispatcher, Name,
Key, Value, undefined) of
{{ok, _, _}, Dispatcher} ->
ok;
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_expirations(Dispatcher, Name, Now) ->
case cloudi_service_db_riak:get_index_range(Dispatcher, Name,
{binary_index, "expiration"},
<<"1970">>, Now, undefined) of
{{ok, Keys, _, _}, Dispatcher} ->
{ok, Keys};
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_delete(_, _, []) ->
ok;
riak_delete(Dispatcher, Name, [Key | Keys]) ->
case cloudi_service_db_riak:delete(Dispatcher, Name, Key, undefined) of
{ok, Dispatcher} ->
riak_delete(Dispatcher, Name, Keys);
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_tokens_clean(Dispatcher, Database) ->
Now = timestamp(),
NameRequest = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
NameAccess = Database ++ ?RIAK_BUCKET_TOKEN_ACCESS,
case riak_expirations(Dispatcher, NameRequest, Now) of
{ok, KeysRequest} ->
case riak_delete(Dispatcher, NameRequest, KeysRequest) of
ok ->
case riak_expirations(Dispatcher, NameAccess, Now) of
{ok, KeysAccess} ->
case riak_delete(Dispatcher, NameAccess,
KeysAccess) of
ok ->
ok;
{error, _} = Error ->
Error
end;
{error, _} = Error ->
Error
end;
{error, _} = Error ->
Error
end;
{error, _} = Error ->
Error
end.
riak_signature_methods(Dispatcher, Database, Realm, ConsumerKey, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_CONFIGURATION,
Key = consumer_key(ConsumerKey, match_realm(Realm)),
case cloudi_service_db_riak:get(Dispatcher, Name, Key, Timeout) of
{{ok, Key, Value}, Dispatcher} ->
{_, _,
SignatureMethodPlaintext,
SignatureMethodHMACSHA1,
SignatureMethodRSASHA1,
CallbackRegex} = json_to_configuration(Value),
{ok, {SignatureMethodPlaintext,
SignatureMethodHMACSHA1,
SignatureMethodRSASHA1}, CallbackRegex};
{{error, notfound}, Dispatcher} ->
{error, not_found};
{{siblings, _, _}, Dispatcher} ->
{error, siblings};
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_nonce_check(Dispatcher, Name, Key, Nonce, Timeout) ->
case cloudi_service_db_riak:get(Dispatcher, Name, Key, Timeout) of
{{ok, Key, Value}, Dispatcher} ->
JSON = jsx:decode(Value, [{return_maps, false}]),
case lists:keyfind(<<"nonce_request">>, 1, JSON) of
{_, Nonce} ->
{error, nonce_exists};
false ->
case lists:keyfind(<<"nonce_access">>, 1, JSON) of
{_, Nonce} ->
{error, nonce_exists};
false ->
ok
end
end;
{{error, notfound}, Dispatcher} ->
ok;
{{siblings, _, _}, Dispatcher} ->
{error, siblings};
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_request_check(Dispatcher, Database,
Realm, ConsumerKey, NonceRequest, Timeout) ->
NameRequest = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
NameAccess = Database ++ ?RIAK_BUCKET_TOKEN_ACCESS,
Key = consumer_key(ConsumerKey, match_realm(Realm)),
NonceRequestMatch = match_value(NonceRequest),
case riak_token_nonce_check(Dispatcher, NameRequest,
Key, NonceRequestMatch, Timeout) of
ok ->
case riak_token_nonce_check(Dispatcher, NameAccess,
Key, NonceRequestMatch, Timeout) of
ok ->
ok;
{error, _} = Error ->
Error
end;
{error, _} = Error ->
Error
end.
riak_token_request_store(Dispatcher, Database,
Realm, ConsumerKey,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest,
TokenRequest, TokenRequestSecret,
CallbackURL, CallbackQS,
ExpirationSeconds, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
ConsumerKeyMatch = match_value(ConsumerKey),
RealmMatch = match_realm(Realm),
Expiration = timestamp(ExpirationSeconds),
TokenRequestMatch = match_value(TokenRequest),
Key = TokenRequestMatch,
Value = token_request_to_json(TokenRequestMatch, TokenRequestSecret,
ConsumerKeyMatch, RealmMatch,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest,
CallbackURL, CallbackQS,
null, Expiration),
Options = [{indexes,
token_indexes(ConsumerKeyMatch, RealmMatch, Expiration)}],
case cloudi_service_db_riak:new(Dispatcher, Name,
Key, Value, Options, Timeout) of
{{ok, _, _}, Dispatcher} ->
ok;
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_request_find(Dispatcher, Database, TokenRequest, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
Key = match_value(TokenRequest),
case cloudi_service_db_riak:get(Dispatcher, Name, Key, Timeout) of
{{ok, Key, Value}, Dispatcher} ->
JSON = jsx:decode(Value, [{return_maps, false}]),
{_, CallbackURL} = lists:keyfind(<<"callback_url">>, 1, JSON),
{_, CallbackQS} = lists:keyfind(<<"callback_qs">>, 1, JSON),
{ok, CallbackURL, CallbackQS};
{{error, notfound}, Dispatcher} ->
{error, not_found};
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_request_update(Dispatcher, Database, TokenRequest,
Verifier, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
Key = match_value(TokenRequest),
case cloudi_service_db_riak:get(Dispatcher, Name, Key,
[{object, true}], Timeout) of
{{ok, Key, Object}, Dispatcher} ->
case cloudi_service_db_riak:object_value(Object) of
{ok, Key, Value} ->
{TokenRequest, TokenRequestSecret,
ConsumerKey, Realm, SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, CallbackURL, CallbackQS,
_, Expiration} = json_to_token_request(Value),
NewValue = token_request_to_json(TokenRequest,
TokenRequestSecret, ConsumerKey, Realm,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, CallbackURL, CallbackQS,
Verifier, Expiration),
NewObject = cloudi_service_db_riak:object_update(Object,
NewValue),
case cloudi_service_db_riak:put(Dispatcher, Name,
NewObject, Timeout) of
{{ok, _, _}, Dispatcher} ->
ok;
{{error, _} = Error, Dispatcher} ->
Error
end;
{siblings, _, _} ->
{error, siblings};
{error, _} = Error ->
Error
end;
{{error, notfound}, Dispatcher} ->
{error, not_found};
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_request_verify(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
TimestampNow, NonceAccess,
TokenRequest, Verifier, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
TokenRequestMatch = match_value(TokenRequest),
Key = TokenRequestMatch,
case cloudi_service_db_riak:get(Dispatcher, Name, Key, Timeout) of
{{ok, Key, Value}, Dispatcher} ->
ConsumerKeyMatch = match_value(ConsumerKey),
RealmMatch = match_realm(Realm),
SignatureMethodMatch = match_value(SignatureMethod),
TimestampNowMatch = match_value(TimestampNow),
NonceAccessMatch = match_value(NonceAccess),
VerifierMatch = match_value(Verifier),
case json_to_token_request(Value) of
{TokenRequestMatch, TokenRequestSecret,
ConsumerKeyMatch, RealmMatch,
SignatureMethodMatch, ClientSharedSecret,
Timestamp, NonceRequest, _CallbackURL, _CallbackQS,
VerifierMatch, _Expiration}
when NonceRequest /= NonceAccessMatch,
Timestamp < TimestampNowMatch ->
{ok, ClientSharedSecret, NonceRequest, TokenRequestSecret};
_ ->
{error, not_found}
end;
{{siblings, _, _}, Dispatcher} ->
{error, siblings};
{{error, notfound}, Dispatcher} ->
{error, not_found};
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_request_delete(Dispatcher, Database, TokenRequest, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_REQUEST,
Key = match_value(TokenRequest),
case cloudi_service_db_riak:delete(Dispatcher, Name,
Key, Timeout) of
{ok, Dispatcher} ->
ok;
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_access_store(Dispatcher, Database,
Realm, ConsumerKey,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess,
TokenAccess, TokenAccessSecret,
ExpirationSeconds, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_ACCESS,
ConsumerKeyMatch = match_value(ConsumerKey),
RealmMatch = match_realm(Realm),
Expiration = timestamp(ExpirationSeconds),
TokenAccessMatch = match_value(TokenAccess),
Key = TokenAccessMatch,
Value = token_access_to_json(TokenAccessMatch, TokenAccessSecret,
ConsumerKeyMatch, RealmMatch,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess,
Expiration),
Options = [{indexes,
token_indexes(ConsumerKeyMatch, RealmMatch, Expiration)}],
case cloudi_service_db_riak:new(Dispatcher, Name,
Key, Value, Options, Timeout) of
{{ok, _, _}, Dispatcher} ->
ok;
{{error, _} = Error, Dispatcher} ->
Error
end.
riak_token_access_verify(Dispatcher, Database,
Realm, ConsumerKey, SignatureMethod,
TimestampNow, Nonce, TokenAccess, Timeout) ->
Name = Database ++ ?RIAK_BUCKET_TOKEN_ACCESS,
TokenAccessMatch = match_value(TokenAccess),
Key = TokenAccessMatch,
case cloudi_service_db_riak:get(Dispatcher, Name, Key, Timeout) of
{{ok, Key, Value}, Dispatcher} ->
ConsumerKeyMatch = match_value(ConsumerKey),
RealmMatch = match_realm(Realm),
SignatureMethodMatch = match_value(SignatureMethod),
TimestampNowMatch = match_value(TimestampNow),
NonceMatch = match_value(Nonce),
case json_to_token_access(Value) of
{TokenAccessMatch, TokenAccessSecret,
ConsumerKeyMatch, RealmMatch,
SignatureMethodMatch, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess, _Expiration}
when NonceRequest /= NonceMatch,
NonceAccess /= NonceMatch,
Timestamp < TimestampNowMatch ->
{ok, ClientSharedSecret, TokenAccessSecret};
_ ->
{error, not_found}
end;
{{siblings, _, _}, Dispatcher} ->
{error, siblings};
{{error, notfound}, Dispatcher} ->
{error, not_found};
{{error, _} = Error, Dispatcher} ->
Error
end.
match_value(Value) ->
if
is_list(Value) ->
erlang:list_to_binary(Value);
is_binary(Value) ->
Value
end.
match_value_nullable(Value) ->
if
Value =:= null ->
null;
is_list(Value) ->
erlang:list_to_binary(Value);
is_binary(Value) ->
Value
end.
match_realm(Realm) ->
if
Realm =:= null ->
null;
is_binary(Realm) ->
cloudi_string:lowercase(Realm);
is_list(Realm) ->
erlang:list_to_binary(cloudi_string:lowercase(Realm))
end.
consumer_key(ConsumerKey, Realm) ->
RealmValue = if
Realm =:= null ->
<<"'null'">>;
is_binary(Realm) ->
Realm
end,
erlang:iolist_to_binary([ConsumerKey, $,, RealmValue]).
token_indexes(ConsumerKey, Realm, Expiration)
when is_binary(Expiration) ->
[{{binary_index, "consumer_key,realm"},
[consumer_key(ConsumerKey, Realm)]},
{{binary_index, "expiration"},
[Expiration]}].
configuration_to_json(ConsumerKey, Realm,
SignatureMethodPlaintext, SignatureMethodHMACSHA1,
SignatureMethodRSASHA1, CallbackRegex) ->
ConsumerKeyValue = match_value(ConsumerKey),
RealmValue = match_value_nullable(Realm),
SignatureMethodPlaintextValue =
match_value_nullable(SignatureMethodPlaintext),
SignatureMethodHMACSHA1Value =
match_value_nullable(SignatureMethodHMACSHA1),
SignatureMethodRSASHA1Value =
match_value_nullable(SignatureMethodRSASHA1),
CallbackRegexValue = match_value(CallbackRegex),
jsx:encode([{<<"consumer_key">>,
ConsumerKeyValue},
{<<"realm">>,
RealmValue},
{<<"signature_method_plaintext">>,
SignatureMethodPlaintextValue},
{<<"signature_method_hmac_sha1">>,
SignatureMethodHMACSHA1Value},
{<<"signature_method_rsa_sha1">>,
SignatureMethodRSASHA1Value},
{<<"callback_regex">>,
CallbackRegexValue}]).
json_to_configuration(Value)
when is_binary(Value) ->
JSON0 = jsx:decode(Value, [{return_maps, false}]),
{value, {_, ConsumerKey},
JSON1} = lists:keytake(<<"consumer_key">>, 1, JSON0),
{value, {_, Realm},
JSON2} = lists:keytake(<<"realm">>, 1, JSON1),
{value, {_, SignatureMethodPlaintext},
JSON3} = lists:keytake(<<"signature_method_plaintext">>, 1, JSON2),
{value, {_, SignatureMethodHMACSHA1},
JSON4} = lists:keytake(<<"signature_method_hmac_sha1">>, 1, JSON3),
{value, {_, SignatureMethodRSASHA1},
JSON5} = lists:keytake(<<"signature_method_rsa_sha1">>, 1, JSON4),
{value, {_, CallbackRegex},
[]} = lists:keytake(<<"callback_regex">>, 1, JSON5),
{ConsumerKey, Realm, SignatureMethodPlaintext,
SignatureMethodHMACSHA1, SignatureMethodRSASHA1, CallbackRegex}.
token_request_to_json(TokenRequest, TokenRequestSecret,
ConsumerKey, Realm,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, CallbackURL, CallbackQS,
Verifier, Expiration) ->
TokenRequestValue = match_value(TokenRequest),
TokenRequestSecretValue = match_value(TokenRequestSecret),
ConsumerKeyValue = match_value(ConsumerKey),
RealmValue = match_value_nullable(Realm),
SignatureMethodValue = match_value(SignatureMethod),
ClientSharedSecretValue = match_value(ClientSharedSecret),
TimestampValue = match_value(Timestamp),
NonceRequestValue = match_value(NonceRequest),
CallbackURLValue = match_value(CallbackURL),
CallbackQSValue = match_value(CallbackQS),
VerifierValue = match_value_nullable(Verifier),
ExpirationValue = match_value(Expiration),
jsx:encode([{<<"token_request">>,
TokenRequestValue},
{<<"token_request_secret">>,
TokenRequestSecretValue},
{<<"consumer_key">>,
ConsumerKeyValue},
{<<"realm">>,
RealmValue},
{<<"signature_method">>,
SignatureMethodValue},
{<<"client_shared_secret">>,
ClientSharedSecretValue},
{<<"timestamp">>,
TimestampValue},
{<<"nonce_request">>,
NonceRequestValue},
{<<"callback_url">>,
CallbackURLValue},
{<<"callback_qs">>,
CallbackQSValue},
{<<"verifier">>,
VerifierValue},
{<<"expiration">>,
ExpirationValue}]).
json_to_token_request(Value)
when is_binary(Value) ->
JSON0 = jsx:decode(Value, [{return_maps, false}]),
{value, {_, TokenRequest},
JSON1} = lists:keytake(<<"token_request">>, 1, JSON0),
{value, {_, TokenRequestSecret},
JSON2} = lists:keytake(<<"token_request_secret">>, 1, JSON1),
{value, {_, ConsumerKey},
JSON3} = lists:keytake(<<"consumer_key">>, 1, JSON2),
{value, {_, Realm},
JSON4} = lists:keytake(<<"realm">>, 1, JSON3),
{value, {_, SignatureMethod},
JSON5} = lists:keytake(<<"signature_method">>, 1, JSON4),
{value, {_, ClientSharedSecret},
JSON6} = lists:keytake(<<"client_shared_secret">>, 1, JSON5),
{value, {_, Timestamp},
JSON7} = lists:keytake(<<"timestamp">>, 1, JSON6),
{value, {_, NonceRequest},
JSON8} = lists:keytake(<<"nonce_request">>, 1, JSON7),
{value, {_, CallbackURL},
JSON9} = lists:keytake(<<"callback_url">>, 1, JSON8),
{value, {_, CallbackQS},
JSON10} = lists:keytake(<<"callback_qs">>, 1, JSON9),
{value, {_, Verifier},
JSON11} = lists:keytake(<<"verifier">>, 1, JSON10),
{value, {_, Expiration},
[]} = lists:keytake(<<"expiration">>, 1, JSON11),
{TokenRequest, TokenRequestSecret,
ConsumerKey, Realm, SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, CallbackURL, CallbackQS,
Verifier, Expiration}.
token_access_to_json(TokenAccess, TokenAccessSecret,
ConsumerKey, Realm,
SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess, Expiration) ->
TokenAccessValue = match_value(TokenAccess),
TokenAccessSecretValue = match_value(TokenAccessSecret),
ConsumerKeyValue = match_value(ConsumerKey),
RealmValue = match_value_nullable(Realm),
SignatureMethodValue = match_value(SignatureMethod),
ClientSharedSecretValue = match_value(ClientSharedSecret),
TimestampValue = match_value(Timestamp),
NonceRequestValue = match_value(NonceRequest),
NonceAccessValue = match_value(NonceAccess),
ExpirationValue = match_value(Expiration),
jsx:encode([{<<"token_access">>,
TokenAccessValue},
{<<"token_access_secret">>,
TokenAccessSecretValue},
{<<"consumer_key">>,
ConsumerKeyValue},
{<<"realm">>,
RealmValue},
{<<"signature_method">>,
SignatureMethodValue},
{<<"client_shared_secret">>,
ClientSharedSecretValue},
{<<"timestamp">>,
TimestampValue},
{<<"nonce_request">>,
NonceRequestValue},
{<<"nonce_access">>,
NonceAccessValue},
{<<"expiration">>,
ExpirationValue}]).
json_to_token_access(Value)
when is_binary(Value) ->
JSON0 = jsx:decode(Value, [{return_maps, false}]),
{value, {_, TokenAccess},
JSON1} = lists:keytake(<<"token_access">>, 1, JSON0),
{value, {_, TokenAccessSecret},
JSON2} = lists:keytake(<<"token_access_secret">>, 1, JSON1),
{value, {_, ConsumerKey},
JSON3} = lists:keytake(<<"consumer_key">>, 1, JSON2),
{value, {_, Realm},
JSON4} = lists:keytake(<<"realm">>, 1, JSON3),
{value, {_, SignatureMethod},
JSON5} = lists:keytake(<<"signature_method">>, 1, JSON4),
{value, {_, ClientSharedSecret},
JSON6} = lists:keytake(<<"client_shared_secret">>, 1, JSON5),
{value, {_, Timestamp},
JSON7} = lists:keytake(<<"timestamp">>, 1, JSON6),
{value, {_, NonceRequest},
JSON8} = lists:keytake(<<"nonce_request">>, 1, JSON7),
{value, {_, NonceAccess},
JSON9} = lists:keytake(<<"nonce_access">>, 1, JSON8),
{value, {_, Expiration},
[]} = lists:keytake(<<"expiration">>, 1, JSON9),
{TokenAccess, TokenAccessSecret,
ConsumerKey, Realm, SignatureMethod, ClientSharedSecret,
Timestamp, NonceRequest, NonceAccess, Expiration}.
timestamp() ->
timestamp(0).
timestamp(0) ->
{_, _, Microseconds} = Now = os:timestamp(),
{{Year, Month, Day},
{Hour, Minute, Second}} = calendar:now_to_universal_time(Now),
erlang:iolist_to_binary(io_lib:format("~4..0b-~2..0b-~2..0bT"
"~2..0b:~2..0b:~2..0b."
"~6..0bZ",
[Year, Month, Day,
Hour, Minute, Second,
Microseconds]));
timestamp(SecondsInFuture) ->
{_, _, Microseconds} = Now = os:timestamp(),
{{Year, Month, Day},
{Hour, Minute, Second}} =
calendar:gregorian_seconds_to_datetime(
calendar:datetime_to_gregorian_seconds(
calendar:now_to_universal_time(Now)) + SecondsInFuture),
erlang:iolist_to_binary(io_lib:format("~4..0b-~2..0b-~2..0bT"
"~2..0b:~2..0b:~2..0b."
"~6..0bZ",
[Year, Month, Day,
Hour, Minute, Second,
Microseconds])).