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,
|
||||
identifier/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,
|
||||
lower0_9/0, label/0,
|
||||
ss_tag/0, search_tag/0, description/0, package_type/0,
|
||||
@ -60,11 +60,12 @@
|
||||
Minor :: non_neg_integer() | z,
|
||||
Patch :: non_neg_integer() | z}.
|
||||
-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(),
|
||||
Public :: none | key_bin(),
|
||||
Private :: none | key_bin()}.
|
||||
-type key_bin() :: {Sig :: none | {key_name(), binary()},
|
||||
Der :: binary()}.
|
||||
DER :: binary()}.
|
||||
-type key_id() :: {realm(), key_name()}.
|
||||
-type key_name() :: key_hash().
|
||||
-type key_hash() :: binary().
|
||||
@ -225,12 +226,12 @@ do(["sync", "keys"]) ->
|
||||
done(zx_auth:sync_keys());
|
||||
do(["create", "user"]) ->
|
||||
done(zx_local:create_user());
|
||||
do(["create", "userfile"]) ->
|
||||
done(zx_local:create_userfile());
|
||||
do(["create", "keypair"]) ->
|
||||
done(zx_local:grow_a_pair());
|
||||
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]) ->
|
||||
done(zx_local:import_user(ZdufFile));
|
||||
do(["list", "users", Realm]) ->
|
||||
@ -888,9 +889,8 @@ usage_dev() ->
|
||||
" zx reject PackageID~n"
|
||||
" zx sync keys~n"
|
||||
" zx create user~n"
|
||||
" zx create userfile~n"
|
||||
" zx create keypair~n"
|
||||
" zx export user~n"
|
||||
" zx export user [dangerous]~n"
|
||||
" zx import user ZDUF~n"
|
||||
" zx list users Realm~n"
|
||||
" zx list packagers PackageName~n"
|
||||
|
||||
@ -307,6 +307,7 @@ add_user(ZPUF) ->
|
||||
add_user2(Bin) ->
|
||||
case zx_lib:b_to_t(Bin) of
|
||||
{ok, {UserInfo, KeyData}} -> add_user3(UserInfo, KeyData);
|
||||
{ok, _} -> {error, "Malformed user file.", 1};
|
||||
error -> {error, "Bad user file.", 1}
|
||||
end.
|
||||
|
||||
|
||||
@ -152,8 +152,8 @@
|
||||
list_sysops/1,
|
||||
fetch/1, install/1, build/1,
|
||||
wait_result/1, wait_results/1]).
|
||||
-export([register_key/2, get_key/2, keybin/2,
|
||||
find_keypair/1, have_key/2, list_keys/2]).
|
||||
-export([register_key/2, get_key/2, get_keybin/2,
|
||||
have_key/2, list_keys/2]).
|
||||
-export([report/1, result/2, notify/2]).
|
||||
-export([connect/0, disconnect/0]).
|
||||
-export([conf/1, conf/2, hosts/0,
|
||||
@ -187,8 +187,7 @@
|
||||
dropped = maps:new() :: requests(),
|
||||
timer = none :: none | reference(),
|
||||
mx = mx_new() :: monitor_index(),
|
||||
cx = new_cx() :: conn_index(),
|
||||
kx = load_kx() :: key_index()}).
|
||||
cx = new_cx() :: conn_index()}).
|
||||
|
||||
-record(conf,
|
||||
{realms = zx_lib:list_realms() :: [zx:realm()],
|
||||
@ -213,7 +212,7 @@
|
||||
-record(rmeta,
|
||||
{serial = 0 :: non_neg_integer(),
|
||||
prime = {"zomp.tsuriai.jp", 11311} :: zx:host(),
|
||||
key = [] :: zx:key_name(),
|
||||
key = none :: none | zx:key_name(),
|
||||
sysop = none :: zx:user_name(),
|
||||
assigned = none :: none | managed | pid(),
|
||||
available = [] :: [pid()]}).
|
||||
@ -224,12 +223,6 @@
|
||||
requests = [] :: [id()],
|
||||
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
|
||||
-type state() :: #s{}.
|
||||
@ -239,11 +232,6 @@
|
||||
| {unsubscribe, pid(), zx:package()}
|
||||
| {request, pid(), id(), 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 conn_index() :: #cx{} | zomp | proxy.
|
||||
-type realm_meta() :: #rmeta{}.
|
||||
@ -606,8 +594,8 @@ drop_mirror(Host) ->
|
||||
when Owner :: zx:realm() | zx:user_id(),
|
||||
KeyData :: zx:key_data().
|
||||
|
||||
register_key(Owner, Data) ->
|
||||
gen_server:call(?MODULE, {register_key, Owner, Data}).
|
||||
register_key(Owner, KeyData) ->
|
||||
gen_server:call(?MODULE, {register_key, Owner, KeyData}).
|
||||
|
||||
|
||||
-spec get_key(Type, KeyID) -> Result
|
||||
@ -624,23 +612,25 @@ 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,
|
||||
KeyID :: zx:key_id(),
|
||||
Result :: {ok, binary()}
|
||||
| {error, file:posix()}.
|
||||
|
||||
keybin(Type, KeyID) ->
|
||||
gen_server:call(?MODULE, {keybin, Type, KeyID}).
|
||||
get_keybin(Type, KeyID) ->
|
||||
gen_server:call(?MODULE, {get_keybin, Type, KeyID}).
|
||||
|
||||
|
||||
-spec find_keypair(KeyName) -> Result
|
||||
when KeyName :: zx:key_name(),
|
||||
Result :: {ok, zx:key_id()}
|
||||
| error.
|
||||
|
||||
find_keypair(KeyName) ->
|
||||
gen_server:call(?MODULE, {find_keypair, KeyName}).
|
||||
% TODO: This should be an external request to a Zomp node.
|
||||
% FIXME: Determine how this should work.
|
||||
%-spec find_keypair(KeyName) -> Result
|
||||
% when KeyName :: zx:key_name(),
|
||||
% Result :: {ok, zx:key_id()}
|
||||
% | error.
|
||||
%
|
||||
%find_keypair(KeyName) ->
|
||||
% gen_server:call(?MODULE, {find_keypair, KeyName}).
|
||||
|
||||
|
||||
-spec have_key(Type, KeyID) -> boolean()
|
||||
@ -814,29 +804,25 @@ handle_call({build, PackageID}, _, State) ->
|
||||
Result = do_build(PackageID),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({get_key, Type, KeyID}, _, State = #s{kx = KX}) ->
|
||||
Result = do_get_key(Type, KeyID, element(1, KX)),
|
||||
handle_call({get_key, Type, KeyID}, _, State) ->
|
||||
Result = do_get_key(Type, KeyID),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({keybin, Type, KeyID}, _, State = #s{kx = KX}) ->
|
||||
Result = do_keybin(Type, KeyID, element(1, KX)),
|
||||
handle_call({get_keybin, Type, KeyID}, _, State) ->
|
||||
Result = do_get_keybin(Type, KeyID),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({find_keypair, KeyName}, _, State = #s{kx = KX}) ->
|
||||
Result = maps:find(KeyName, element(2, KX)),
|
||||
handle_call({have_key, Type, KeyID}, _, State) ->
|
||||
Result = do_have_key(Type, KeyID),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({have_key, Type, KeyID}, _, State = #s{kx = KX}) ->
|
||||
Result = do_have_key(Type, KeyID, element(1, KX)),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({list_keys, Type, Owner}, _, State = #s{kx = KX}) ->
|
||||
Result = do_list_keys(Type, Owner, KX),
|
||||
handle_call({list_keys, Type, Owner}, _, State) ->
|
||||
Result = do_list_keys(Type, Owner),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({register_key, Owner, Data}, _, State) ->
|
||||
{Result, NextState} = do_register_key(Owner, Data, State),
|
||||
NewState = eval_queue(NextState),
|
||||
Result = do_register_key(Owner, Data),
|
||||
NewState = eval_queue(State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({takeover, Realm}, _, State = #s{conf = Conf}) ->
|
||||
{Result, NewConf} = do_takeover(Realm, Conf),
|
||||
@ -1868,11 +1854,9 @@ do_takeover(Realm, C = #conf{realms = Realms, managed = Managed}) ->
|
||||
end.
|
||||
|
||||
|
||||
-spec do_abdicate(Realm, State) -> {Result, NewState}
|
||||
-spec do_abdicate(Realm, State) -> NewState
|
||||
when Realm :: zx:realm(),
|
||||
State :: state(),
|
||||
Result :: ok
|
||||
| {error, unmanaged},
|
||||
NewState :: state().
|
||||
|
||||
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},
|
||||
ok = save_conf(NewC),
|
||||
ok = log(info, "No longer managing realm: ~160tp", [Realm]),
|
||||
{ok, State#s{conf = NewC}};
|
||||
State#s{conf = NewC};
|
||||
false ->
|
||||
ok = tell(error, "Cannot abdicate an unmanaged realm."),
|
||||
{{error, unmanaged}, State}
|
||||
State
|
||||
end.
|
||||
|
||||
|
||||
%%% Key Functions
|
||||
|
||||
-spec load_kx() -> key_index().
|
||||
|
||||
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}
|
||||
-spec do_register_key(Owner, KeyData) -> Result
|
||||
when Owner :: zx:realm() | zx:user_id(),
|
||||
KeyData :: zx:key_data(),
|
||||
State :: state(),
|
||||
NewState :: state().
|
||||
Result :: ok
|
||||
| {error, Reason},
|
||||
Reason :: bad_user
|
||||
| bad_realm
|
||||
| file:posix().
|
||||
|
||||
do_register_key(UserID = {Realm, _}, Data, State = #s{kx = {RX, PX, UK}}) ->
|
||||
{NewRX, NewPX} = do_register_realm_key(Realm, Data, RX, PX),
|
||||
NewUK =
|
||||
case Data of
|
||||
{PairHash, none, {_, K}} when is_binary(K) ->
|
||||
Update = fun({Ps, Ks}) -> {Ps, [PairHash | Ks]} end,
|
||||
maps:update_with(UserID, Update, {[], [PairHash]}, UK);
|
||||
{PairHash, {_, P}, none} when is_binary(P) ->
|
||||
Update = fun({Ps, Ks}) -> {[PairHash | Ps], Ks} end,
|
||||
maps:update_with(UserID, Update, {[PairHash], []}, UK);
|
||||
{PairHash, {_, P}, {_, K}} when is_binary(P), is_binary(K) ->
|
||||
Update = fun({Ps, Ks}) -> {[PairHash | Ps], [PairHash | Ks]} end,
|
||||
maps:update_with(UserID, Update, {[PairHash], [PairHash]}, UK)
|
||||
do_register_key(Owner = {Realm, UserName}, KeyData) ->
|
||||
case zx_userconf:load(Owner) of
|
||||
{ok, UC} ->
|
||||
do_register_key2(Realm, UC, KeyData);
|
||||
{error, bad_user} ->
|
||||
UC = zx_userconf:new(),
|
||||
NewUC = UC#{realm => Realm, username => UserName},
|
||||
do_register_key2(Realm, NewUC, KeyData);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
|
||||
do_register_key2(Realm, UC = #{keys := Keys}, KeyData = {KeyHash, _, _}) ->
|
||||
NewUC =
|
||||
case lists:member(KeyHash, Keys) of
|
||||
false -> UC#{keys => [KeyHash | Keys]};
|
||||
true -> UC
|
||||
end,
|
||||
NewKX = {NewRX, NewPX, NewUK},
|
||||
ok = stash_keys(NewKX),
|
||||
{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}}.
|
||||
ok = zx_userconf:save(NewUC),
|
||||
do_register_key3(Realm, KeyData).
|
||||
|
||||
do_register_realm_key(Realm, Data, RealmIndex, PairIndex) ->
|
||||
PairHash = element(1, Data),
|
||||
{K, Pairs} = do_register_realm_key2(Realm, Data),
|
||||
KeyRegistry = maps:get(Realm, RealmIndex, #{}),
|
||||
NewKeyRegistry = update_registry(PairHash, K, KeyRegistry),
|
||||
NewRealmIndex = maps:put(Realm, NewKeyRegistry, RealmIndex),
|
||||
NewPairIndex = maps:merge(PairIndex, Pairs),
|
||||
{NewRealmIndex, NewPairIndex}.
|
||||
|
||||
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).
|
||||
do_register_key3(Realm, {KeyHash, none, {_, Key}}) ->
|
||||
zx_key:save_bin(private, {Realm, KeyHash}, Key);
|
||||
do_register_key3(Realm, {KeyHash, {_, Pub}, none}) ->
|
||||
zx_key:save_bin(public, {Realm, KeyHash}, Pub);
|
||||
do_register_key3(Realm, {KeyHash, {_, Pub}, {_, Key}}) ->
|
||||
ok = zx_key:save_bin(public, {Realm, KeyHash}, Pub),
|
||||
zx_key:save(private, {Realm, KeyHash}, Key);
|
||||
do_register_key3(_, {_, none, none}) ->
|
||||
ok.
|
||||
|
||||
|
||||
-spec do_get_key(Type, KeyID, RK) -> Result
|
||||
-spec do_get_key(Type, KeyID) -> Result
|
||||
when Type :: public | private,
|
||||
KeyID :: zx:key_id(),
|
||||
RK :: rk_index(),
|
||||
Result :: {ok, public_key:rsa_public_key() | public_key:rsa_private_key()}
|
||||
| {error, Reason},
|
||||
Reason :: bad_realm
|
||||
| no_pub
|
||||
| no_key
|
||||
| bad_key.
|
||||
| file:posix().
|
||||
|
||||
do_get_key(Type, {Realm, KeyName}, RK) ->
|
||||
case maps:find(Realm, RK) of
|
||||
{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.
|
||||
do_get_key(Type, KeyID) ->
|
||||
zx_key:load(Type, KeyID).
|
||||
|
||||
|
||||
-spec do_keybin(Type, KeyID, RK) -> Result
|
||||
-spec do_get_keybin(Type, KeyID) -> Result
|
||||
when Type :: public | private,
|
||||
KeyID :: zx:key_id(),
|
||||
RK :: rk_index(),
|
||||
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) ->
|
||||
case maps:find(Realm, RK) of
|
||||
{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.
|
||||
do_get_keybin(Type, KeyID) ->
|
||||
zx_key:load_bin(Type, KeyID).
|
||||
|
||||
|
||||
-spec do_have_key(Type, KeyID, RK) -> boolean()
|
||||
-spec do_have_key(Type, KeyID) -> boolean()
|
||||
when Type :: public | private,
|
||||
KeyID :: zx:key_id(),
|
||||
RK :: rk_index().
|
||||
KeyID :: zx:key_id().
|
||||
|
||||
do_have_key(Type, {Realm, KeyName}, RK) ->
|
||||
case maps:find(Realm, RK) of
|
||||
{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.
|
||||
do_have_key(Type, KeyID) ->
|
||||
zx_key:exists(Type, KeyID).
|
||||
|
||||
|
||||
-spec do_list_keys(Type, Owner, KX) -> Result
|
||||
-spec do_list_keys(Type, Owner) -> Result
|
||||
when Type :: public | private,
|
||||
Owner :: zx:realm() | zx:user_id(),
|
||||
KX :: key_index(),
|
||||
Result :: {ok, [zx:key_hash()]}
|
||||
| {error, bad_realm | bad_user}.
|
||||
|
||||
do_list_keys(public, UserID, {_, _, UK}) when is_tuple(UserID) ->
|
||||
case maps:find(UserID, UK) of
|
||||
{ok, {Keys, _}} -> {ok, Keys};
|
||||
error -> {error, bad_user}
|
||||
do_list_keys(Type, UserID = {Realm, _}) ->
|
||||
case zx_userconf:load(UserID) of
|
||||
{ok, #{keys := Keys}} -> do_list_keys2(Type, [{Realm, K} || K <- Keys]);
|
||||
Error -> Error
|
||||
end;
|
||||
do_list_keys(private, UserID, {_, _, UK}) when is_tuple(UserID) ->
|
||||
case maps:find(UserID, UK) of
|
||||
{ok, {_, Keys}} -> {ok, Keys};
|
||||
error -> {error, bad_user}
|
||||
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}
|
||||
do_list_keys(Type, Realm) ->
|
||||
case zx_lib:load_realm_conf(Realm) of
|
||||
{ok, #{key := Key}} -> do_list_keys2(Type, [{Realm, Key}]);
|
||||
Error -> Error
|
||||
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
|
||||
when Realm :: zx:realm(),
|
||||
State :: state(),
|
||||
NewState :: state().
|
||||
|
||||
do_drop_realm(Realm, State = #s{kx = {RealmIndex, Pairs, Owners}}) ->
|
||||
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,
|
||||
do_drop_realm(Realm, State) ->
|
||||
Dirs = [etc, var, tmp, log, key, zsp, lib],
|
||||
RM = fun(D) -> ok = zx_lib:rm_rf(zx_lib:path(D, Realm)) end,
|
||||
ok = lists:foreach(RM, Dirs),
|
||||
{_, NewState} = 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.
|
||||
do_abdicate(Realm, State).
|
||||
|
||||
|
||||
-spec become_proxy(State) -> NewState
|
||||
|
||||
@ -13,33 +13,15 @@
|
||||
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||
-license("GPL-3.0").
|
||||
|
||||
-export([path/2,
|
||||
generate_rsa/1,
|
||||
load/2, sign/2, verify/3]).
|
||||
-export([generate_rsa/1,
|
||||
save/3, load/2,
|
||||
save_bin/3, load_bin/2,
|
||||
exists/2,
|
||||
sign/2, verify/3]).
|
||||
|
||||
-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
|
||||
when Owner :: zx:realm() | zx:user_id(),
|
||||
Result :: {ok, zx:key_hash()}
|
||||
@ -170,27 +152,93 @@ openssl() ->
|
||||
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
|
||||
when Type :: private | public,
|
||||
KeyID :: zx:key_id(),
|
||||
Result :: {ok, DecodedKey :: term()}
|
||||
| {error, Reason :: file:posix()}.
|
||||
Result :: {ok, DecodedKey :: zx:key()}
|
||||
| {error, file:posix()}.
|
||||
%% @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) ->
|
||||
DerType =
|
||||
case Type of
|
||||
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
|
||||
case load_bin(Type, KeyID) of
|
||||
{ok, KeyDER} -> {ok, public_key:der_decode(der_label(Type), KeyDER)};
|
||||
Error -> Error
|
||||
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
|
||||
when Data :: binary(),
|
||||
Key :: public_key:rsa_private_key(),
|
||||
@ -210,3 +258,28 @@ sign(Data, Key) ->
|
||||
|
||||
verify(Data, 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,
|
||||
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,
|
||||
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,
|
||||
write_project_meta/1, write_project_meta/2,
|
||||
write_terms/2, exec_shell/1,
|
||||
@ -141,12 +141,6 @@ new_logpath(PackageID = {Realm, Name, _}) ->
|
||||
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
|
||||
when Path :: file:filename(),
|
||||
Result :: ok
|
||||
@ -205,31 +199,12 @@ realm_exists(Realm) ->
|
||||
%% Check the given Realm's config file for the current prime node and return it.
|
||||
|
||||
get_prime(Realm) ->
|
||||
case realm_meta(Realm) of
|
||||
{ok, RealmMeta} ->
|
||||
{prime, Prime} = lists:keyfind(prime, 1, RealmMeta),
|
||||
{ok, Prime};
|
||||
Error ->
|
||||
Error
|
||||
case load_realm_conf(Realm) of
|
||||
{ok, RealmMeta} -> maps:find(prime, RealmMeta);
|
||||
Error -> Error
|
||||
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
|
||||
when Result :: {ok, zx_zsp:meta()}
|
||||
| {error, file:posix()}.
|
||||
@ -666,7 +641,8 @@ realm_conf(Realm) ->
|
||||
Result :: {ok, RealmConf}
|
||||
| {error, Reason},
|
||||
RealmConf :: map(),
|
||||
Reason :: badarg
|
||||
Reason :: bad_realm
|
||||
| badarg
|
||||
| terminated
|
||||
| system_limit
|
||||
| file:posix()
|
||||
@ -679,6 +655,8 @@ load_realm_conf(Realm) ->
|
||||
case file:consult(Path) of
|
||||
{ok, C} ->
|
||||
{ok, maps:from_list(C)};
|
||||
{error, enoent} ->
|
||||
{error, bad_realm};
|
||||
Error ->
|
||||
ok = log(warning, "Loading realm conf ~ts failed with: ~tw", [Path, Error]),
|
||||
Error
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
-export([initialize/0, set_version/1,
|
||||
list_realms/0, list_packages/1, list_versions/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,
|
||||
update_meta/0, update_app_file/0,
|
||||
import_realm/1, drop_realm/1, logpath/2,
|
||||
@ -23,7 +23,7 @@
|
||||
drop_mirror/0, drop_mirror/1, drop_mirror/2,
|
||||
create_project/0,
|
||||
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]).
|
||||
|
||||
-export([create_user/1, select_private_key/1]).
|
||||
@ -836,21 +836,6 @@ print_packages(PackageID) ->
|
||||
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().
|
||||
|
||||
list_sysops(Realm) ->
|
||||
@ -1734,8 +1719,8 @@ package_exists({Realm, Package, _}) ->
|
||||
|
||||
grow_a_pair() ->
|
||||
case pick_realm() of
|
||||
{error, no_realms} -> {error, "No realms configured.", 61};
|
||||
Realm -> grow_a_pair(Realm)
|
||||
{ok, Realm} -> grow_a_pair(Realm);
|
||||
{error, no_realms} -> {error, "No realms configured.", 61}
|
||||
end.
|
||||
|
||||
grow_a_pair(Realm) ->
|
||||
@ -1744,13 +1729,11 @@ grow_a_pair(Realm) ->
|
||||
|
||||
grow_a_pair(Realm, UserName) ->
|
||||
case zx_key:generate_rsa(Realm) of
|
||||
{ok, PairHash} ->
|
||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
||||
{ok, UserData} = file:consult(UserConf),
|
||||
Keys = proplists:get_value(keys, UserData),
|
||||
NewKeys = [PairHash | Keys],
|
||||
NewUserData = lists:keystor(keys, 1, {keys, NewKeys}, UserData),
|
||||
zx_lib:write_terms(UserConf, NewUserData);
|
||||
{ok, KeyHash} ->
|
||||
UserID = {Realm, UserName},
|
||||
{ok, UserConf = #{keys := Keys}} = zx_userconf:load(UserID),
|
||||
NewUserConf = UserConf#{keys => [KeyHash | Keys]},
|
||||
zx_userconf:save(NewUserConf);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
@ -1836,7 +1819,7 @@ store_realm(#realm_init{realm = Realm,
|
||||
RealmConfPath = filename:join(zx_lib:path(etc, Realm), "realm.conf"),
|
||||
ok = zx_lib:write_terms(RealmConfPath, RealmConf),
|
||||
ok = create_realmfile(Realm),
|
||||
ok = create_userfile(Realm, UserName),
|
||||
ok = export_user(zpuf, Realm, UserName),
|
||||
ZPUF = Realm ++ "-" ++ UserName ++ ".zpuf",
|
||||
ZRF = Realm ++ ".zrf",
|
||||
Message =
|
||||
@ -2091,38 +2074,69 @@ store_user(#user_data{realm = Realm,
|
||||
realname = RealName,
|
||||
contact_info = ContactInfo,
|
||||
keys = [KeyHash]}) ->
|
||||
UserConf =
|
||||
[{realm, Realm},
|
||||
{username, UserName},
|
||||
{realname, RealName},
|
||||
{contact_info, ContactInfo},
|
||||
{keys, [KeyHash]}],
|
||||
Path = zx_lib:userconf({Realm, UserName}),
|
||||
ok = filelib:ensure_dir(Path),
|
||||
zx_lib:write_terms(Path, UserConf).
|
||||
UC = zx_userconf:new(),
|
||||
NewUC =
|
||||
UC#{realm := Realm,
|
||||
username := UserName,
|
||||
realname := RealName,
|
||||
contact_info := ContactInfo,
|
||||
keys := [KeyHash]},
|
||||
zx_userconf:save(NewUC).
|
||||
|
||||
|
||||
-spec create_userfile() -> ok.
|
||||
-spec export_user(Type) -> ok
|
||||
when Type :: zpuf | zduf.
|
||||
|
||||
create_userfile() ->
|
||||
export_user(Type) ->
|
||||
case pick_realm() of
|
||||
{error, no_realms} -> {error, "No realms configured.", 61};
|
||||
Realm -> create_userfile(Realm)
|
||||
{ok, Realm} -> export_user(Type, Realm);
|
||||
{error, no_realms} -> {error, "No realms configured.", 61}
|
||||
end.
|
||||
|
||||
|
||||
create_userfile(Realm) ->
|
||||
export_user(Type, Realm) ->
|
||||
UserName = select_user(Realm),
|
||||
create_userfile(Realm, UserName).
|
||||
export_user(Type, Realm, UserName).
|
||||
|
||||
|
||||
create_userfile(Realm, UserName) ->
|
||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
||||
{ok, UserData} = file:consult(UserConf),
|
||||
Keys = proplists:get_value(keys, UserData),
|
||||
export_user(Type, Realm, UserName) ->
|
||||
{ok, UserConf} = zx_userconf:load({Realm, UserName}),
|
||||
Keys = maps:get(keys, UserConf),
|
||||
{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 =
|
||||
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} ->
|
||||
Public = {none, Der},
|
||||
[{KeyName, Public, none} | Acc];
|
||||
@ -2130,54 +2144,12 @@ create_userfile(Realm, UserName) ->
|
||||
Acc
|
||||
end
|
||||
end,
|
||||
PubKeyData = lists:foldl(Load, [], Keys),
|
||||
UserFile = Realm ++ "-" ++ UserName ++ ".zpuf",
|
||||
Bin = term_to_binary({UserData, PubKeyData}),
|
||||
ok = file:write_file(UserFile, Bin),
|
||||
Ext = ".zpuf",
|
||||
Message =
|
||||
"Wrote Zomp public user file to ~ts.~n"
|
||||
"This file can be given to a sysop from ~ts and added to the realm.~n"
|
||||
"It ONLY contains PUBLIC KEY data.~n",
|
||||
io:format(Message, [UserFile, Realm]).
|
||||
|
||||
|
||||
-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]).
|
||||
{Load, Ext, Message}.
|
||||
|
||||
|
||||
-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}
|
||||
end.
|
||||
|
||||
import_user3(NewUserData, KeyData) ->
|
||||
Realm = proplists:get_value(realm, NewUserData),
|
||||
UserName = proplists:get_value(username, NewUserData),
|
||||
UserConf = zx_lib:userconf({Realm, UserName}),
|
||||
case file:consult(UserConf) of
|
||||
{ok, OldUserData} ->
|
||||
UpdatedUserData = merge_userconf(NewUserData, OldUserData, KeyData),
|
||||
ok = zx_lib:write_terms(UserConf, UpdatedUserData),
|
||||
import_user3(UserData, KeyData) ->
|
||||
UserConf = #{realm := Realm, username := UserName} = maps:from_list(UserData),
|
||||
UserID = {Realm, UserName},
|
||||
case zx_userconf:load(UserID) of
|
||||
{ok, OldUserConf} ->
|
||||
NewUserConf = zx_userconf:merge(UserConf, OldUserConf, KeyData),
|
||||
ok = zx_userconf:save(NewUserConf),
|
||||
import_user4(Realm, UserName, KeyData);
|
||||
{error, enoent} ->
|
||||
case filelib:is_dir(filename:dirname(UserConf)) of
|
||||
true ->
|
||||
ok = zx_lib:write_terms(UserConf, NewUserData),
|
||||
import_user4(Realm, UserName, KeyData);
|
||||
false ->
|
||||
ok = tell(error, "Realm ~tp is not configured.", [Realm]),
|
||||
{error, bad_realm}
|
||||
end
|
||||
{error, bad_user} ->
|
||||
ok = zx_userconf:save(UserConf),
|
||||
import_user4(Realm, UserName, KeyData);
|
||||
{error, bad_realm} ->
|
||||
ok = tell(error, "Realm ~tp is not configured.", [Realm]),
|
||||
{error, bad_realm};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
import_user4(Realm, UserName, Keys) ->
|
||||
@ -2227,16 +2197,6 @@ import_user4(Realm, UserName, Keys) ->
|
||||
ok = lists:foreach(Register, Keys),
|
||||
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
|
||||
when Result :: {ok, zx:realm()}
|
||||
@ -2534,7 +2494,7 @@ create_realmfile(Realm) ->
|
||||
{ok, RealmConf} = zx_lib:load_realm_conf(Realm),
|
||||
ok = tell("Realm found, creating realm file..."),
|
||||
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}),
|
||||
ZRF = Realm ++ ".zrf",
|
||||
ok = file:write_file(ZRF, Blob),
|
||||
@ -2716,9 +2676,7 @@ select_user(Realm) ->
|
||||
| error.
|
||||
|
||||
select_private_key(UserID) ->
|
||||
ConfPath = zx_lib:userconf(UserID),
|
||||
{ok, UserConf} = file:consult(ConfPath),
|
||||
Keys = proplists:get_value(keys, UserConf),
|
||||
{ok, #{keys := Keys}} = zx_userconf:load(UserID),
|
||||
Realm = element(1, UserID),
|
||||
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