Arrrrrganazing...
This commit is contained in:
parent
1fde98932c
commit
da456ef4e4
@ -39,7 +39,7 @@
|
|||||||
-export_type([serial/0, package_id/0, package/0, realm/0, name/0, version/0,
|
-export_type([serial/0, package_id/0, package/0, realm/0, name/0, version/0,
|
||||||
identifier/0,
|
identifier/0,
|
||||||
host/0,
|
host/0,
|
||||||
key_data/0, key_bin/0, key_id/0, key_name/0,
|
key/0, key_data/0, key_bin/0, key_id/0, key_name/0,
|
||||||
user_id/0, user_name/0, contact_info/0, user_data/0,
|
user_id/0, user_name/0, contact_info/0, user_data/0,
|
||||||
lower0_9/0, label/0,
|
lower0_9/0, label/0,
|
||||||
ss_tag/0, search_tag/0, description/0, package_type/0,
|
ss_tag/0, search_tag/0, description/0, package_type/0,
|
||||||
@ -60,11 +60,12 @@
|
|||||||
Minor :: non_neg_integer() | z,
|
Minor :: non_neg_integer() | z,
|
||||||
Patch :: non_neg_integer() | z}.
|
Patch :: non_neg_integer() | z}.
|
||||||
-type host() :: {string() | inet:ip_address(), inet:port_number()}.
|
-type host() :: {string() | inet:ip_address(), inet:port_number()}.
|
||||||
|
-type key() :: term(). % Srsly. This is what public_key:der_decode/2 returns.
|
||||||
-type key_data() :: {Name :: key_name(),
|
-type key_data() :: {Name :: key_name(),
|
||||||
Public :: none | key_bin(),
|
Public :: none | key_bin(),
|
||||||
Private :: none | key_bin()}.
|
Private :: none | key_bin()}.
|
||||||
-type key_bin() :: {Sig :: none | {key_name(), binary()},
|
-type key_bin() :: {Sig :: none | {key_name(), binary()},
|
||||||
Der :: binary()}.
|
DER :: binary()}.
|
||||||
-type key_id() :: {realm(), key_name()}.
|
-type key_id() :: {realm(), key_name()}.
|
||||||
-type key_name() :: key_hash().
|
-type key_name() :: key_hash().
|
||||||
-type key_hash() :: binary().
|
-type key_hash() :: binary().
|
||||||
@ -225,12 +226,12 @@ do(["sync", "keys"]) ->
|
|||||||
done(zx_auth:sync_keys());
|
done(zx_auth:sync_keys());
|
||||||
do(["create", "user"]) ->
|
do(["create", "user"]) ->
|
||||||
done(zx_local:create_user());
|
done(zx_local:create_user());
|
||||||
do(["create", "userfile"]) ->
|
|
||||||
done(zx_local:create_userfile());
|
|
||||||
do(["create", "keypair"]) ->
|
do(["create", "keypair"]) ->
|
||||||
done(zx_local:grow_a_pair());
|
done(zx_local:grow_a_pair());
|
||||||
do(["export", "user"]) ->
|
do(["export", "user"]) ->
|
||||||
done(zx_local:export_user());
|
done(zx_local:export_user(zpuf));
|
||||||
|
do(["export", "user", "dangerous"]) ->
|
||||||
|
done(zx_local:export_user(zduf));
|
||||||
do(["import", "user", ZdufFile]) ->
|
do(["import", "user", ZdufFile]) ->
|
||||||
done(zx_local:import_user(ZdufFile));
|
done(zx_local:import_user(ZdufFile));
|
||||||
do(["list", "users", Realm]) ->
|
do(["list", "users", Realm]) ->
|
||||||
@ -888,9 +889,8 @@ usage_dev() ->
|
|||||||
" zx reject PackageID~n"
|
" zx reject PackageID~n"
|
||||||
" zx sync keys~n"
|
" zx sync keys~n"
|
||||||
" zx create user~n"
|
" zx create user~n"
|
||||||
" zx create userfile~n"
|
|
||||||
" zx create keypair~n"
|
" zx create keypair~n"
|
||||||
" zx export user~n"
|
" zx export user [dangerous]~n"
|
||||||
" zx import user ZDUF~n"
|
" zx import user ZDUF~n"
|
||||||
" zx list users Realm~n"
|
" zx list users Realm~n"
|
||||||
" zx list packagers PackageName~n"
|
" zx list packagers PackageName~n"
|
||||||
|
|||||||
@ -307,6 +307,7 @@ add_user(ZPUF) ->
|
|||||||
add_user2(Bin) ->
|
add_user2(Bin) ->
|
||||||
case zx_lib:b_to_t(Bin) of
|
case zx_lib:b_to_t(Bin) of
|
||||||
{ok, {UserInfo, KeyData}} -> add_user3(UserInfo, KeyData);
|
{ok, {UserInfo, KeyData}} -> add_user3(UserInfo, KeyData);
|
||||||
|
{ok, _} -> {error, "Malformed user file.", 1};
|
||||||
error -> {error, "Bad user file.", 1}
|
error -> {error, "Bad user file.", 1}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
@ -152,8 +152,8 @@
|
|||||||
list_sysops/1,
|
list_sysops/1,
|
||||||
fetch/1, install/1, build/1,
|
fetch/1, install/1, build/1,
|
||||||
wait_result/1, wait_results/1]).
|
wait_result/1, wait_results/1]).
|
||||||
-export([register_key/2, get_key/2, keybin/2,
|
-export([register_key/2, get_key/2, get_keybin/2,
|
||||||
find_keypair/1, have_key/2, list_keys/2]).
|
have_key/2, list_keys/2]).
|
||||||
-export([report/1, result/2, notify/2]).
|
-export([report/1, result/2, notify/2]).
|
||||||
-export([connect/0, disconnect/0]).
|
-export([connect/0, disconnect/0]).
|
||||||
-export([conf/1, conf/2, hosts/0,
|
-export([conf/1, conf/2, hosts/0,
|
||||||
@ -187,8 +187,7 @@
|
|||||||
dropped = maps:new() :: requests(),
|
dropped = maps:new() :: requests(),
|
||||||
timer = none :: none | reference(),
|
timer = none :: none | reference(),
|
||||||
mx = mx_new() :: monitor_index(),
|
mx = mx_new() :: monitor_index(),
|
||||||
cx = new_cx() :: conn_index(),
|
cx = new_cx() :: conn_index()}).
|
||||||
kx = load_kx() :: key_index()}).
|
|
||||||
|
|
||||||
-record(conf,
|
-record(conf,
|
||||||
{realms = zx_lib:list_realms() :: [zx:realm()],
|
{realms = zx_lib:list_realms() :: [zx:realm()],
|
||||||
@ -213,7 +212,7 @@
|
|||||||
-record(rmeta,
|
-record(rmeta,
|
||||||
{serial = 0 :: non_neg_integer(),
|
{serial = 0 :: non_neg_integer(),
|
||||||
prime = {"zomp.tsuriai.jp", 11311} :: zx:host(),
|
prime = {"zomp.tsuriai.jp", 11311} :: zx:host(),
|
||||||
key = [] :: zx:key_name(),
|
key = none :: none | zx:key_name(),
|
||||||
sysop = none :: zx:user_name(),
|
sysop = none :: zx:user_name(),
|
||||||
assigned = none :: none | managed | pid(),
|
assigned = none :: none | managed | pid(),
|
||||||
available = [] :: [pid()]}).
|
available = [] :: [pid()]}).
|
||||||
@ -224,12 +223,6 @@
|
|||||||
requests = [] :: [id()],
|
requests = [] :: [id()],
|
||||||
subs = [] :: [{pid(), zx:package()}]}).
|
subs = [] :: [{pid(), zx:package()}]}).
|
||||||
|
|
||||||
-record(key,
|
|
||||||
{pubhash = none :: none | binary(),
|
|
||||||
pub = none :: none | public_key:public_key(),
|
|
||||||
keyhash = none :: none | binary(),
|
|
||||||
key = none :: none | public_key:private_key()}).
|
|
||||||
|
|
||||||
|
|
||||||
%% State Types
|
%% State Types
|
||||||
-type state() :: #s{}.
|
-type state() :: #s{}.
|
||||||
@ -239,11 +232,6 @@
|
|||||||
| {unsubscribe, pid(), zx:package()}
|
| {unsubscribe, pid(), zx:package()}
|
||||||
| {request, pid(), id(), action()}.
|
| {request, pid(), id(), action()}.
|
||||||
-type requests() :: #{id() := {pid(), action()}}.
|
-type requests() :: #{id() := {pid(), action()}}.
|
||||||
-type key_index() :: {rk_index(), pair_index(), key_owners()}.
|
|
||||||
-type rk_index() :: #{zx:realm() := key_registry()}.
|
|
||||||
-type pair_index() :: #{zx:key_hash() := zx:key_id()}.
|
|
||||||
-type key_registry() :: #{zx:key_hash() := #key{}}.
|
|
||||||
-type key_owners() :: #{zx:user_id() := {[zx:key_hash()], [zx:key_hash()]}}.
|
|
||||||
-type monitor_index() :: #{pid() := {reference(), category()}}.
|
-type monitor_index() :: #{pid() := {reference(), category()}}.
|
||||||
-type conn_index() :: #cx{} | zomp | proxy.
|
-type conn_index() :: #cx{} | zomp | proxy.
|
||||||
-type realm_meta() :: #rmeta{}.
|
-type realm_meta() :: #rmeta{}.
|
||||||
@ -606,8 +594,8 @@ drop_mirror(Host) ->
|
|||||||
when Owner :: zx:realm() | zx:user_id(),
|
when Owner :: zx:realm() | zx:user_id(),
|
||||||
KeyData :: zx:key_data().
|
KeyData :: zx:key_data().
|
||||||
|
|
||||||
register_key(Owner, Data) ->
|
register_key(Owner, KeyData) ->
|
||||||
gen_server:call(?MODULE, {register_key, Owner, Data}).
|
gen_server:call(?MODULE, {register_key, Owner, KeyData}).
|
||||||
|
|
||||||
|
|
||||||
-spec get_key(Type, KeyID) -> Result
|
-spec get_key(Type, KeyID) -> Result
|
||||||
@ -624,23 +612,25 @@ get_key(Type, KeyID) ->
|
|||||||
gen_server:call(?MODULE, {get_key, Type, KeyID}).
|
gen_server:call(?MODULE, {get_key, Type, KeyID}).
|
||||||
|
|
||||||
|
|
||||||
-spec keybin(Type, KeyID) -> Result
|
-spec get_keybin(Type, KeyID) -> Result
|
||||||
when Type :: public | private,
|
when Type :: public | private,
|
||||||
KeyID :: zx:key_id(),
|
KeyID :: zx:key_id(),
|
||||||
Result :: {ok, binary()}
|
Result :: {ok, binary()}
|
||||||
| {error, file:posix()}.
|
| {error, file:posix()}.
|
||||||
|
|
||||||
keybin(Type, KeyID) ->
|
get_keybin(Type, KeyID) ->
|
||||||
gen_server:call(?MODULE, {keybin, Type, KeyID}).
|
gen_server:call(?MODULE, {get_keybin, Type, KeyID}).
|
||||||
|
|
||||||
|
|
||||||
-spec find_keypair(KeyName) -> Result
|
% TODO: This should be an external request to a Zomp node.
|
||||||
when KeyName :: zx:key_name(),
|
% FIXME: Determine how this should work.
|
||||||
Result :: {ok, zx:key_id()}
|
%-spec find_keypair(KeyName) -> Result
|
||||||
| error.
|
% when KeyName :: zx:key_name(),
|
||||||
|
% Result :: {ok, zx:key_id()}
|
||||||
find_keypair(KeyName) ->
|
% | error.
|
||||||
gen_server:call(?MODULE, {find_keypair, KeyName}).
|
%
|
||||||
|
%find_keypair(KeyName) ->
|
||||||
|
% gen_server:call(?MODULE, {find_keypair, KeyName}).
|
||||||
|
|
||||||
|
|
||||||
-spec have_key(Type, KeyID) -> boolean()
|
-spec have_key(Type, KeyID) -> boolean()
|
||||||
@ -814,29 +804,25 @@ handle_call({build, PackageID}, _, State) ->
|
|||||||
Result = do_build(PackageID),
|
Result = do_build(PackageID),
|
||||||
NewState = eval_queue(State),
|
NewState = eval_queue(State),
|
||||||
{reply, Result, NewState};
|
{reply, Result, NewState};
|
||||||
handle_call({get_key, Type, KeyID}, _, State = #s{kx = KX}) ->
|
handle_call({get_key, Type, KeyID}, _, State) ->
|
||||||
Result = do_get_key(Type, KeyID, element(1, KX)),
|
Result = do_get_key(Type, KeyID),
|
||||||
NewState = eval_queue(State),
|
NewState = eval_queue(State),
|
||||||
{reply, Result, NewState};
|
{reply, Result, NewState};
|
||||||
handle_call({keybin, Type, KeyID}, _, State = #s{kx = KX}) ->
|
handle_call({get_keybin, Type, KeyID}, _, State) ->
|
||||||
Result = do_keybin(Type, KeyID, element(1, KX)),
|
Result = do_get_keybin(Type, KeyID),
|
||||||
NewState = eval_queue(State),
|
NewState = eval_queue(State),
|
||||||
{reply, Result, NewState};
|
{reply, Result, NewState};
|
||||||
handle_call({find_keypair, KeyName}, _, State = #s{kx = KX}) ->
|
handle_call({have_key, Type, KeyID}, _, State) ->
|
||||||
Result = maps:find(KeyName, element(2, KX)),
|
Result = do_have_key(Type, KeyID),
|
||||||
NewState = eval_queue(State),
|
NewState = eval_queue(State),
|
||||||
{reply, Result, NewState};
|
{reply, Result, NewState};
|
||||||
handle_call({have_key, Type, KeyID}, _, State = #s{kx = KX}) ->
|
handle_call({list_keys, Type, Owner}, _, State) ->
|
||||||
Result = do_have_key(Type, KeyID, element(1, KX)),
|
Result = do_list_keys(Type, Owner),
|
||||||
NewState = eval_queue(State),
|
|
||||||
{reply, Result, NewState};
|
|
||||||
handle_call({list_keys, Type, Owner}, _, State = #s{kx = KX}) ->
|
|
||||||
Result = do_list_keys(Type, Owner, KX),
|
|
||||||
NewState = eval_queue(State),
|
NewState = eval_queue(State),
|
||||||
{reply, Result, NewState};
|
{reply, Result, NewState};
|
||||||
handle_call({register_key, Owner, Data}, _, State) ->
|
handle_call({register_key, Owner, Data}, _, State) ->
|
||||||
{Result, NextState} = do_register_key(Owner, Data, State),
|
Result = do_register_key(Owner, Data),
|
||||||
NewState = eval_queue(NextState),
|
NewState = eval_queue(State),
|
||||||
{reply, Result, NewState};
|
{reply, Result, NewState};
|
||||||
handle_call({takeover, Realm}, _, State = #s{conf = Conf}) ->
|
handle_call({takeover, Realm}, _, State = #s{conf = Conf}) ->
|
||||||
{Result, NewConf} = do_takeover(Realm, Conf),
|
{Result, NewConf} = do_takeover(Realm, Conf),
|
||||||
@ -1868,11 +1854,9 @@ do_takeover(Realm, C = #conf{realms = Realms, managed = Managed}) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec do_abdicate(Realm, State) -> {Result, NewState}
|
-spec do_abdicate(Realm, State) -> NewState
|
||||||
when Realm :: zx:realm(),
|
when Realm :: zx:realm(),
|
||||||
State :: state(),
|
State :: state(),
|
||||||
Result :: ok
|
|
||||||
| {error, unmanaged},
|
|
||||||
NewState :: state().
|
NewState :: state().
|
||||||
|
|
||||||
do_abdicate(Realm, State = #s{conf = C = #conf{managed = Managed}}) ->
|
do_abdicate(Realm, State = #s{conf = C = #conf{managed = Managed}}) ->
|
||||||
@ -1882,271 +1866,121 @@ do_abdicate(Realm, State = #s{conf = C = #conf{managed = Managed}}) ->
|
|||||||
NewC = C#conf{managed = NewManaged},
|
NewC = C#conf{managed = NewManaged},
|
||||||
ok = save_conf(NewC),
|
ok = save_conf(NewC),
|
||||||
ok = log(info, "No longer managing realm: ~160tp", [Realm]),
|
ok = log(info, "No longer managing realm: ~160tp", [Realm]),
|
||||||
{ok, State#s{conf = NewC}};
|
State#s{conf = NewC};
|
||||||
false ->
|
false ->
|
||||||
ok = tell(error, "Cannot abdicate an unmanaged realm."),
|
ok = tell(error, "Cannot abdicate an unmanaged realm."),
|
||||||
{{error, unmanaged}, State}
|
State
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%% Key Functions
|
%%% Key Functions
|
||||||
|
|
||||||
-spec load_kx() -> key_index().
|
-spec do_register_key(Owner, KeyData) -> Result
|
||||||
|
|
||||||
load_kx() ->
|
|
||||||
case file:read_file(key_stash_path()) of
|
|
||||||
{ok, Bin} -> binary_to_term(Bin);
|
|
||||||
{error, enoent} -> {maps:new(), maps:new(), maps:new()}
|
|
||||||
end.
|
|
||||||
|
|
||||||
key_stash_path() ->
|
|
||||||
filename:join(zx_lib:path(key), "key.stash").
|
|
||||||
|
|
||||||
|
|
||||||
-spec do_register_key(Owner, KeyData, State) -> {ok, NewState}
|
|
||||||
when Owner :: zx:realm() | zx:user_id(),
|
when Owner :: zx:realm() | zx:user_id(),
|
||||||
KeyData :: zx:key_data(),
|
KeyData :: zx:key_data(),
|
||||||
State :: state(),
|
Result :: ok
|
||||||
NewState :: state().
|
| {error, Reason},
|
||||||
|
Reason :: bad_user
|
||||||
|
| bad_realm
|
||||||
|
| file:posix().
|
||||||
|
|
||||||
do_register_key(UserID = {Realm, _}, Data, State = #s{kx = {RX, PX, UK}}) ->
|
do_register_key(Owner = {Realm, UserName}, KeyData) ->
|
||||||
{NewRX, NewPX} = do_register_realm_key(Realm, Data, RX, PX),
|
case zx_userconf:load(Owner) of
|
||||||
NewUK =
|
{ok, UC} ->
|
||||||
case Data of
|
do_register_key2(Realm, UC, KeyData);
|
||||||
{PairHash, none, {_, K}} when is_binary(K) ->
|
{error, bad_user} ->
|
||||||
Update = fun({Ps, Ks}) -> {Ps, [PairHash | Ks]} end,
|
UC = zx_userconf:new(),
|
||||||
maps:update_with(UserID, Update, {[], [PairHash]}, UK);
|
NewUC = UC#{realm => Realm, username => UserName},
|
||||||
{PairHash, {_, P}, none} when is_binary(P) ->
|
do_register_key2(Realm, NewUC, KeyData);
|
||||||
Update = fun({Ps, Ks}) -> {[PairHash | Ps], Ks} end,
|
Error ->
|
||||||
maps:update_with(UserID, Update, {[PairHash], []}, UK);
|
Error
|
||||||
{PairHash, {_, P}, {_, K}} when is_binary(P), is_binary(K) ->
|
end.
|
||||||
Update = fun({Ps, Ks}) -> {[PairHash | Ps], [PairHash | Ks]} end,
|
|
||||||
maps:update_with(UserID, Update, {[PairHash], [PairHash]}, UK)
|
|
||||||
|
do_register_key2(Realm, UC = #{keys := Keys}, KeyData = {KeyHash, _, _}) ->
|
||||||
|
NewUC =
|
||||||
|
case lists:member(KeyHash, Keys) of
|
||||||
|
false -> UC#{keys => [KeyHash | Keys]};
|
||||||
|
true -> UC
|
||||||
end,
|
end,
|
||||||
NewKX = {NewRX, NewPX, NewUK},
|
ok = zx_userconf:save(NewUC),
|
||||||
ok = stash_keys(NewKX),
|
do_register_key3(Realm, KeyData).
|
||||||
{ok, State#s{kx = NewKX}};
|
|
||||||
do_register_key(Realm, Data, State = #s{kx = {RX, PX, UK}}) ->
|
|
||||||
{NewRX, NewPX} = do_register_realm_key(Realm, Data, RX, PX),
|
|
||||||
NewKX = {NewRX, NewPX, UK},
|
|
||||||
ok = stash_keys(NewKX),
|
|
||||||
{ok, State#s{kx = NewKX}}.
|
|
||||||
|
|
||||||
do_register_realm_key(Realm, Data, RealmIndex, PairIndex) ->
|
do_register_key3(Realm, {KeyHash, none, {_, Key}}) ->
|
||||||
PairHash = element(1, Data),
|
zx_key:save_bin(private, {Realm, KeyHash}, Key);
|
||||||
{K, Pairs} = do_register_realm_key2(Realm, Data),
|
do_register_key3(Realm, {KeyHash, {_, Pub}, none}) ->
|
||||||
KeyRegistry = maps:get(Realm, RealmIndex, #{}),
|
zx_key:save_bin(public, {Realm, KeyHash}, Pub);
|
||||||
NewKeyRegistry = update_registry(PairHash, K, KeyRegistry),
|
do_register_key3(Realm, {KeyHash, {_, Pub}, {_, Key}}) ->
|
||||||
NewRealmIndex = maps:put(Realm, NewKeyRegistry, RealmIndex),
|
ok = zx_key:save_bin(public, {Realm, KeyHash}, Pub),
|
||||||
NewPairIndex = maps:merge(PairIndex, Pairs),
|
zx_key:save(private, {Realm, KeyHash}, Key);
|
||||||
{NewRealmIndex, NewPairIndex}.
|
do_register_key3(_, {_, none, none}) ->
|
||||||
|
ok.
|
||||||
do_register_realm_key2(Realm, {PairHash, {_, PubBin}, none}) ->
|
|
||||||
PubHash = crypto:hash(sha512, PubBin),
|
|
||||||
ok = store_key(public, {Realm, PairHash}, PubBin),
|
|
||||||
Pub = public_key:der_decode('RSAPublicKey', PubBin),
|
|
||||||
K = #key{pubhash = PubHash, pub = Pub},
|
|
||||||
Pairs = #{PubHash => PairHash},
|
|
||||||
{K, Pairs};
|
|
||||||
do_register_realm_key2(Realm, {PairHash, none, {_, KeyBin}}) ->
|
|
||||||
KeyHash = crypto:hash(sha512, KeyBin),
|
|
||||||
ok = store_key(private, {Realm, PairHash}, KeyBin),
|
|
||||||
Key = public_key:der_decode('RSAPrivateKey', KeyBin),
|
|
||||||
K = #key{keyhash = KeyHash, key = Key},
|
|
||||||
Pairs = #{KeyHash => PairHash},
|
|
||||||
{K, Pairs};
|
|
||||||
do_register_realm_key2(Realm, {PairHash, {_, PubBin}, {_, KeyBin}}) ->
|
|
||||||
PubHash = crypto:hash(sha512, PubBin),
|
|
||||||
KeyHash = crypto:hash(sha512, KeyBin),
|
|
||||||
ok = store_key(public, {Realm, PairHash}, PubBin),
|
|
||||||
ok = store_key(private, {Realm, PairHash}, KeyBin),
|
|
||||||
PairHash = crypto:hash(sha512, <<PubHash/binary, KeyHash/binary>>),
|
|
||||||
Pub = public_key:der_decode('RSAPublicKey', PubBin),
|
|
||||||
Key = public_key:der_decode('RSAPrivateKey', KeyBin),
|
|
||||||
K = #key{pubhash = PubHash, pub = Pub, keyhash = KeyHash, key = Key},
|
|
||||||
Pairs = #{PubHash => PairHash, KeyHash => PairHash},
|
|
||||||
{K, Pairs}.
|
|
||||||
|
|
||||||
store_key(Type, KeyID, Bin) ->
|
|
||||||
Path = zx_key:path(Type, KeyID),
|
|
||||||
ok = filelib:ensure_dir(Path),
|
|
||||||
file:write_file(Path, Bin).
|
|
||||||
|
|
||||||
update_registry(PairHash,
|
|
||||||
K = #key{pubhash = none, keyhash = KeyHash, key = Key},
|
|
||||||
Registry) ->
|
|
||||||
case maps:find(PairHash, Registry) of
|
|
||||||
error ->
|
|
||||||
maps:put(PairHash, K, Registry);
|
|
||||||
{ok, #key{pubhash = none, keyhash = none}} ->
|
|
||||||
maps:put(PairHash, K, Registry);
|
|
||||||
{ok, OldK = #key{keyhash = none}} ->
|
|
||||||
maps:put(PairHash, OldK#key{keyhash = KeyHash, key = Key}, Registry);
|
|
||||||
{ok, #key{keyhash = KeyHash}} ->
|
|
||||||
Registry
|
|
||||||
end;
|
|
||||||
update_registry(PairHash,
|
|
||||||
K = #key{keyhash = none, pubhash = PubHash, pub = Pub},
|
|
||||||
Registry) ->
|
|
||||||
case maps:find(PairHash, Registry) of
|
|
||||||
error ->
|
|
||||||
maps:put(PairHash, K, Registry);
|
|
||||||
{ok, #key{pubhash = none, keyhash = none}} ->
|
|
||||||
maps:put(PairHash, K, Registry);
|
|
||||||
{ok, OldK = #key{pubhash = none}} ->
|
|
||||||
maps:put(PairHash, OldK#key{pubhash = PubHash, pub = Pub}, Registry);
|
|
||||||
{ok, #key{pubhash = PubHash}} ->
|
|
||||||
Registry
|
|
||||||
end;
|
|
||||||
update_registry(PairHash, K, Registry) ->
|
|
||||||
maps:put(PairHash, K, Registry).
|
|
||||||
|
|
||||||
stash_keys(KX) ->
|
|
||||||
Bin = term_to_binary(KX),
|
|
||||||
file:write_file(key_stash_path(), Bin).
|
|
||||||
|
|
||||||
|
|
||||||
-spec do_get_key(Type, KeyID, RK) -> Result
|
-spec do_get_key(Type, KeyID) -> Result
|
||||||
when Type :: public | private,
|
when Type :: public | private,
|
||||||
KeyID :: zx:key_id(),
|
KeyID :: zx:key_id(),
|
||||||
RK :: rk_index(),
|
|
||||||
Result :: {ok, public_key:rsa_public_key() | public_key:rsa_private_key()}
|
Result :: {ok, public_key:rsa_public_key() | public_key:rsa_private_key()}
|
||||||
| {error, Reason},
|
| {error, Reason},
|
||||||
Reason :: bad_realm
|
Reason :: bad_realm
|
||||||
| no_pub
|
| no_pub
|
||||||
| no_key
|
| no_key
|
||||||
| bad_key.
|
| file:posix().
|
||||||
|
|
||||||
do_get_key(Type, {Realm, KeyName}, RK) ->
|
do_get_key(Type, KeyID) ->
|
||||||
case maps:find(Realm, RK) of
|
zx_key:load(Type, KeyID).
|
||||||
{ok, Registry} -> do_get_key2(Type, KeyName, Registry);
|
|
||||||
error -> {error, bad_realm}
|
|
||||||
end.
|
|
||||||
|
|
||||||
do_get_key2(public, KeyName, Registry) ->
|
|
||||||
case maps:find(KeyName, Registry) of
|
|
||||||
{ok, #key{pub = none}} -> {error, no_pub};
|
|
||||||
{ok, #key{pub = Pub}} -> {ok, Pub};
|
|
||||||
error -> {error, bad_key}
|
|
||||||
end;
|
|
||||||
do_get_key2(private, KeyName, Registry) ->
|
|
||||||
case maps:find(KeyName, Registry) of
|
|
||||||
{ok, #key{key = none}} -> {error, no_key};
|
|
||||||
{ok, #key{key = Key}} -> {ok, Key};
|
|
||||||
error -> {error, bad_key}
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-spec do_keybin(Type, KeyID, RK) -> Result
|
-spec do_get_keybin(Type, KeyID) -> Result
|
||||||
when Type :: public | private,
|
when Type :: public | private,
|
||||||
KeyID :: zx:key_id(),
|
KeyID :: zx:key_id(),
|
||||||
RK :: rk_index(),
|
|
||||||
Result :: {ok, binary()}
|
Result :: {ok, binary()}
|
||||||
| {error, bad_realm | bad_key | no_key | file:posix()}.
|
| {error, bad_realm | no_key | no_pub | file:posix()}.
|
||||||
|
|
||||||
do_keybin(Type, KeyID = {Realm, _}, RK) ->
|
do_get_keybin(Type, KeyID) ->
|
||||||
case maps:find(Realm, RK) of
|
zx_key:load_bin(Type, KeyID).
|
||||||
{ok, Registry} -> do_keybin2(Type, KeyID, Registry);
|
|
||||||
error -> {error, bad_realm}
|
|
||||||
end.
|
|
||||||
|
|
||||||
do_keybin2(public, KeyID = {_, KeyName}, Registry) ->
|
|
||||||
case maps:find(KeyName, Registry) of
|
|
||||||
{ok, #key{pubhash = none}} -> {error, no_key};
|
|
||||||
{ok, #key{}} -> file:read_file(zx_key:path(public, KeyID));
|
|
||||||
error -> {error, bad_key}
|
|
||||||
end;
|
|
||||||
do_keybin2(private, KeyID, Registry) ->
|
|
||||||
KeyName = element(2, KeyID),
|
|
||||||
case maps:find(KeyName, Registry) of
|
|
||||||
{ok, #key{keyhash = none}} -> {error, no_key};
|
|
||||||
{ok, #key{}} -> file:read_file(zx_key:path(private, KeyID));
|
|
||||||
error -> {error, bad_key}
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-spec do_have_key(Type, KeyID, RK) -> boolean()
|
-spec do_have_key(Type, KeyID) -> boolean()
|
||||||
when Type :: public | private,
|
when Type :: public | private,
|
||||||
KeyID :: zx:key_id(),
|
KeyID :: zx:key_id().
|
||||||
RK :: rk_index().
|
|
||||||
|
|
||||||
do_have_key(Type, {Realm, KeyName}, RK) ->
|
do_have_key(Type, KeyID) ->
|
||||||
case maps:find(Realm, RK) of
|
zx_key:exists(Type, KeyID).
|
||||||
{ok, Registry} ->
|
|
||||||
case maps:find(KeyName, Registry) of
|
|
||||||
{ok, K} -> do_have_key2(Type, K);
|
|
||||||
error -> false
|
|
||||||
end;
|
|
||||||
error ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
do_have_key2(public, #key{pub = none}) -> false;
|
|
||||||
do_have_key2(private, #key{key = none}) -> false;
|
|
||||||
do_have_key2(_, #key{}) -> true.
|
|
||||||
|
|
||||||
|
|
||||||
-spec do_list_keys(Type, Owner, KX) -> Result
|
-spec do_list_keys(Type, Owner) -> Result
|
||||||
when Type :: public | private,
|
when Type :: public | private,
|
||||||
Owner :: zx:realm() | zx:user_id(),
|
Owner :: zx:realm() | zx:user_id(),
|
||||||
KX :: key_index(),
|
|
||||||
Result :: {ok, [zx:key_hash()]}
|
Result :: {ok, [zx:key_hash()]}
|
||||||
| {error, bad_realm | bad_user}.
|
| {error, bad_realm | bad_user}.
|
||||||
|
|
||||||
do_list_keys(public, UserID, {_, _, UK}) when is_tuple(UserID) ->
|
do_list_keys(Type, UserID = {Realm, _}) ->
|
||||||
case maps:find(UserID, UK) of
|
case zx_userconf:load(UserID) of
|
||||||
{ok, {Keys, _}} -> {ok, Keys};
|
{ok, #{keys := Keys}} -> do_list_keys2(Type, [{Realm, K} || K <- Keys]);
|
||||||
error -> {error, bad_user}
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
do_list_keys(private, UserID, {_, _, UK}) when is_tuple(UserID) ->
|
do_list_keys(Type, Realm) ->
|
||||||
case maps:find(UserID, UK) of
|
case zx_lib:load_realm_conf(Realm) of
|
||||||
{ok, {_, Keys}} -> {ok, Keys};
|
{ok, #{key := Key}} -> do_list_keys2(Type, [{Realm, Key}]);
|
||||||
error -> {error, bad_user}
|
Error -> Error
|
||||||
end;
|
|
||||||
do_list_keys(public, Realm, {RK, _, _}) ->
|
|
||||||
case maps:find(Realm, RK) of
|
|
||||||
{ok, Registry} ->
|
|
||||||
List = maps:to_list(Registry),
|
|
||||||
{ok, [ID || {ID, #key{pubhash = PH}} <- List, is_binary(PH)]};
|
|
||||||
error ->
|
|
||||||
{error, bad_realm}
|
|
||||||
end;
|
|
||||||
do_list_keys(private, Realm, {RK, _, _}) ->
|
|
||||||
case maps:find(Realm, RK) of
|
|
||||||
{ok, Registry} ->
|
|
||||||
List = maps:to_list(Registry),
|
|
||||||
{ok, [ID || {ID, #key{keyhash = KH}} <- List, is_binary(KH)]};
|
|
||||||
error ->
|
|
||||||
{error, bad_realm}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
do_list_keys2(Type, KeyIDs) ->
|
||||||
|
Exists = fun(ID) -> zx_key:exists(Type, ID) end,
|
||||||
|
{ok, lists:filter(Exists, KeyIDs)}.
|
||||||
|
|
||||||
|
|
||||||
-spec do_drop_realm(Realm, State) -> NewState
|
-spec do_drop_realm(Realm, State) -> NewState
|
||||||
when Realm :: zx:realm(),
|
when Realm :: zx:realm(),
|
||||||
State :: state(),
|
State :: state(),
|
||||||
NewState :: state().
|
NewState :: state().
|
||||||
|
|
||||||
do_drop_realm(Realm, State = #s{kx = {RealmIndex, Pairs, Owners}}) ->
|
do_drop_realm(Realm, State) ->
|
||||||
NewKX =
|
|
||||||
case maps:take(Realm, RealmIndex) of
|
|
||||||
{KeyIndex, NextRealmIndex} ->
|
|
||||||
NewPairs = scrub_pairs(maps:values(KeyIndex), Pairs),
|
|
||||||
Screen = fun({{R, _}, _}) -> R == Realm end,
|
|
||||||
NewOwners = maps:filter(Screen, Owners),
|
|
||||||
{NextRealmIndex, NewPairs, NewOwners};
|
|
||||||
error ->
|
|
||||||
{RealmIndex, Pairs, Owners}
|
|
||||||
end,
|
|
||||||
Dirs = [etc, var, tmp, log, key, zsp, lib],
|
Dirs = [etc, var, tmp, log, key, zsp, lib],
|
||||||
RM = fun(D) -> ok = zx_lib:rm_rf(zx_lib:path(D, Realm)) end,
|
RM = fun(D) -> ok = zx_lib:rm_rf(zx_lib:path(D, Realm)) end,
|
||||||
ok = lists:foreach(RM, Dirs),
|
ok = lists:foreach(RM, Dirs),
|
||||||
{_, NewState} = do_abdicate(Realm, State),
|
do_abdicate(Realm, State).
|
||||||
NewState#s{kx = NewKX}.
|
|
||||||
|
|
||||||
scrub_pairs([#key{pubhash = PubHash, keyhash = KeyHash} | Rest], Pairs) ->
|
|
||||||
scrub_pairs(Rest, maps:without([PubHash, KeyHash], Pairs));
|
|
||||||
scrub_pairs([], Pairs) ->
|
|
||||||
Pairs.
|
|
||||||
|
|
||||||
|
|
||||||
-spec become_proxy(State) -> NewState
|
-spec become_proxy(State) -> NewState
|
||||||
|
|||||||
@ -13,33 +13,15 @@
|
|||||||
-copyright("Craig Everett <zxq9@zxq9.com>").
|
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||||
-license("GPL-3.0").
|
-license("GPL-3.0").
|
||||||
|
|
||||||
-export([path/2,
|
-export([generate_rsa/1,
|
||||||
generate_rsa/1,
|
save/3, load/2,
|
||||||
load/2, sign/2, verify/3]).
|
save_bin/3, load_bin/2,
|
||||||
|
exists/2,
|
||||||
|
sign/2, verify/3]).
|
||||||
|
|
||||||
-include("zx_logger.hrl").
|
-include("zx_logger.hrl").
|
||||||
|
|
||||||
|
|
||||||
%%% Functions
|
|
||||||
|
|
||||||
-spec path(public | private, zx:key_id()) -> file:filename().
|
|
||||||
|
|
||||||
path(Type, {Realm, KeyHash}) ->
|
|
||||||
Size = byte_size(KeyHash) * 8,
|
|
||||||
<<N:Size>> = KeyHash,
|
|
||||||
String = integer_to_list(N, 36),
|
|
||||||
Name =
|
|
||||||
case Type of
|
|
||||||
public -> String ++ ".pub.der";
|
|
||||||
private -> String ++ ".key.der"
|
|
||||||
end,
|
|
||||||
zx_lib:path(key, Realm, Name).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%% Key generation
|
|
||||||
|
|
||||||
|
|
||||||
-spec generate_rsa(Owner) -> Result
|
-spec generate_rsa(Owner) -> Result
|
||||||
when Owner :: zx:realm() | zx:user_id(),
|
when Owner :: zx:realm() | zx:user_id(),
|
||||||
Result :: {ok, zx:key_hash()}
|
Result :: {ok, zx:key_hash()}
|
||||||
@ -170,27 +152,93 @@ openssl() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec save(Type, KeyID, Key) -> Result
|
||||||
|
when Type :: private | public,
|
||||||
|
KeyID :: zx:key_id(),
|
||||||
|
Key :: zx:key(),
|
||||||
|
Result :: ok
|
||||||
|
| {error, file:posix()}.
|
||||||
|
%% @private
|
||||||
|
%% Encode a key to DER and store it as a file.
|
||||||
|
|
||||||
|
save(Type, KeyID, Key) ->
|
||||||
|
KeyDER = public_key:der_encode(der_label(Type), Key),
|
||||||
|
save_bin(Type, KeyID, KeyDER).
|
||||||
|
|
||||||
|
|
||||||
-spec load(Type, KeyID) -> Result
|
-spec load(Type, KeyID) -> Result
|
||||||
when Type :: private | public,
|
when Type :: private | public,
|
||||||
KeyID :: zx:key_id(),
|
KeyID :: zx:key_id(),
|
||||||
Result :: {ok, DecodedKey :: term()}
|
Result :: {ok, DecodedKey :: zx:key()}
|
||||||
| {error, Reason :: file:posix()}.
|
| {error, file:posix()}.
|
||||||
%% @private
|
%% @private
|
||||||
%% Hide the details behind reading and loading DER encoded RSA key files.
|
%% Load and decode a DER encoded key from file.
|
||||||
|
|
||||||
load(Type, KeyID) ->
|
load(Type, KeyID) ->
|
||||||
DerType =
|
case load_bin(Type, KeyID) of
|
||||||
case Type of
|
{ok, KeyDER} -> {ok, public_key:der_decode(der_label(Type), KeyDER)};
|
||||||
private -> 'RSAPrivateKey';
|
|
||||||
public -> 'RSAPublicKey'
|
|
||||||
end,
|
|
||||||
Path = path(Type, KeyID),
|
|
||||||
case file:read_file(Path) of
|
|
||||||
{ok, Bin} -> {ok, public_key:der_decode(DerType, Bin)};
|
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec save_bin(Type, KeyID, Bin) -> Result
|
||||||
|
when Type :: private | public,
|
||||||
|
KeyID :: zx:key_id(),
|
||||||
|
Bin :: binary(),
|
||||||
|
Result :: ok
|
||||||
|
| {error, bad_realm | file:posix()}.
|
||||||
|
%% @private
|
||||||
|
%% Save a key's binary representation directly.
|
||||||
|
|
||||||
|
save_bin(Type, KeyID = {Realm, _}, Bin) ->
|
||||||
|
case zx_lib:realm_exists(Realm) of
|
||||||
|
true -> save_bin2(Type, KeyID, Bin);
|
||||||
|
false -> {error, bad_realm}
|
||||||
|
end.
|
||||||
|
|
||||||
|
save_bin2(Type, KeyID, Bin) ->
|
||||||
|
Path = path(Type, KeyID),
|
||||||
|
ok = filelib:ensure_dir(Path),
|
||||||
|
ok = log(info, "Saving ~p key to ~p.", [Type, Path]),
|
||||||
|
file:write_file(Path, Bin).
|
||||||
|
|
||||||
|
|
||||||
|
-spec load_bin(Type, KeyID) -> Result
|
||||||
|
when Type :: private | public,
|
||||||
|
KeyID :: zx:key_id(),
|
||||||
|
Result :: {ok, binary()}
|
||||||
|
| {error, bad_realm | no_key | no_pub | file:posix()}.
|
||||||
|
%% @private
|
||||||
|
%% Load a binary key's representation directly without decoding it.
|
||||||
|
|
||||||
|
load_bin(Type, KeyID = {Realm, _}) ->
|
||||||
|
case zx_lib:realm_exists(Realm) of
|
||||||
|
true -> load_bin2(Type, KeyID);
|
||||||
|
false -> {error, bad_realm}
|
||||||
|
end.
|
||||||
|
|
||||||
|
load_bin2(Type, KeyID) ->
|
||||||
|
Path = path(Type, KeyID),
|
||||||
|
ok = log(info, "Loading ~p key from ~p.", [Type, Path]),
|
||||||
|
case file:read_file(Path) of
|
||||||
|
{ok, Bin} ->
|
||||||
|
{ok, Bin};
|
||||||
|
{error, enoent} ->
|
||||||
|
Reason = case Type of private -> no_key; public -> no_pub end,
|
||||||
|
{error, Reason};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec exists(Type, KeyID) -> boolean()
|
||||||
|
when Type :: private | public,
|
||||||
|
KeyID :: zx:key_id().
|
||||||
|
|
||||||
|
exists(Type, KeyID) ->
|
||||||
|
filelib:is_regular(path(Type, KeyID)).
|
||||||
|
|
||||||
|
|
||||||
-spec sign(Data, Key) -> Signature
|
-spec sign(Data, Key) -> Signature
|
||||||
when Data :: binary(),
|
when Data :: binary(),
|
||||||
Key :: public_key:rsa_private_key(),
|
Key :: public_key:rsa_private_key(),
|
||||||
@ -210,3 +258,28 @@ sign(Data, Key) ->
|
|||||||
|
|
||||||
verify(Data, Signature, PubKey) ->
|
verify(Data, Signature, PubKey) ->
|
||||||
public_key:verify(Data, sha512, Signature, PubKey).
|
public_key:verify(Data, sha512, Signature, PubKey).
|
||||||
|
|
||||||
|
|
||||||
|
der_label(private) -> 'RSAPrivateKey';
|
||||||
|
der_label(public) -> 'RSAPublicKey'.
|
||||||
|
|
||||||
|
|
||||||
|
-spec path(public | private, zx:key_id()) -> file:filename().
|
||||||
|
|
||||||
|
path(Type, {Realm, KeyHash}) ->
|
||||||
|
Name = name(Type, KeyHash),
|
||||||
|
zx_lib:path(key, Realm, Name).
|
||||||
|
|
||||||
|
|
||||||
|
string(KeyHash) ->
|
||||||
|
Size = byte_size(KeyHash) * 8,
|
||||||
|
<<N:Size>> = KeyHash,
|
||||||
|
integer_to_list(N, 36).
|
||||||
|
|
||||||
|
|
||||||
|
name(Type, KeyHash) ->
|
||||||
|
String = string(KeyHash),
|
||||||
|
case Type of
|
||||||
|
public -> String ++ ".pub.der";
|
||||||
|
private -> String ++ ".key.der"
|
||||||
|
end.
|
||||||
|
|||||||
@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
-export([zomp_dir/0, find_zomp_dir/0,
|
-export([zomp_dir/0, find_zomp_dir/0,
|
||||||
path/1, path/2, path/3, path/4, ppath/2,
|
path/1, path/2, path/3, path/4, ppath/2,
|
||||||
new_logpath/1, userconf/1,
|
new_logpath/1,
|
||||||
force_dir/1, mktemp_dir/1, random_string/0,
|
force_dir/1, mktemp_dir/1, random_string/0,
|
||||||
list_realms/0, realm_exists/1,
|
list_realms/0, realm_exists/1,
|
||||||
get_prime/1, realm_meta/1,
|
get_prime/1,
|
||||||
read_project_meta/0, read_project_meta/1, read_package_meta/1,
|
read_project_meta/0, read_project_meta/1, read_package_meta/1,
|
||||||
write_project_meta/1, write_project_meta/2,
|
write_project_meta/1, write_project_meta/2,
|
||||||
write_terms/2, exec_shell/1,
|
write_terms/2, exec_shell/1,
|
||||||
@ -141,12 +141,6 @@ new_logpath(PackageID = {Realm, Name, _}) ->
|
|||||||
filename:join(Dir, FileName).
|
filename:join(Dir, FileName).
|
||||||
|
|
||||||
|
|
||||||
-spec userconf(zx:user_id()) -> file:filename().
|
|
||||||
|
|
||||||
userconf({Realm, UserName}) ->
|
|
||||||
filename:join(path(etc, Realm), UserName ++ ".user").
|
|
||||||
|
|
||||||
|
|
||||||
-spec force_dir(Path) -> Result
|
-spec force_dir(Path) -> Result
|
||||||
when Path :: file:filename(),
|
when Path :: file:filename(),
|
||||||
Result :: ok
|
Result :: ok
|
||||||
@ -205,31 +199,12 @@ realm_exists(Realm) ->
|
|||||||
%% Check the given Realm's config file for the current prime node and return it.
|
%% Check the given Realm's config file for the current prime node and return it.
|
||||||
|
|
||||||
get_prime(Realm) ->
|
get_prime(Realm) ->
|
||||||
case realm_meta(Realm) of
|
case load_realm_conf(Realm) of
|
||||||
{ok, RealmMeta} ->
|
{ok, RealmMeta} -> maps:find(prime, RealmMeta);
|
||||||
{prime, Prime} = lists:keyfind(prime, 1, RealmMeta),
|
Error -> Error
|
||||||
{ok, Prime};
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec realm_meta(Realm) -> Result
|
|
||||||
when Realm :: string(),
|
|
||||||
Result :: {ok, Meta}
|
|
||||||
| {error, Reason},
|
|
||||||
Meta :: [{atom(), term()}],
|
|
||||||
Reason :: file:posix().
|
|
||||||
%% @private
|
|
||||||
%% Given a realm name, try to locate and read the realm's configuration file if it
|
|
||||||
%% exists, exiting with an appropriate error message if there is a problem reading
|
|
||||||
%% the file.
|
|
||||||
|
|
||||||
realm_meta(Realm) ->
|
|
||||||
RealmFile = filename:join(path(etc, Realm), "realm.conf"),
|
|
||||||
file:consult(RealmFile).
|
|
||||||
|
|
||||||
|
|
||||||
-spec read_project_meta() -> Result
|
-spec read_project_meta() -> Result
|
||||||
when Result :: {ok, zx_zsp:meta()}
|
when Result :: {ok, zx_zsp:meta()}
|
||||||
| {error, file:posix()}.
|
| {error, file:posix()}.
|
||||||
@ -666,7 +641,8 @@ realm_conf(Realm) ->
|
|||||||
Result :: {ok, RealmConf}
|
Result :: {ok, RealmConf}
|
||||||
| {error, Reason},
|
| {error, Reason},
|
||||||
RealmConf :: map(),
|
RealmConf :: map(),
|
||||||
Reason :: badarg
|
Reason :: bad_realm
|
||||||
|
| badarg
|
||||||
| terminated
|
| terminated
|
||||||
| system_limit
|
| system_limit
|
||||||
| file:posix()
|
| file:posix()
|
||||||
@ -679,6 +655,8 @@ load_realm_conf(Realm) ->
|
|||||||
case file:consult(Path) of
|
case file:consult(Path) of
|
||||||
{ok, C} ->
|
{ok, C} ->
|
||||||
{ok, maps:from_list(C)};
|
{ok, maps:from_list(C)};
|
||||||
|
{error, enoent} ->
|
||||||
|
{error, bad_realm};
|
||||||
Error ->
|
Error ->
|
||||||
ok = log(warning, "Loading realm conf ~ts failed with: ~tw", [Path, Error]),
|
ok = log(warning, "Loading realm conf ~ts failed with: ~tw", [Path, Error]),
|
||||||
Error
|
Error
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
-export([initialize/0, set_version/1,
|
-export([initialize/0, set_version/1,
|
||||||
list_realms/0, list_packages/1, list_versions/1,
|
list_realms/0, list_packages/1, list_versions/1,
|
||||||
latest/1, describe/1, provides/1,
|
latest/1, describe/1, provides/1,
|
||||||
list_keys/0, list_sysops/1,
|
list_sysops/1,
|
||||||
set_dep/1, list_deps/0, list_deps/1, drop_dep/1, verup/1, package/1,
|
set_dep/1, list_deps/0, list_deps/1, drop_dep/1, verup/1, package/1,
|
||||||
update_meta/0, update_app_file/0,
|
update_meta/0, update_app_file/0,
|
||||||
import_realm/1, drop_realm/1, logpath/2,
|
import_realm/1, drop_realm/1, logpath/2,
|
||||||
@ -23,7 +23,7 @@
|
|||||||
drop_mirror/0, drop_mirror/1, drop_mirror/2,
|
drop_mirror/0, drop_mirror/1, drop_mirror/2,
|
||||||
create_project/0,
|
create_project/0,
|
||||||
grow_a_pair/0, grow_a_pair/2, drop_key/1,
|
grow_a_pair/0, grow_a_pair/2, drop_key/1,
|
||||||
create_user/0, create_userfile/0, export_user/0, import_user/1,
|
create_user/0, export_user/1, import_user/1,
|
||||||
create_realm/0, create_realmfile/0, create_realmfile/1]).
|
create_realm/0, create_realmfile/0, create_realmfile/1]).
|
||||||
|
|
||||||
-export([create_user/1, select_private_key/1]).
|
-export([create_user/1, select_private_key/1]).
|
||||||
@ -836,21 +836,6 @@ print_packages(PackageID) ->
|
|||||||
io:format("~ts~n", [PackageString]).
|
io:format("~ts~n", [PackageString]).
|
||||||
|
|
||||||
|
|
||||||
-spec list_keys() -> zx:outcome().
|
|
||||||
|
|
||||||
list_keys() ->
|
|
||||||
{ok, Realm} = pick_realm(),
|
|
||||||
UserName = select_user(Realm),
|
|
||||||
{ok, ID} = zx_daemon:list_keys({Realm, UserName}),
|
|
||||||
case zx_daemon:wait_result(ID) of
|
|
||||||
{ok, RemoteKeys} ->
|
|
||||||
Print = fun(KN) -> io:format("~ts~n", [KN]) end,
|
|
||||||
lists:foreach(Print, RemoteKeys);
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-spec list_sysops(zx:realm()) -> zx:outcome().
|
-spec list_sysops(zx:realm()) -> zx:outcome().
|
||||||
|
|
||||||
list_sysops(Realm) ->
|
list_sysops(Realm) ->
|
||||||
@ -1734,8 +1719,8 @@ package_exists({Realm, Package, _}) ->
|
|||||||
|
|
||||||
grow_a_pair() ->
|
grow_a_pair() ->
|
||||||
case pick_realm() of
|
case pick_realm() of
|
||||||
{error, no_realms} -> {error, "No realms configured.", 61};
|
{ok, Realm} -> grow_a_pair(Realm);
|
||||||
Realm -> grow_a_pair(Realm)
|
{error, no_realms} -> {error, "No realms configured.", 61}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
grow_a_pair(Realm) ->
|
grow_a_pair(Realm) ->
|
||||||
@ -1744,13 +1729,11 @@ grow_a_pair(Realm) ->
|
|||||||
|
|
||||||
grow_a_pair(Realm, UserName) ->
|
grow_a_pair(Realm, UserName) ->
|
||||||
case zx_key:generate_rsa(Realm) of
|
case zx_key:generate_rsa(Realm) of
|
||||||
{ok, PairHash} ->
|
{ok, KeyHash} ->
|
||||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
UserID = {Realm, UserName},
|
||||||
{ok, UserData} = file:consult(UserConf),
|
{ok, UserConf = #{keys := Keys}} = zx_userconf:load(UserID),
|
||||||
Keys = proplists:get_value(keys, UserData),
|
NewUserConf = UserConf#{keys => [KeyHash | Keys]},
|
||||||
NewKeys = [PairHash | Keys],
|
zx_userconf:save(NewUserConf);
|
||||||
NewUserData = lists:keystor(keys, 1, {keys, NewKeys}, UserData),
|
|
||||||
zx_lib:write_terms(UserConf, NewUserData);
|
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end.
|
end.
|
||||||
@ -1836,7 +1819,7 @@ store_realm(#realm_init{realm = Realm,
|
|||||||
RealmConfPath = filename:join(zx_lib:path(etc, Realm), "realm.conf"),
|
RealmConfPath = filename:join(zx_lib:path(etc, Realm), "realm.conf"),
|
||||||
ok = zx_lib:write_terms(RealmConfPath, RealmConf),
|
ok = zx_lib:write_terms(RealmConfPath, RealmConf),
|
||||||
ok = create_realmfile(Realm),
|
ok = create_realmfile(Realm),
|
||||||
ok = create_userfile(Realm, UserName),
|
ok = export_user(zpuf, Realm, UserName),
|
||||||
ZPUF = Realm ++ "-" ++ UserName ++ ".zpuf",
|
ZPUF = Realm ++ "-" ++ UserName ++ ".zpuf",
|
||||||
ZRF = Realm ++ ".zrf",
|
ZRF = Realm ++ ".zrf",
|
||||||
Message =
|
Message =
|
||||||
@ -2091,38 +2074,69 @@ store_user(#user_data{realm = Realm,
|
|||||||
realname = RealName,
|
realname = RealName,
|
||||||
contact_info = ContactInfo,
|
contact_info = ContactInfo,
|
||||||
keys = [KeyHash]}) ->
|
keys = [KeyHash]}) ->
|
||||||
UserConf =
|
UC = zx_userconf:new(),
|
||||||
[{realm, Realm},
|
NewUC =
|
||||||
{username, UserName},
|
UC#{realm := Realm,
|
||||||
{realname, RealName},
|
username := UserName,
|
||||||
{contact_info, ContactInfo},
|
realname := RealName,
|
||||||
{keys, [KeyHash]}],
|
contact_info := ContactInfo,
|
||||||
Path = zx_lib:userconf({Realm, UserName}),
|
keys := [KeyHash]},
|
||||||
ok = filelib:ensure_dir(Path),
|
zx_userconf:save(NewUC).
|
||||||
zx_lib:write_terms(Path, UserConf).
|
|
||||||
|
|
||||||
|
|
||||||
-spec create_userfile() -> ok.
|
-spec export_user(Type) -> ok
|
||||||
|
when Type :: zpuf | zduf.
|
||||||
|
|
||||||
create_userfile() ->
|
export_user(Type) ->
|
||||||
case pick_realm() of
|
case pick_realm() of
|
||||||
{error, no_realms} -> {error, "No realms configured.", 61};
|
{ok, Realm} -> export_user(Type, Realm);
|
||||||
Realm -> create_userfile(Realm)
|
{error, no_realms} -> {error, "No realms configured.", 61}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
create_userfile(Realm) ->
|
export_user(Type, Realm) ->
|
||||||
UserName = select_user(Realm),
|
UserName = select_user(Realm),
|
||||||
create_userfile(Realm, UserName).
|
export_user(Type, Realm, UserName).
|
||||||
|
|
||||||
|
|
||||||
create_userfile(Realm, UserName) ->
|
export_user(Type, Realm, UserName) ->
|
||||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
{ok, UserConf} = zx_userconf:load({Realm, UserName}),
|
||||||
{ok, UserData} = file:consult(UserConf),
|
Keys = maps:get(keys, UserConf),
|
||||||
Keys = proplists:get_value(keys, UserData),
|
{Load, Ext, Message} = exporter(Type, Realm),
|
||||||
|
KeyData = lists:foldl(Load, [], Keys),
|
||||||
|
UserFile = Realm ++ "-" ++ UserName ++ Ext,
|
||||||
|
UserData = maps:to_list(UserConf),
|
||||||
|
ok = tell(info, "UserData: ~p", [UserData]),
|
||||||
|
Bin = term_to_binary({UserData, KeyData}),
|
||||||
|
ok = file:write_file(UserFile, Bin),
|
||||||
|
io:format(Message, [UserFile, Realm]).
|
||||||
|
|
||||||
|
exporter(zduf, Realm) ->
|
||||||
Load =
|
Load =
|
||||||
fun(KeyName, Acc) ->
|
fun(KeyName, Acc) ->
|
||||||
case zx_daemon:keybin(public, {Realm, KeyName}) of
|
Key =
|
||||||
|
case zx_daemon:get_keybin(private, {Realm, KeyName}) of
|
||||||
|
{ok, KDer} -> {none, KDer};
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
Pub =
|
||||||
|
case zx_daemon:get_keybin(public, {Realm, KeyName}) of
|
||||||
|
{ok, PDer} -> {none, PDer};
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
[{KeyName, Pub, Key} | Acc]
|
||||||
|
end,
|
||||||
|
Ext = ".zduf",
|
||||||
|
Message =
|
||||||
|
"Wrote Zomp DANGEROUS user file to ~ts.~n"
|
||||||
|
"WARNING: This file contains your PRIVATE KEYS and should NEVER be shared.~n"
|
||||||
|
"Its only use is for the `import user [.zduf]` command!~n"
|
||||||
|
"Importing the user will only work if you have first imported realm ~ts.~n",
|
||||||
|
{Load, Ext, Message};
|
||||||
|
exporter(zpuf, Realm) ->
|
||||||
|
Load =
|
||||||
|
fun(KeyName, Acc) ->
|
||||||
|
case zx_daemon:get_keybin(public, {Realm, KeyName}) of
|
||||||
{ok, Der} ->
|
{ok, Der} ->
|
||||||
Public = {none, Der},
|
Public = {none, Der},
|
||||||
[{KeyName, Public, none} | Acc];
|
[{KeyName, Public, none} | Acc];
|
||||||
@ -2130,54 +2144,12 @@ create_userfile(Realm, UserName) ->
|
|||||||
Acc
|
Acc
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
PubKeyData = lists:foldl(Load, [], Keys),
|
Ext = ".zpuf",
|
||||||
UserFile = Realm ++ "-" ++ UserName ++ ".zpuf",
|
|
||||||
Bin = term_to_binary({UserData, PubKeyData}),
|
|
||||||
ok = file:write_file(UserFile, Bin),
|
|
||||||
Message =
|
Message =
|
||||||
"Wrote Zomp public user file to ~ts.~n"
|
"Wrote Zomp public user file to ~ts.~n"
|
||||||
"This file can be given to a sysop from ~ts and added to the realm.~n"
|
"This file can be given to a sysop from ~ts and added to the realm.~n"
|
||||||
"It ONLY contains PUBLIC KEY data.~n",
|
"It ONLY contains PUBLIC KEY data.~n",
|
||||||
io:format(Message, [UserFile, Realm]).
|
{Load, Ext, Message}.
|
||||||
|
|
||||||
|
|
||||||
-spec export_user() -> ok.
|
|
||||||
|
|
||||||
export_user() ->
|
|
||||||
case pick_realm() of
|
|
||||||
{error, no_realms} -> {error, "No realms configured.", 61};
|
|
||||||
Realm -> export_user(Realm)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
export_user(Realm) ->
|
|
||||||
UserName = select_user(Realm),
|
|
||||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
|
||||||
{ok, UserData} = file:consult(UserConf),
|
|
||||||
Keys = proplists:get_value(keys, UserData),
|
|
||||||
Load =
|
|
||||||
fun(KeyName, Acc) ->
|
|
||||||
Key =
|
|
||||||
case zx_daemon:keybin(private, {Realm, KeyName}) of
|
|
||||||
{ok, KDer} -> {none, KDer};
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
Pub =
|
|
||||||
case zx_daemon:keybin(public, {Realm, KeyName}) of
|
|
||||||
{ok, PDer} -> {none, PDer};
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
[{KeyName, Pub, Key} | Acc]
|
|
||||||
end,
|
|
||||||
KeyData = lists:foldl(Load, [], Keys),
|
|
||||||
UserFile = Realm ++ "-" ++ UserName ++ ".zduf",
|
|
||||||
Bin = term_to_binary({UserData, KeyData}),
|
|
||||||
ok = file:write_file(UserFile, Bin),
|
|
||||||
Message =
|
|
||||||
"Wrote Zomp DANGEROUS user file to ~ts.~n"
|
|
||||||
"WARNING: This file contains your PRIVATE KEYS and should NEVER be shared with "
|
|
||||||
"anyone. Its only use is for the `import user [.zduf]` command!~n",
|
|
||||||
io:format(Message, [UserFile]).
|
|
||||||
|
|
||||||
|
|
||||||
-spec import_user(file:filename()) -> zx:outcome().
|
-spec import_user(file:filename()) -> zx:outcome().
|
||||||
@ -2202,24 +2174,22 @@ import_user2(Bin) ->
|
|||||||
{error, "Bad data. Is this really a legitimate .zduf or .zpuf?", 1}
|
{error, "Bad data. Is this really a legitimate .zduf or .zpuf?", 1}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
import_user3(NewUserData, KeyData) ->
|
import_user3(UserData, KeyData) ->
|
||||||
Realm = proplists:get_value(realm, NewUserData),
|
UserConf = #{realm := Realm, username := UserName} = maps:from_list(UserData),
|
||||||
UserName = proplists:get_value(username, NewUserData),
|
UserID = {Realm, UserName},
|
||||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
case zx_userconf:load(UserID) of
|
||||||
case file:consult(UserConf) of
|
{ok, OldUserConf} ->
|
||||||
{ok, OldUserData} ->
|
NewUserConf = zx_userconf:merge(UserConf, OldUserConf, KeyData),
|
||||||
UpdatedUserData = merge_userconf(NewUserData, OldUserData, KeyData),
|
ok = zx_userconf:save(NewUserConf),
|
||||||
ok = zx_lib:write_terms(UserConf, UpdatedUserData),
|
|
||||||
import_user4(Realm, UserName, KeyData);
|
import_user4(Realm, UserName, KeyData);
|
||||||
{error, enoent} ->
|
{error, bad_user} ->
|
||||||
case filelib:is_dir(filename:dirname(UserConf)) of
|
ok = zx_userconf:save(UserConf),
|
||||||
true ->
|
|
||||||
ok = zx_lib:write_terms(UserConf, NewUserData),
|
|
||||||
import_user4(Realm, UserName, KeyData);
|
import_user4(Realm, UserName, KeyData);
|
||||||
false ->
|
{error, bad_realm} ->
|
||||||
ok = tell(error, "Realm ~tp is not configured.", [Realm]),
|
ok = tell(error, "Realm ~tp is not configured.", [Realm]),
|
||||||
{error, bad_realm}
|
{error, bad_realm};
|
||||||
end
|
Error ->
|
||||||
|
Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
import_user4(Realm, UserName, Keys) ->
|
import_user4(Realm, UserName, Keys) ->
|
||||||
@ -2227,16 +2197,6 @@ import_user4(Realm, UserName, Keys) ->
|
|||||||
ok = lists:foreach(Register, Keys),
|
ok = lists:foreach(Register, Keys),
|
||||||
tell(info, "Imported user ~ts to realm ~ts.", [UserName, Realm]).
|
tell(info, "Imported user ~ts to realm ~ts.", [UserName, Realm]).
|
||||||
|
|
||||||
merge_userconf(NewUserData, OldUserData, KeyData) ->
|
|
||||||
NewMap = maps:from_list(NewUserData),
|
|
||||||
OldMap = maps:from_list(OldUserData),
|
|
||||||
KeySet = sets:from_list([element(1, K) || K <- KeyData]),
|
|
||||||
NewKeySet = sets:from_list(maps:get(keys, NewMap)),
|
|
||||||
OldKeySet = sets:from_list(maps:get(keys, OldMap)),
|
|
||||||
UltimateKeySet = sets:intersection([KeySet, NewKeySet, OldKeySet]),
|
|
||||||
Final = maps:put(keys, sets:to_list(UltimateKeySet), maps:merge(OldMap, NewMap)),
|
|
||||||
maps:to_list(Final).
|
|
||||||
|
|
||||||
|
|
||||||
-spec pick_realm() -> Result
|
-spec pick_realm() -> Result
|
||||||
when Result :: {ok, zx:realm()}
|
when Result :: {ok, zx:realm()}
|
||||||
@ -2534,7 +2494,7 @@ create_realmfile(Realm) ->
|
|||||||
{ok, RealmConf} = zx_lib:load_realm_conf(Realm),
|
{ok, RealmConf} = zx_lib:load_realm_conf(Realm),
|
||||||
ok = tell("Realm found, creating realm file..."),
|
ok = tell("Realm found, creating realm file..."),
|
||||||
KeyName = maps:get(key, RealmConf),
|
KeyName = maps:get(key, RealmConf),
|
||||||
{ok, PubDER} = zx_daemon:keybin(public, {Realm, KeyName}),
|
{ok, PubDER} = zx_daemon:get_keybin(public, {Realm, KeyName}),
|
||||||
Blob = term_to_binary({RealmConf, PubDER}),
|
Blob = term_to_binary({RealmConf, PubDER}),
|
||||||
ZRF = Realm ++ ".zrf",
|
ZRF = Realm ++ ".zrf",
|
||||||
ok = file:write_file(ZRF, Blob),
|
ok = file:write_file(ZRF, Blob),
|
||||||
@ -2716,9 +2676,7 @@ select_user(Realm) ->
|
|||||||
| error.
|
| error.
|
||||||
|
|
||||||
select_private_key(UserID) ->
|
select_private_key(UserID) ->
|
||||||
ConfPath = zx_lib:userconf(UserID),
|
{ok, #{keys := Keys}} = zx_userconf:load(UserID),
|
||||||
{ok, UserConf} = file:consult(ConfPath),
|
|
||||||
Keys = proplists:get_value(keys, UserConf),
|
|
||||||
Realm = element(1, UserID),
|
Realm = element(1, UserID),
|
||||||
select_private_key2(Realm, Keys).
|
select_private_key2(Realm, Keys).
|
||||||
|
|
||||||
|
|||||||
87
zomp/lib/otpr/zx/0.3.0/src/zx_userconf.erl
Normal file
87
zomp/lib/otpr/zx/0.3.0/src/zx_userconf.erl
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
%%% @doc
|
||||||
|
%%% ZX UserConf
|
||||||
|
%%%
|
||||||
|
%%% Handling user configuration data.
|
||||||
|
%%% @end
|
||||||
|
|
||||||
|
-module(zx_userconf).
|
||||||
|
-vsn("0.3.0").
|
||||||
|
-author("Craig Everett <zxq9@zxq9.com>").
|
||||||
|
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||||
|
-license("GPL-3.0").
|
||||||
|
|
||||||
|
-export([new/0, save/1, load/1, merge/2, merge/3, path/1]).
|
||||||
|
|
||||||
|
-type data() :: #{realm := string(),
|
||||||
|
username := string(),
|
||||||
|
realname := string(),
|
||||||
|
contact_info := [zx:contact_info()],
|
||||||
|
keys := [binary()]}.
|
||||||
|
|
||||||
|
|
||||||
|
new() ->
|
||||||
|
#{realm => "",
|
||||||
|
username => "",
|
||||||
|
realname => "",
|
||||||
|
contact_info => "",
|
||||||
|
keys => []}.
|
||||||
|
|
||||||
|
|
||||||
|
-spec save(data()) -> ok | {error, file:posix()}.
|
||||||
|
|
||||||
|
save(Data = #{realm := Realm, username := UserName}) ->
|
||||||
|
Path = path({Realm, UserName}),
|
||||||
|
ok = filelib:ensure_dir(Path),
|
||||||
|
Terms = maps:to_list(Data),
|
||||||
|
zx_lib:write_terms(Path, Terms).
|
||||||
|
|
||||||
|
|
||||||
|
-spec load(UserID) -> Result
|
||||||
|
when UserID :: zx:user_id(),
|
||||||
|
Result :: {ok, zx:userconf()}
|
||||||
|
| {error, Reason},
|
||||||
|
Reason :: bad_realm
|
||||||
|
| bad_user
|
||||||
|
| file:posix().
|
||||||
|
|
||||||
|
load(UserID = {Realm, _}) ->
|
||||||
|
case zx_lib:realm_exists(Realm) of
|
||||||
|
true -> load2(UserID);
|
||||||
|
false -> {error, bad_realm}
|
||||||
|
end.
|
||||||
|
|
||||||
|
load2(UserID) ->
|
||||||
|
case file:consult(path(UserID)) of
|
||||||
|
{ok, Terms} -> {ok, maps:from_list(Terms)};
|
||||||
|
{error, enoent} -> {error, bad_user};
|
||||||
|
Error -> Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec merge(New, Old) -> Merged
|
||||||
|
when New :: data(),
|
||||||
|
Old :: data(),
|
||||||
|
Merged :: data().
|
||||||
|
|
||||||
|
merge(New, Old) ->
|
||||||
|
merge(New, Old, []).
|
||||||
|
|
||||||
|
|
||||||
|
-spec merge(New, Old, Keys) -> Merged
|
||||||
|
when New :: data(),
|
||||||
|
Old :: data(),
|
||||||
|
Keys :: [zx:key_data()],
|
||||||
|
Merged :: data().
|
||||||
|
|
||||||
|
merge(New, Old, KeyData) ->
|
||||||
|
KeySet = sets:from_list([element(1, K) || K <- KeyData]),
|
||||||
|
NewKeySet = sets:from_list(maps:get(keys, New)),
|
||||||
|
OldKeySet = sets:from_list(maps:get(keys, Old)),
|
||||||
|
UltimateKeySet = sets:intersection([KeySet, NewKeySet, OldKeySet]),
|
||||||
|
maps:put(keys, sets:to_list(UltimateKeySet), maps:merge(Old, New)).
|
||||||
|
|
||||||
|
|
||||||
|
-spec path(zx:user_id()) -> file:filename().
|
||||||
|
|
||||||
|
path({Realm, UserName}) ->
|
||||||
|
filename:join(zx_lib:path(etc, Realm), UserName ++ ".user").
|
||||||
Loading…
x
Reference in New Issue
Block a user