wip
This commit is contained in:
parent
b64ddffe8b
commit
996e2e29b2
350
zx
350
zx
@ -46,6 +46,8 @@
|
|||||||
% DER :: binary()}.
|
% DER :: binary()}.
|
||||||
-type key_id() :: {realm(), key_name()}.
|
-type key_id() :: {realm(), key_name()}.
|
||||||
-type key_name() :: label().
|
-type key_name() :: label().
|
||||||
|
-type user() :: {realm(), username()}.
|
||||||
|
-type username() :: label().
|
||||||
-type lower0_9() :: [$a..$z | $0..$9 | $_].
|
-type lower0_9() :: [$a..$z | $0..$9 | $_].
|
||||||
-type label() :: [$a..$z | $0..$9 | $_ | $- | $.].
|
-type label() :: [$a..$z | $0..$9 | $_ | $- | $.].
|
||||||
-type package_meta() :: map().
|
-type package_meta() :: map().
|
||||||
@ -87,6 +89,8 @@ start(["set", "version", VersionString]) ->
|
|||||||
set_version(VersionString);
|
set_version(VersionString);
|
||||||
start(["add", "realm", RealmFile]) ->
|
start(["add", "realm", RealmFile]) ->
|
||||||
add_realm(RealmFile);
|
add_realm(RealmFile);
|
||||||
|
start(["add", "package", PackageName]) ->
|
||||||
|
add_package(PackageName);
|
||||||
start(["drop", "dep", PackageString]) ->
|
start(["drop", "dep", PackageString]) ->
|
||||||
PackageID = package_id(PackageString),
|
PackageID = package_id(PackageString),
|
||||||
drop_dep(PackageID);
|
drop_dep(PackageID);
|
||||||
@ -584,6 +588,67 @@ add_realm(Path, Data) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec add_package(PackageName) -> no_return()
|
||||||
|
when PackageName :: package().
|
||||||
|
|
||||||
|
add_package(PackageName) ->
|
||||||
|
ok = file:set_cwd(zomp_dir()),
|
||||||
|
case string:lexemes(PackageName, "-") of
|
||||||
|
[Realm, Name] ->
|
||||||
|
case {valid_lower0_9(Realm), valid_lower0_9(Name)} of
|
||||||
|
{true, true} ->
|
||||||
|
add_package(Realm, Name);
|
||||||
|
{false, true} ->
|
||||||
|
ok = log(warning, "Invalid realm name: ~tp", [Realm]),
|
||||||
|
halt(1);
|
||||||
|
{true, false} ->
|
||||||
|
ok = log(warning, "Invalid package name: ~tp", [Name]),
|
||||||
|
halt(1);
|
||||||
|
{false, false} ->
|
||||||
|
ok = log(warning, "Invalid realm and package names."),
|
||||||
|
halt(1)
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
ok = log(warning, "Name ~tp is not a valid package name.", [PackageName]),
|
||||||
|
halt(1)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec add_package(Realm, Name) -> no_return()
|
||||||
|
when Realm :: realm(),
|
||||||
|
Name :: name().
|
||||||
|
%% @private
|
||||||
|
%% This sysop-only command can add a package to a realm operated by the caller.
|
||||||
|
|
||||||
|
add_package(Realm, Name) ->
|
||||||
|
Socket =
|
||||||
|
case connect_auth(Realm) of
|
||||||
|
{ok, S} ->
|
||||||
|
S;
|
||||||
|
Error ->
|
||||||
|
M1 = "Connection failed to realm prime with ~160tp.",
|
||||||
|
ok = log(warning, M1, [Error]),
|
||||||
|
halt(1)
|
||||||
|
end,
|
||||||
|
ok = send(Socket, {add_package, {Realm, Name}}),
|
||||||
|
receive
|
||||||
|
{tcp, Socket, Bin} ->
|
||||||
|
case binary_to_term(Bin, [safe]) of
|
||||||
|
ok ->
|
||||||
|
ok = log(info, "\"~ts-~ts\" added successfully.", [Realm, Name]),
|
||||||
|
halt(0);
|
||||||
|
{error, Reason} ->
|
||||||
|
M2 = "Operation failed. Server sends reason: ~160tp",
|
||||||
|
ok = log(error, M2, [Reason]),
|
||||||
|
halt(1)
|
||||||
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
|
after 5000 ->
|
||||||
|
ok = log(warning, "Operation timed After submission to server."),
|
||||||
|
halt(1)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%% Drop dependency
|
%%% Drop dependency
|
||||||
|
|
||||||
@ -649,7 +714,10 @@ drop_realm(Realm) ->
|
|||||||
case get_input() of
|
case get_input() of
|
||||||
"Y" ->
|
"Y" ->
|
||||||
ok = file:delete(RealmConf),
|
ok = file:delete(RealmConf),
|
||||||
clear_keys(Realm);
|
ok = drop_prime(Realm),
|
||||||
|
ok = clear_keys(Realm),
|
||||||
|
ok = log(info, "All traces of realm ~ts have been removed."),
|
||||||
|
halt(0);
|
||||||
_ ->
|
_ ->
|
||||||
ok = log(info, "Aborting."),
|
ok = log(info, "Aborting."),
|
||||||
halt(0)
|
halt(0)
|
||||||
@ -659,8 +727,23 @@ drop_realm(Realm) ->
|
|||||||
clear_keys(Realm)
|
clear_keys(Realm)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec drop_prime(realm()) -> ok.
|
||||||
|
|
||||||
-spec clear_keys(realm()) -> no_return().
|
drop_prime(Realm) ->
|
||||||
|
Path = "zomp.conf",
|
||||||
|
case file:consult(Path) of
|
||||||
|
{ok, Conf} ->
|
||||||
|
{managed, Primes} = lists:keyfind(managed, 1, Conf),
|
||||||
|
NewPrimes = lists:delete(Realm, Primes),
|
||||||
|
NewConf = lists:keystore(managed, 1, Primes, {managed, NewPrimes}),
|
||||||
|
ok = write_terms(Path, NewConf),
|
||||||
|
log(info, "Ensuring ~ts is not a prime in ~ts", [Realm, Path]);
|
||||||
|
{error, enoent} ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec clear_keys(realm()) -> ok.
|
||||||
|
|
||||||
clear_keys(Realm) ->
|
clear_keys(Realm) ->
|
||||||
KeyDir = filename:join([zomp_dir(), "key", Realm]),
|
KeyDir = filename:join([zomp_dir(), "key", Realm]),
|
||||||
@ -671,11 +754,9 @@ clear_keys(Realm) ->
|
|||||||
Delete = fun(K) -> file:delete(K) end,
|
Delete = fun(K) -> file:delete(K) end,
|
||||||
ok = lists:foreach(Delete, Keys),
|
ok = lists:foreach(Delete, Keys),
|
||||||
ok = file:del_dir(KeyDir),
|
ok = file:del_dir(KeyDir),
|
||||||
ok = log(info, "Done!"),
|
log(info, "Done!");
|
||||||
halt(0);
|
|
||||||
false ->
|
false ->
|
||||||
ok = log(warning, "Keydir ~ts not found", [KeyDir]),
|
log(warning, "Keydir ~ts not found", [KeyDir])
|
||||||
halt(1)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
@ -726,13 +807,22 @@ run_local(Args) ->
|
|||||||
execute(State, Args).
|
execute(State, Args).
|
||||||
|
|
||||||
|
|
||||||
|
-spec execute(State, Args) -> no_return()
|
||||||
|
when State :: state(),
|
||||||
|
Args :: [string()].
|
||||||
|
%% @private
|
||||||
|
%% Gets all the target application's ducks in a row and launches them, then enters
|
||||||
|
%% the exec_wait/1 loop to wait for any queries from the application.
|
||||||
|
|
||||||
execute(State = #s{type = app, realm = Realm, name = Name, version = Version}, Args) ->
|
execute(State = #s{type = app, realm = Realm, name = Name, version = Version}, Args) ->
|
||||||
true = register(zx, self()),
|
true = register(zx, self()),
|
||||||
ok = inets:start(),
|
ok = inets:start(),
|
||||||
ok = log(info, "Starting ~ts", [package_string({Realm, Name, Version})]),
|
ok = log(info, "Starting ~ts", [package_string({Realm, Name, Version})]),
|
||||||
{ok, Apps} = application:ensure_all_started(list_to_atom(Name)),
|
AppMod = list_to_atom(Name),
|
||||||
|
{ok, Apps} = application:ensure_all_started(AppMod),
|
||||||
ok = log(info, "Started, ~tp", [Apps]),
|
ok = log(info, "Started, ~tp", [Apps]),
|
||||||
exec_wait(State#s{});
|
ok = pass_argv(AppMod, Args),
|
||||||
|
exec_wait(State);
|
||||||
execute(#s{type = lib, realm = Realm, name = Name, version = Version}, _) ->
|
execute(#s{type = lib, realm = Realm, name = Name, version = Version}, _) ->
|
||||||
Message = "Lib ~ts is available on the system, but is not a standalone app.",
|
Message = "Lib ~ts is available on the system, but is not a standalone app.",
|
||||||
PackageString = package_string({Realm, Name, Version}),
|
PackageString = package_string({Realm, Name, Version}),
|
||||||
@ -740,6 +830,20 @@ execute(#s{type = lib, realm = Realm, name = Name, version = Version}, _) ->
|
|||||||
halt(0).
|
halt(0).
|
||||||
|
|
||||||
|
|
||||||
|
-spec pass_argv(AppMod, Args) -> ok
|
||||||
|
when AppMod :: module(),
|
||||||
|
Args :: [string()].
|
||||||
|
%% @private
|
||||||
|
%% Check whether the AppMod:accept_argv/1 is implemented. If so, pass in the
|
||||||
|
%% command line arguments provided.
|
||||||
|
|
||||||
|
pass_argv(AppMod, Args) ->
|
||||||
|
case lists:member({accept_argv, 1}, AppMod:module_info(exports)) of
|
||||||
|
true -> AppMod:accept_argv(Args);
|
||||||
|
false -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%% Package generation
|
%%% Package generation
|
||||||
|
|
||||||
@ -848,6 +952,15 @@ remove_binaries(TargetDir) ->
|
|||||||
%% the registered zompc process convert itself to a gen_server via zompc_lib to
|
%% the registered zompc process convert itself to a gen_server via zompc_lib to
|
||||||
%% provide more advanced functionality?)
|
%% provide more advanced functionality?)
|
||||||
|
|
||||||
|
exec_wait(State = #s{pid = none, mon = none}) ->
|
||||||
|
receive
|
||||||
|
{monitor, Pid} ->
|
||||||
|
Mon = monitor(process, Pid),
|
||||||
|
exec_wait(State#s{pid = Pid, mon = Mon});
|
||||||
|
Unexpected ->
|
||||||
|
ok = log(warning, "Unexpected message: ~tp", [Unexpected]),
|
||||||
|
exec_wait(State)
|
||||||
|
end;
|
||||||
exec_wait(State = #s{pid = Pid, mon = Mon}) ->
|
exec_wait(State = #s{pid = Pid, mon = Mon}) ->
|
||||||
receive
|
receive
|
||||||
{check_update, Requester, Ref} ->
|
{check_update, Requester, Ref} ->
|
||||||
@ -899,9 +1012,7 @@ submit(PackageFile) ->
|
|||||||
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
|
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
|
||||||
Meta = binary_to_term(MetaBin),
|
Meta = binary_to_term(MetaBin),
|
||||||
{package_id, {Realm, Package, Version}} = lists:keyfind(package_id, 1, Meta),
|
{package_id, {Realm, Package, Version}} = lists:keyfind(package_id, 1, Meta),
|
||||||
{sig, {KeyID = {Realm, KeyName}, _}} = lists:keyfind(sig, 1, Meta),
|
{ok, Socket} = connect_auth(Realm),
|
||||||
true = ensure_keypair(KeyID),
|
|
||||||
{ok, Socket} = connect_auth(Realm, KeyName),
|
|
||||||
ok = send(Socket, {submit, {Realm, Package, Version}}),
|
ok = send(Socket, {submit, {Realm, Package, Version}}),
|
||||||
ok =
|
ok =
|
||||||
receive
|
receive
|
||||||
@ -912,7 +1023,9 @@ submit(PackageFile) ->
|
|||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
ok = log(info, "Server refused with ~tp", [Reason]),
|
ok = log(info, "Server refused with ~tp", [Reason]),
|
||||||
halt(0)
|
halt(0)
|
||||||
end
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 5000 ->
|
after 5000 ->
|
||||||
ok = log(warning, "Server timed out!"),
|
ok = log(warning, "Server timed out!"),
|
||||||
halt(0)
|
halt(0)
|
||||||
@ -922,7 +1035,9 @@ submit(PackageFile) ->
|
|||||||
ok =
|
ok =
|
||||||
receive
|
receive
|
||||||
{tcp, Socket, Response2} ->
|
{tcp, Socket, Response2} ->
|
||||||
log(info, "Response: ~tp", [Response2])
|
log(info, "Response: ~tp", [Response2]);
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 5000 ->
|
after 5000 ->
|
||||||
log(warning, "Server timed out!")
|
log(warning, "Server timed out!")
|
||||||
end,
|
end,
|
||||||
@ -941,6 +1056,13 @@ send(Socket, Message) ->
|
|||||||
gen_tcp:send(Socket, Bin).
|
gen_tcp:send(Socket, Bin).
|
||||||
|
|
||||||
|
|
||||||
|
-spec halt_on_unexpected_close() -> no_return().
|
||||||
|
|
||||||
|
halt_on_unexpected_close() ->
|
||||||
|
ok = log(warning, "Socket closed unexpectedly."),
|
||||||
|
halt(1).
|
||||||
|
|
||||||
|
|
||||||
-spec connect_user(realm()) -> gen_tcp:socket() | no_return().
|
-spec connect_user(realm()) -> gen_tcp:socket() | no_return().
|
||||||
%% @private
|
%% @private
|
||||||
%% Connect to a given realm, whatever method is required.
|
%% Connect to a given realm, whatever method is required.
|
||||||
@ -961,7 +1083,12 @@ connect_user(Realm) ->
|
|||||||
|
|
||||||
connect_user(Realm, []) ->
|
connect_user(Realm, []) ->
|
||||||
{Host, Port} = get_prime(Realm),
|
{Host, Port} = get_prime(Realm),
|
||||||
ok = log(info, "Trying prime at ~ts:~tp", [inet:ntoa(Host), Port]),
|
HostString =
|
||||||
|
case io_lib:printable_unicode_list(Host) of
|
||||||
|
true -> Host;
|
||||||
|
false -> inet:ntoa(Host)
|
||||||
|
end,
|
||||||
|
ok = log(info, "Trying prime at ~ts:~160tp", [HostString, Port]),
|
||||||
case gen_tcp:connect(Host, Port, connect_options(), 5000) of
|
case gen_tcp:connect(Host, Port, connect_options(), 5000) of
|
||||||
{ok, Socket} ->
|
{ok, Socket} ->
|
||||||
confirm_user(Realm, Socket, []);
|
confirm_user(Realm, Socket, []);
|
||||||
@ -1002,7 +1129,9 @@ confirm_user(Realm, Socket, Hosts) ->
|
|||||||
ok = log(info, "Redirected..."),
|
ok = log(info, "Redirected..."),
|
||||||
ok = disconnect(Socket),
|
ok = disconnect(Socket),
|
||||||
connect_user(Realm, Next ++ Hosts)
|
connect_user(Realm, Next ++ Hosts)
|
||||||
end
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 5000 ->
|
after 5000 ->
|
||||||
ok = log(warning, "Host ~ts:~p timed out.", [Host, Port]),
|
ok = log(warning, "Host ~ts:~p timed out.", [Host, Port]),
|
||||||
ok = disconnect(Socket),
|
ok = disconnect(Socket),
|
||||||
@ -1053,7 +1182,9 @@ confirm_serial(Realm, Socket, Hosts) ->
|
|||||||
ok = log(info, "Node is no longer serving realm. Trying another."),
|
ok = log(info, "Node is no longer serving realm. Trying another."),
|
||||||
ok = disconnect(Socket),
|
ok = disconnect(Socket),
|
||||||
connect_user(Realm, Hosts)
|
connect_user(Realm, Hosts)
|
||||||
end
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 5000 ->
|
after 5000 ->
|
||||||
ok = log(info, "Host timed out on confirm_serial. Trying another."),
|
ok = log(info, "Host timed out on confirm_serial. Trying another."),
|
||||||
ok = disconnect(Socket),
|
ok = disconnect(Socket),
|
||||||
@ -1061,30 +1192,32 @@ confirm_serial(Realm, Socket, Hosts) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec connect_auth(Realm, KeyName) -> Result
|
-spec connect_auth(Realm) -> Result
|
||||||
when Realm :: realm(),
|
when Realm :: realm(),
|
||||||
KeyName :: key_name(),
|
|
||||||
Result :: {ok, gen_tcp:socket()}
|
Result :: {ok, gen_tcp:socket()}
|
||||||
| {error, Reason :: term()}.
|
| {error, Reason :: term()}.
|
||||||
%% @private
|
%% @private
|
||||||
%% Connect to one of the servers in the realm constellation.
|
%% Connect to one of the servers in the realm constellation.
|
||||||
|
|
||||||
connect_auth(Realm, KeyName) ->
|
connect_auth(Realm) ->
|
||||||
{ok, Key} = loadkey(private, {Realm, KeyName}),
|
RealmConf = load_realm_conf(Realm),
|
||||||
{Host, Port} = get_prime(Realm),
|
{User, KeyID, Key} = prep_auth(Realm, RealmConf),
|
||||||
|
{prime, {Host, Port}} = lists:keyfind(prime, 1, RealmConf),
|
||||||
case gen_tcp:connect(Host, Port, connect_options(), 5000) of
|
case gen_tcp:connect(Host, Port, connect_options(), 5000) of
|
||||||
{ok, Socket} ->
|
{ok, Socket} ->
|
||||||
ok = log(info, "Connected to ~tp prime.", [Realm]),
|
ok = log(info, "Connected to ~tp prime.", [Realm]),
|
||||||
confirm_auth(Socket, Key);
|
connect_auth(Socket, Realm, User, KeyID, Key);
|
||||||
Error = {error, E} ->
|
Error = {error, E} ->
|
||||||
ok = log(warning, "Connection problem: ~tp", [E]),
|
ok = log(warning, "Connection problem: ~tp", [E]),
|
||||||
{error, Error}
|
{error, Error}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec connect_auth(Socket, Realm, User, KeyID, Key) -> Result
|
||||||
-spec confirm_auth(Socket, Key) -> Result
|
|
||||||
when Socket :: gen_tcp:socket(),
|
when Socket :: gen_tcp:socket(),
|
||||||
|
Realm :: realm(),
|
||||||
|
User :: user(),
|
||||||
|
KeyID :: key_id(),
|
||||||
Key :: term(),
|
Key :: term(),
|
||||||
Result :: {ok, gen_tcp:socket()}
|
Result :: {ok, gen_tcp:socket()}
|
||||||
| {error, timeout}.
|
| {error, timeout}.
|
||||||
@ -1092,19 +1225,118 @@ connect_auth(Realm, KeyName) ->
|
|||||||
%% Send a protocol ID string to notify the server what we're up to, disconnect
|
%% Send a protocol ID string to notify the server what we're up to, disconnect
|
||||||
%% if it does not return an "OK" response within 5 seconds.
|
%% if it does not return an "OK" response within 5 seconds.
|
||||||
|
|
||||||
confirm_auth(Socket, Key) ->
|
connect_auth(Socket, Realm, User, KeyID, Key) ->
|
||||||
ok = log(info, "Would be using key ~tp now", [Key]),
|
|
||||||
{ok, {Host, Port}} = inet:peername(Socket),
|
|
||||||
ok = gen_tcp:send(Socket, <<"OTPR AUTH 1">>),
|
ok = gen_tcp:send(Socket, <<"OTPR AUTH 1">>),
|
||||||
receive
|
receive
|
||||||
{tcp, Socket, <<"OK">>} ->
|
{tcp, Socket, Bin} ->
|
||||||
{ok, Socket}
|
ok = binary_to_term(Bin, [safe]),
|
||||||
|
confirm_auth(Socket, Realm, User, KeyID, Key);
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 5000 ->
|
after 5000 ->
|
||||||
ok = log(warning, "Host ~s:~p timed out.", [Host, Port]),
|
ok = log(warning, "Host realm ~160tp prime timed out.", [Realm]),
|
||||||
{error, auth_timeout}
|
{error, auth_timeout}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
confirm_auth(Socket, Realm, User, KeyID, Key) ->
|
||||||
|
ok = send(Socket, {User, KeyID}),
|
||||||
|
receive
|
||||||
|
{tcp, Socket, Bin} ->
|
||||||
|
case binary_to_term(Bin, [safe]) of
|
||||||
|
{sign, Blob} ->
|
||||||
|
Sig = public_key:sign(Blob, sha512, Key),
|
||||||
|
ok = send(Socket, {signed, Sig}),
|
||||||
|
confirm_auth(Socket);
|
||||||
|
{error, not_prime} ->
|
||||||
|
M1 = "Connected node is not prime for realm ~160tp",
|
||||||
|
ok = log(warning, M1, [Realm]),
|
||||||
|
ok = disconnect(Socket),
|
||||||
|
{error, not_prime};
|
||||||
|
{error, bad_user} ->
|
||||||
|
M2 = "Bad user record ~160tp",
|
||||||
|
ok = log(warning, M2, [User]),
|
||||||
|
ok = disconnect(Socket),
|
||||||
|
{error, bad_user};
|
||||||
|
{error, unauthorized_key} ->
|
||||||
|
M3 = "Unauthorized user key ~160tp",
|
||||||
|
ok = log(warning, M3, [KeyID]),
|
||||||
|
ok = disconnect(Socket),
|
||||||
|
{error, unauthorized_key};
|
||||||
|
{error, Reason} ->
|
||||||
|
Message = "Could not begin auth exchange. Failed with ~160tp",
|
||||||
|
ok = log(warning, Message, [Reason]),
|
||||||
|
ok = disconnect(Socket),
|
||||||
|
{error, Reason}
|
||||||
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
|
after 5000 ->
|
||||||
|
ok = log(warning, "Host realm ~tp prime timed out.", [Realm]),
|
||||||
|
{error, auth_timeout}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
confirm_auth(Socket) ->
|
||||||
|
receive
|
||||||
|
{tcp, Socket, Bin} ->
|
||||||
|
case binary_to_term(Bin, [safe]) of
|
||||||
|
ok -> {ok, Socket};
|
||||||
|
Other -> {error, Other}
|
||||||
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
|
after 5000 ->
|
||||||
|
{error, timeout}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec prep_auth(Realm, RealmConf) -> {User, KeyID, Key} | no_return()
|
||||||
|
when Realm :: realm(),
|
||||||
|
RealmConf :: [term()],
|
||||||
|
User :: user(),
|
||||||
|
KeyID :: key_id(),
|
||||||
|
Key :: term().
|
||||||
|
%% @private
|
||||||
|
%% Loads the appropriate User, KeyID and reads in a registered key for use in
|
||||||
|
%% connect_auth/4.
|
||||||
|
|
||||||
|
prep_auth(Realm, RealmConf) ->
|
||||||
|
Users =
|
||||||
|
case file:consult("zomp.users") of
|
||||||
|
{ok, U} ->
|
||||||
|
U;
|
||||||
|
{error, enoent} ->
|
||||||
|
ok = log(warning, "You do not have any users configured."),
|
||||||
|
halt(1)
|
||||||
|
end,
|
||||||
|
{User, KeyIDs} =
|
||||||
|
case lists:keyfind(Realm, 1, Users) of
|
||||||
|
{Realm, UserName, []} ->
|
||||||
|
W = "User ~tp does not have any keys registered for realm ~tp.",
|
||||||
|
ok = log(warning, W, [UserName, Realm]),
|
||||||
|
ok = log(info, "Contact the following sysop(s) to register a key:"),
|
||||||
|
{sysops, Sysops} = lists:keyfind(sysops, 1, RealmConf),
|
||||||
|
PrintContact =
|
||||||
|
fun({_, _, Email, Name, _, _}) ->
|
||||||
|
log(info, "Sysop: ~ts Email: ~ts", [Name, Email])
|
||||||
|
end,
|
||||||
|
ok = lists:foreach(PrintContact, Sysops),
|
||||||
|
halt(1);
|
||||||
|
{Realm, UserName, KeyNames} ->
|
||||||
|
KIDs = [{Realm, KeyName} || KeyName <- KeyNames],
|
||||||
|
{{Realm, UserName}, KIDs};
|
||||||
|
false ->
|
||||||
|
Message = "You are not a user of the given realm: ~160tp.",
|
||||||
|
ok = log(warning, Message, [Realm]),
|
||||||
|
halt(1)
|
||||||
|
end,
|
||||||
|
KeyID = hd(KeyIDs),
|
||||||
|
true = ensure_keypair(KeyID),
|
||||||
|
{ok, Key} = loadkey(private, KeyID),
|
||||||
|
{User, KeyID, Key}.
|
||||||
|
|
||||||
|
|
||||||
-spec connect_options() -> [gen_tcp:connect_option()].
|
-spec connect_options() -> [gen_tcp:connect_option()].
|
||||||
%% @private
|
%% @private
|
||||||
%% Hide away the default options used for TCP connections.
|
%% Hide away the default options used for TCP connections.
|
||||||
@ -1411,10 +1643,10 @@ loadkey(Type, {Realm, KeyName}) ->
|
|||||||
{DerType, Path} =
|
{DerType, Path} =
|
||||||
case Type of
|
case Type of
|
||||||
private ->
|
private ->
|
||||||
P = filename:join([zomp_dir(), "key", Realm, KeyName ++ "key.der"]),
|
P = filename:join([zomp_dir(), "key", Realm, KeyName ++ ".key.der"]),
|
||||||
{'RSAPrivateKey', P};
|
{'RSAPrivateKey', P};
|
||||||
public ->
|
public ->
|
||||||
P = filename:join([zomp_dir(), "key", Realm, KeyName ++ "pub.der"]),
|
P = filename:join([zomp_dir(), "key", Realm, KeyName ++ ".pub.der"]),
|
||||||
{'RSAPublicKey', P}
|
{'RSAPublicKey', P}
|
||||||
end,
|
end,
|
||||||
ok = log(info, "Loading key from file ~ts", [Path]),
|
ok = log(info, "Loading key from file ~ts", [Path]),
|
||||||
@ -1642,8 +1874,8 @@ create_realm(ZompConf, Realm, ExAddress, ExPort) ->
|
|||||||
Message =
|
Message =
|
||||||
"~n"
|
"~n"
|
||||||
" Enter the local (internal/LAN) port number at which this service should be "
|
" Enter the local (internal/LAN) port number at which this service should be "
|
||||||
"available. (This might be different from the public port visible from the internet"
|
"available. (This might be different from the public port visible from the "
|
||||||
"if you are port forwarding or have a complex network layout.)~n",
|
"internet if you are port forwarding or have a complex network layout.)~n",
|
||||||
ok = io:format(Message),
|
ok = io:format(Message),
|
||||||
InPort = prompt_port_number(Current),
|
InPort = prompt_port_number(Current),
|
||||||
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort).
|
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort).
|
||||||
@ -1667,7 +1899,7 @@ prompt_port_number(Current) ->
|
|||||||
Port when 16#ffff >= Port, Port > 0 ->
|
Port when 16#ffff >= Port, Port > 0 ->
|
||||||
Port;
|
Port;
|
||||||
Illegal ->
|
Illegal ->
|
||||||
Whoops = "Whoops! ~tw is out of bounds (1~65535). Try again...~n",
|
Whoops = "Whoops! ~tw is out of bounds (1~65535). Try again.~n",
|
||||||
ok = io:format(Whoops, [Illegal]),
|
ok = io:format(Whoops, [Illegal]),
|
||||||
prompt_port_number(Current)
|
prompt_port_number(Current)
|
||||||
end
|
end
|
||||||
@ -1811,12 +2043,10 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
|
|||||||
" directory on your personal or dev machine.~n",
|
" directory on your personal or dev machine.~n",
|
||||||
ok = io:format(Message, [Realm]),
|
ok = io:format(Message, [Realm]),
|
||||||
UserRecord =
|
UserRecord =
|
||||||
{{UserName, Realm},
|
{{Realm, UserName},
|
||||||
[filename:basename(SysopPub, ".pub.der")],
|
[filename:basename(SysopPub, ".pub.der")],
|
||||||
Email,
|
Email,
|
||||||
RealName,
|
RealName},
|
||||||
1,
|
|
||||||
Timestamp},
|
|
||||||
RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"),
|
RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"),
|
||||||
RealmMeta =
|
RealmMeta =
|
||||||
[{realm, Realm},
|
[{realm, Realm},
|
||||||
@ -1849,35 +2079,29 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
|
|||||||
-spec create_realmfile(realm()) -> no_return().
|
-spec create_realmfile(realm()) -> no_return().
|
||||||
|
|
||||||
create_realmfile(Realm) ->
|
create_realmfile(Realm) ->
|
||||||
ConfPath = filename:join(zomp_dir(), realm_conf(Realm)),
|
RealmConf = load_realm_conf(Realm),
|
||||||
case file:consult(ConfPath) of
|
|
||||||
{ok, RealmConf} ->
|
|
||||||
ok = log(info, "Realm found, creating realm file..."),
|
ok = log(info, "Realm found, creating realm file..."),
|
||||||
{revision, Revision} = lists:keyfind(revision, 1, RealmConf),
|
{revision, Revision} = lists:keyfind(revision, 1, RealmConf),
|
||||||
{realm_keys, RealmKeys} = lists:keyfind(realm_keys, 1, RealmConf),
|
{realm_keys, RealmKeys} = lists:keyfind(realm_keys, 1, RealmConf),
|
||||||
{package_keys, PackageKeys} = lists:keyfind(package_keys, 1, RealmConf),
|
{package_keys, PackageKeys} = lists:keyfind(package_keys, 1, RealmConf),
|
||||||
RealmKeyIDs = [element(1, K) || K <- RealmKeys],
|
RealmKeyIDs = [element(1, K) || K <- RealmKeys],
|
||||||
PackageKeyIDs = [element(1, K) || K <- PackageKeys],
|
PackageKeyIDs = [element(1, K) || K <- PackageKeys],
|
||||||
create_realmfile(Realm, ConfPath, Revision, RealmKeyIDs, PackageKeyIDs);
|
create_realmfile(Realm, Revision, RealmKeyIDs, PackageKeyIDs).
|
||||||
{error, enoent} ->
|
|
||||||
ok = log(warning, "There is no configured realm called ~ts.", [Realm]),
|
|
||||||
halt(1)
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec create_realmfile(Realm, ConfPath, Revision, RealmKeyIDs, PackageKeyIDs) -> ok
|
|
||||||
|
-spec create_realmfile(Realm, Revision, RealmKeyIDs, PackageKeyIDs) -> ok
|
||||||
when Realm :: realm(),
|
when Realm :: realm(),
|
||||||
ConfPath :: file:filename(),
|
|
||||||
Revision :: non_neg_integer(),
|
Revision :: non_neg_integer(),
|
||||||
RealmKeyIDs :: [key_id()],
|
RealmKeyIDs :: [key_id()],
|
||||||
PackageKeyIDs :: [key_id()].
|
PackageKeyIDs :: [key_id()].
|
||||||
|
|
||||||
create_realmfile(Realm, ConfPath, Revision, RealmKeyIDs, PackageKeyIDs) ->
|
create_realmfile(Realm, Revision, RealmKeyIDs, PackageKeyIDs) ->
|
||||||
{ok, CWD} = file:get_cwd(),
|
{ok, CWD} = file:get_cwd(),
|
||||||
ok = file:set_cwd(zomp_dir()),
|
ok = file:set_cwd(zomp_dir()),
|
||||||
KeyPath = fun({R, K}) -> filename:join(["key", R, K ++ ".pub.der"]) end,
|
KeyPath = fun({R, K}) -> filename:join(["key", R, K ++ ".pub.der"]) end,
|
||||||
RealmKeyPaths = lists:map(KeyPath, RealmKeyIDs),
|
RealmKeyPaths = lists:map(KeyPath, RealmKeyIDs),
|
||||||
PackageKeyPaths = lists:map(KeyPath, PackageKeyIDs),
|
PackageKeyPaths = lists:map(KeyPath, PackageKeyIDs),
|
||||||
Targets = [filename:basename(ConfPath) | RealmKeyPaths ++ PackageKeyPaths],
|
Targets = [realm_conf(Realm) | RealmKeyPaths ++ PackageKeyPaths],
|
||||||
OutFile = filename:join(CWD, Realm ++ "." ++ integer_to_list(Revision) ++ ".zrf"),
|
OutFile = filename:join(CWD, Realm ++ "." ++ integer_to_list(Revision) ++ ".zrf"),
|
||||||
ok = erl_tar:create(OutFile, Targets, [compressed]),
|
ok = erl_tar:create(OutFile, Targets, [compressed]),
|
||||||
ok = log(info, "Realm conf file written to ~ts", [OutFile]),
|
ok = log(info, "Realm conf file written to ~ts", [OutFile]),
|
||||||
@ -1987,7 +2211,9 @@ request_zrp(Socket, PackageID) ->
|
|||||||
Message = "Error receiving package ~ts: ~tp",
|
Message = "Error receiving package ~ts: ~tp",
|
||||||
ok = log(info, Message, [PackageString, Reason]),
|
ok = log(info, Message, [PackageString, Reason]),
|
||||||
Error
|
Error
|
||||||
end
|
end;
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 60000 ->
|
after 60000 ->
|
||||||
{error, timeout}
|
{error, timeout}
|
||||||
end.
|
end.
|
||||||
@ -2004,7 +2230,9 @@ receive_zrp(Socket, PackageID) ->
|
|||||||
ZrpPath = filename:join("zrp", namify_zrp(PackageID)),
|
ZrpPath = filename:join("zrp", namify_zrp(PackageID)),
|
||||||
ok = file:write_file(ZrpPath, Bin),
|
ok = file:write_file(ZrpPath, Bin),
|
||||||
ok = send(Socket, ok),
|
ok = send(Socket, ok),
|
||||||
log(info, "Wrote ~ts", [ZrpPath])
|
log(info, "Wrote ~ts", [ZrpPath]);
|
||||||
|
{tcp_closed, Socket} ->
|
||||||
|
halt_on_unexpected_close()
|
||||||
after 60000 ->
|
after 60000 ->
|
||||||
ok = log(error, "Timeout in socket receive for ~tp", [PackageID]),
|
ok = log(error, "Timeout in socket receive for ~tp", [PackageID]),
|
||||||
{error, timeout}
|
{error, timeout}
|
||||||
@ -2525,6 +2753,22 @@ realm_conf(Realm) ->
|
|||||||
Realm ++ ".realm".
|
Realm ++ ".realm".
|
||||||
|
|
||||||
|
|
||||||
|
-spec load_realm_conf(Realm) -> RealmConf | no_return()
|
||||||
|
when Realm :: realm(),
|
||||||
|
RealmConf :: list().
|
||||||
|
%% @private
|
||||||
|
%% Load the config for the given realm or halt with an error.
|
||||||
|
|
||||||
|
load_realm_conf(Realm) ->
|
||||||
|
Path = filename:join(zomp_dir(), realm_conf(Realm)),
|
||||||
|
case file:consult(Path) of
|
||||||
|
{ok, C} ->
|
||||||
|
C;
|
||||||
|
{error, enoent} ->
|
||||||
|
ok = log(warning, "Realm ~tp is not configured.", [Realm]),
|
||||||
|
halt(1)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
%%% Usage
|
%%% Usage
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user