emqx/lib-ee/emqx_license/src/emqx_license_parser.erl

126 lines
3.7 KiB
Erlang

%%--------------------------------------------------------------------
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% @doc EMQX License Management.
%%--------------------------------------------------------------------
-module(emqx_license_parser).
-include_lib("emqx/include/logger.hrl").
-include("emqx_license.hrl").
-define(PUBKEY, <<
""
"\n"
"-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbtkdos3TZmSv+D7+X5pc0yfcjum2\n"
"Q1DK6PCWkiQihjvjJjKFzdYzcWOgC6f4Ou3mgGAUSjdQYYnFKZ/9f5ax4g==\n"
"-----END PUBLIC KEY-----\n"
""
>>).
-define(LICENSE_PARSE_MODULES, [
emqx_license_parser_v20220101
]).
-type license_data() :: term().
-type customer_type() ::
?SMALL_CUSTOMER
| ?MEDIUM_CUSTOMER
| ?LARGE_CUSTOMER
| ?EVALUATION_CUSTOMER.
-type license_type() :: ?OFFICIAL | ?TRIAL.
-type license() :: #{module := module(), data := license_data()}.
-export_type([
license_data/0,
customer_type/0,
license_type/0,
license/0
]).
-export([
parse/1,
parse/2,
dump/1,
customer_type/1,
license_type/1,
expiry_date/1,
max_connections/1
]).
%%--------------------------------------------------------------------
%% Behaviour
%%--------------------------------------------------------------------
-callback parse(string() | binary(), binary()) -> {ok, license_data()} | {error, term()}.
-callback dump(license_data()) -> list({atom(), term()}).
-callback customer_type(license_data()) -> customer_type().
-callback license_type(license_data()) -> license_type().
-callback expiry_date(license_data()) -> calendar:date().
-callback max_connections(license_data()) -> non_neg_integer().
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
-ifdef(TEST).
-spec parse(string() | binary()) -> {ok, license()} | {error, term()}.
parse(Content) ->
PubKey = persistent_term:get(emqx_license_test_pubkey, ?PUBKEY),
parse(Content, PubKey).
-else.
-spec parse(string() | binary()) -> {ok, license()} | {error, term()}.
parse(Content) ->
parse(Content, ?PUBKEY).
-endif.
parse(Content, Pem) ->
[PemEntry] = public_key:pem_decode(Pem),
Key = public_key:pem_entry_decode(PemEntry),
do_parse(iolist_to_binary(Content), Key, ?LICENSE_PARSE_MODULES, []).
-spec dump(license()) -> list({atom(), term()}).
dump(#{module := Module, data := LicenseData}) ->
Module:dump(LicenseData).
-spec customer_type(license()) -> customer_type().
customer_type(#{module := Module, data := LicenseData}) ->
Module:customer_type(LicenseData).
-spec license_type(license()) -> license_type().
license_type(#{module := Module, data := LicenseData}) ->
Module:license_type(LicenseData).
-spec expiry_date(license()) -> calendar:date().
expiry_date(#{module := Module, data := LicenseData}) ->
Module:expiry_date(LicenseData).
-spec max_connections(license()) -> non_neg_integer().
max_connections(#{module := Module, data := LicenseData}) ->
Module:max_connections(LicenseData).
%%--------------------------------------------------------------------
%% Private functions
%%--------------------------------------------------------------------
do_parse(_Content, _Key, [], Errors) ->
{error, lists:reverse(Errors)};
do_parse(Content, Key, [Module | Modules], Errors) ->
try Module:parse(Content, Key) of
{ok, LicenseData} ->
{ok, #{module => Module, data => LicenseData}};
{error, Error} ->
do_parse(Content, Key, Modules, [{Module, Error} | Errors])
catch
_Class:Error:Stacktrace ->
do_parse(Content, Key, Modules, [{Module, {Error, Stacktrace}} | Errors])
end.