Current section
Files
Jump to
Current section
Files
src/epgsql_sasl_prep_profile.erl
%%% coding: utf-8
%%% @doc
%%% This is a helper module that will validate a utf-8
%%% string based on sasl_prep profile as defined in
%%% https://tools.ietf.org/html/rfc4013
%%% @end
-module(epgsql_sasl_prep_profile).
-export([ validate/1
]).
-spec validate(iolist()) -> iolist().
validate(Str) ->
CharL = unicode:characters_to_list(Str, utf8),
lists:foreach(fun(F) ->
lists:any(F, CharL)
andalso error({non_valid_scram_password, Str})
end, [ fun is_non_asci_space_character/1
, fun is_ascii_control_character/1
, fun is_non_ascii_control_character/1
, fun is_private_use_characters/1
, fun is_non_character_code_points/1
, fun is_surrogate_code_points/1
, fun is_inappropriate_for_plain_text_char/1
, fun is_inappropriate_for_canonical_representation_char/1
, fun is_change_display_properties_or_deprecated_char/1
, fun is_tagging_char/1 ]),
Str.
%% @doc Return true if the given character is a non-ASCII space character
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.1.2
-spec is_non_asci_space_character(char()) -> boolean().
is_non_asci_space_character(C) ->
C == 16#00A0
orelse C == 16#1680
orelse (16#2000 =< C andalso C =< 16#200B)
orelse C == 16#202F
orelse C == 16#205F
orelse C == 16#3000.
%% @doc Return true if the given character is an ASCII control character
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.2.1
-spec is_ascii_control_character(char()) -> boolean().
is_ascii_control_character(C) ->
C =< 16#001F orelse C == 16#007F.
%% @doc Return true if the given character is a non-ASCII control character
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.2.2
-spec is_non_ascii_control_character(char()) -> boolean().
is_non_ascii_control_character(C) ->
(16#0080 =< C andalso C =< 16#009F)
orelse C == 16#06DD
orelse C == 16#070F
orelse C == 16#180E
orelse C == 16#200C
orelse C == 16#200D
orelse C == 16#2028
orelse C == 16#2029
orelse C == 16#2060
orelse C == 16#2061
orelse C == 16#2062
orelse C == 16#2063
orelse (16#206A =< C andalso C =< 16#206F)
orelse C == 16#FEFF
orelse (16#FFF9 =< C andalso C =< 16#FFFC)
orelse (16#1D173 =< C andalso C =< 16#1D17A).
%% @doc Return true if the given character is a private use character
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.3
-spec is_private_use_characters(char()) -> boolean().
is_private_use_characters(C) ->
(16#E000 =< C andalso C =< 16#F8FF)
orelse (16#F000 =< C andalso C =< 16#FFFFD)
orelse (16#100000 =< C andalso C =< 16#10FFFD).
%% @doc Return true if the given character is a non-character code point
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.4
-spec is_non_character_code_points(char()) -> boolean().
is_non_character_code_points(C) ->
(16#FDD0 =< C andalso C =< 16#FDEF)
orelse (16#FFFE =< C andalso C =< 16#FFFF)
orelse (16#1FFFE =< C andalso C =< 16#1FFFF)
orelse (16#2FFFE =< C andalso C =< 16#2FFFF)
orelse (16#3FFFE =< C andalso C =< 16#3FFFF)
orelse (16#4FFFE =< C andalso C =< 16#4FFFF)
orelse (16#5FFFE =< C andalso C =< 16#5FFFF)
orelse (16#6FFFE =< C andalso C =< 16#6FFFF)
orelse (16#7FFFE =< C andalso C =< 16#7FFFF)
orelse (16#8FFFE =< C andalso C =< 16#8FFFF)
orelse (16#9FFFE =< C andalso C =< 16#9FFFF)
orelse (16#AFFFE =< C andalso C =< 16#AFFFF)
orelse (16#BFFFE =< C andalso C =< 16#BFFFF)
orelse (16#CFFFE =< C andalso C =< 16#CFFFF)
orelse (16#DFFFE =< C andalso C =< 16#DFFFF)
orelse (16#EFFFE =< C andalso C =< 16#EFFFF)
orelse (16#FFFFE =< C andalso C =< 16#FFFFF)
orelse (16#10FFFE =< C andalso C =< 16#10FFFF).
%% @doc Return true if the given character is a surrogate code point as defined by
%% https://tools.ietf.org/html/rfc3454#appendix-C.5
-spec is_surrogate_code_points(char()) -> boolean().
is_surrogate_code_points(C) ->
16#D800 =< C andalso C =< 16#DFFF.
%% @doc Return true if the given character is inappropriate for plain text characters
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.6
-spec is_inappropriate_for_plain_text_char(char()) -> boolean().
is_inappropriate_for_plain_text_char(C) ->
C == 16#FFF9
orelse C == 16#FFFA
orelse C == 16#FFFB
orelse C == 16#FFFC
orelse C == 16#FFFD.
%% @doc Return true if the given character is inappropriate for canonical representation
%% as defined by https://tools.ietf.org/html/rfc3454#appendix-C.7
-spec is_inappropriate_for_canonical_representation_char(char()) -> boolean().
is_inappropriate_for_canonical_representation_char(C) ->
16#2FF0 =< C andalso C =< 16#2FFB.
%% @doc Return true if the given character is change display properties or deprecated
%% characters as defined by https://tools.ietf.org/html/rfc3454#appendix-C.8
-spec is_change_display_properties_or_deprecated_char(char()) -> boolean().
is_change_display_properties_or_deprecated_char(C) ->
C == 16#0340
orelse C == 16#0341
orelse C == 16#200E
orelse C == 16#200F
orelse C == 16#202A
orelse C == 16#202B
orelse C == 16#202C
orelse C == 16#202D
orelse C == 16#202E
orelse C == 16#206A
orelse C == 16#206B
orelse C == 16#206C
orelse C == 16#206D
orelse C == 16#206E
orelse C == 16#206F.
%% @doc Return true if the given character is a tagging character as defined by
%% https://tools.ietf.org/html/rfc3454#appendix-C.9
-spec is_tagging_char(char()) -> boolean().
is_tagging_char(C) ->
C == 16#E0001 orelse
(16#E0020 =< C andalso C =< 16#E007F).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
normalize_test() ->
?assertEqual(<<"123 !~">>, validate(<<"123 !~">>)),
?assertEqual(<<"привет"/utf8>>, validate(<<"привет"/utf8>>)),
?assertEqual(<<"Χαίρετε"/utf8>>, validate(<<"Χαίρετε"/utf8>>)),
?assertEqual(<<"你好"/utf8>>, validate(<<"你好"/utf8>>)),
?assertError({non_valid_scram_password, _},
validate(<<"boom in the last char ́"/utf8>>)).
-endif.