This commit is contained in:
Craig Everett 2017-11-24 19:58:06 +09:00
parent 5d7d6599a9
commit cc4d55288d

188
zx
View File

@ -668,7 +668,7 @@ package(TargetDir) ->
[] ->
ok = log(info, "Need to generate key"),
KeyID = prompt_keygen(),
ok = generate_rsa(KeyID),
{ok, _, _} = generate_rsa(KeyID),
package(KeyID, TargetDir);
[KeyName] ->
KeyID = {Realm, KeyName},
@ -1144,11 +1144,14 @@ prompt_keygen() ->
case {valid_lower0_9(Realm), valid_label(KeyName)} of
{true, true} ->
{Realm, KeyName};
{false, _} ->
{false, true} ->
ok = io:format("Bad realm name ~tp. Try again.~n", [Realm]),
prompt_keygen();
{true, false} ->
ok = io:format("Bad key name ~tp. Try again.~n", [KeyName]),
prompt_keygen();
{false, false} ->
ok = io:format("NUTS! Both the key and realm names are illegal. Try again.~n"),
prompt_keygen()
end.
@ -1161,8 +1164,8 @@ create_keypair() ->
ok = file:set_cwd(zomp_dir()),
KeyID = prompt_keygen(),
case generate_rsa(KeyID) of
ok -> halt(0);
Error -> error_exit("create_keypair/0 failed with ~tp", [Error], ?FILE, ?LINE)
{ok, _, _} -> halt(0);
Error -> error_exit("create_keypair/0 failed with ~tp", [Error], ?FILE, ?LINE)
end.
@ -1198,7 +1201,7 @@ generate_rsa({Realm, KeyName}) ->
ok = log(info, "~ts and ~ts agree", [KeyFile, PubFile]),
ok = log(info, "Wrote private key to: ~ts.", [KeyFile]),
ok = log(info, "Wrote public key to: ~ts.", [PubFile]),
ok;
{ok, KeyFile, PubFile};
false ->
ok = lists:foreach(fun file:delete/1, [PemFile, KeyFile, PubFile]),
ok = log(error, "Something has gone wrong."),
@ -1400,11 +1403,12 @@ dialyze() ->
%%% Create Realm & Sysop
create_realm() ->
RealmMessage =
"~n Enter a name for your new realm.~n"
Instructions =
"~n"
" Enter a name for your new realm.~n"
" Valid names can contain only lower-case letters, numbers and the underscore.~n"
" Valid names must begin with a lower-case letter.~n",
ok = io:format(RealmMessage),
ok = io:format(Instructions),
Realm = get_input(),
case valid_lower0_9(Realm) of
true ->
@ -1422,32 +1426,144 @@ create_realm() ->
end.
create_realm(Realm) ->
UserNameMessage =
"~n Enter a username for the realm sysop.~n"
HostInstructions =
"~n"
" Enter the prime (permanent) server's publicly accessible hostname or~n"
" address. If prime is identified by an address and not a name, enter it~n"
" as either a normal IPv4 dot-notation or IPv6 colon-notation.~n"
" Note that address ranges will not be checked for validity.~n",
ok = io:format(HostInstructions),
HostString = get_input(),
Host =
case inet:parse_address(HostString) of
{ok, Address} -> Address;
{error, einval} -> HostString
end,
PortInstructions =
" Enter the port number the host will be listening on.~n"
" The port can be any number from 1 to 65535.~n"
" [Press enter for the default: 11311]~n",
ok = io:format(PortInstructions),
case get_input() of
"" ->
create_realm(Realm, {HostString, 11311});
S ->
try
case list_to_integer(S) of
Port when 16#ffff >= Port, Port > 0 ->
create_realm(Realm, {Host, Port});
Illegal ->
Whoops = "~p is out of bounds (1~65535). Try again...",
ok = io:format(Whoops, [Illegal]),
create_realm(Realm)
end
catch error:badarg ->
Error = "~tp is not a port number. Try again...",
ok = io:format(Error, [S]),
create_realm(Realm)
end
end.
create_realm(Realm, Prime) ->
Instructions =
"~n"
" Enter a username for the realm sysop.~n"
" Valid names can contain only lower-case letters, numbers and the underscore.~n"
" Valid names must begin with a lower-case letter.~n",
ok = io:format(UserNameMessage),
ok = io:format(Instructions),
UserName = get_input(),
case valid_lower0_9(UserName) of
true ->
create_realm(Realm, UserName);
create_realm(Realm, Prime, UserName);
false ->
ok = io:format("Bad username ~tp. Try again.~n", [UserName]),
create_realm(Realm)
create_realm(Realm, Prime)
end.
create_realm(Realm, Prime, UserName) ->
Instructions =
"~n"
" Enter an email address for the realm sysop.~n"
" Valid email address rules apply though the checking done here is quite~n"
" minimal. Check the address you enter carefully. The only people who will~n"
" suffer from an invalid address are your users.~n",
ok = io:format(Instructions),
Email = get_input(),
[User, Host] = string:lexemes(Email, "@"),
case {valid_lower0_9(User), valid_label(Host)} of
{true, true} ->
create_realm(Realm, Prime, UserName, Email);
{false, true} ->
Message = "The user part of the email address seems invalid. Try again.~n",
ok = io:format(Message),
create_realm(Realm, Prime, UserName);
{true, false} ->
Message = "The host part of the email address seems invalid. Try again.~n",
ok = io:format(Message),
create_realm(Realm, Prime, UserName);
{false, false} ->
Message = "This email address seems like its totally bonkers. Try again.~n",
ok = io:format(Message),
create_realm(Realm, Prime, UserName)
end.
create_realm(Realm, UserName, UserRecord, Prime, RealmKey, PackageKey) ->
create_realm(Realm, Prime, UserName, Email) ->
Instructions =
"~n"
" Enter the real name (or whatever name people will recognize) for the sysop.~n"
" There are no rules for this one. Any valid UTF-8 printables are legal.~n",
ok = io:format(Instructions),
RealName = get_input(),
ok = io:format("~nGenerating keys. This might take a while, so settle in...~n"),
{ok, RealmKey, RealmPub} = generate_rsa({Realm, Realm ++ ".1.realm"}),
{ok, PackageKey, PackagePub} = generate_rsa({Realm, Realm ++ ".1.package"}),
{ok, SysopKey, SysopPub} = generate_rsa({Realm, UserName ++ ".1"}),
AllKeys = [RealmKey, RealmPub, PackageKey, PackagePub, SysopKey, SysopPub],
Copy =
fun(From) ->
To = filename:basename(From),
case filelib:is_file(To) of
true ->
M = "Whoops! Keyfile local destination ~tp exists! Aborting",
ok = log(error, M, [To]),
ok = log(info, "Undoing all changes..."),
ok = lists:foreach(fun file:delete/1, AllKeys),
halt(0);
false ->
{ok, _} = file:copy(From, To),
log(info, "Copying ~tp to the local directory", [From])
end
end,
Drop =
fun(File) ->
ok = file:delete(File),
log(info, "Deleting ~tp", [File])
end,
DangerousKeys = [PackageKey, SysopKey],
ok = lists:foreach(Copy, AllKeys),
ok = lists:foreach(Drop, DangerousKeys),
Message =
"~n"
" All of the keys generated have been moved to the current directory.~n"
" MAKE AND SECURELY STORE COPIES OF THESE KEYS.~n"
" The private package key and private sysop login key have been deleted from the~n"
" zomp key directory. These should only exist on your local system, not a prime.~n"
" realm server (particularly if other services are run on that machine).~n"
" Your package and sysop keys will need to be copied to the ~~/.zomp/keys/~p/~n"
" directory on your personal or dev machine.",
ok = io:format(Message, [Realm]),
Timestamp = calendar:now_to_universal_time(erlang:timestamp()),
UserRecord = {{UserName, Realm}, [SysopPub], Email, RealName, 1, Timestamp},
RealmMeta =
[{realm, Realm},
{prime, Prime},
{realm_keys, [RealmKey],
{package_keys, [PacakageKey]
{revision, 0},
{serial, 0},
{mirrors, []}],
ok = log(info, "Seriously, we would be creating a realm now."),
{prime, Prime},
{private, []},
{mirrors, []},
{sysops, [UserRecord]},
{realm_keys, [RealmPub]},
{package_keys, [PackagePub]}],
ok = log(info, "Would be writing ~tp", [RealmMeta]),
halt(0).
@ -1961,7 +2077,33 @@ hurr() -> io:format("That isn't an option.~n").
%%% Directory Management
%%% Directory & File Management
%-spec move_file(From, To) -> Result
% when From :: file:filename(),
% To :: file:filename(),
% Result :: ok
% | {error, Reason},
% Reason :: bad_source
% | destination_exists
% | file:posix().
%%% @private
%%% Utility function to safely copy a file From one path To another without clobbering the
%%% destination in the event it already exists. Both the source and the destination must be
%%% complete filenames, not directories.
%
%move_file(From, To) ->
% case {filelib:is_regular(From), filelib:is_file(To)} of
% {false, false} ->
% case file:copy(From, To) of
% {ok, _} -> file:delete(From);
% Error -> Error
% end;
% {true, _} ->
% {error, bad_source};
% {false, true} ->
% {error, destination_exists}
% end.
-spec ensure_zomp_home() -> ok.
@ -2248,4 +2390,4 @@ log(Level, Format, Args) ->
warning -> "[WARNING]";
error -> "[ERROR]"
end,
io:format("~s ~p: " ++ Format ++ "~n", [Tag, self() | Args]).
io:format("~p ~s: " ++ Format ++ "~n", [self(), Tag | Args]).