Realm creation

This commit is contained in:
Craig Everett 2017-11-29 14:04:44 +09:00
parent 82ea25f1ec
commit 20f27cbcab

211
zx
View File

@ -1404,8 +1404,10 @@ dialyze() ->
create_realm() -> create_realm() ->
ConfFile = filename:join(zomp_dir(), "zomp.conf"), ConfFile = filename:join(zomp_dir(), "zomp.conf"),
{ok, ZompConf} = file:consult(ConfFile), case file:consult(ConfFile) of
create_realm(ZompConf). {ok, ZompConf} -> create_realm(ZompConf);
{error, enoent} -> create_realm([])
end.
create_realm(ZompConf) -> create_realm(ZompConf) ->
Instructions = Instructions =
@ -1426,107 +1428,115 @@ create_realm(ZompConf) ->
create_realm(ZompConf) create_realm(ZompConf)
end; end;
false -> false ->
ok = io:format("Bad realm name ~tp. Try again.~n", [Realm]), ok = io:format("Bad realm name \"~ts\". Try again.~n", [Realm]),
create_realm(ZompConf) create_realm(ZompConf)
end. end.
create_realm(ZompConf, Realm) -> create_realm(ZompConf, Realm) ->
{external_address, XA} = lists:keyfind(external_address, 1, ZompConf), ExAddress =
case lists:keyfind(external_address, 1, ZompConf) of
false -> prompt_external_address();
{external_address, none} -> prompt_external_address();
{external_address, Current} -> prompt_external_address(Current)
end,
create_realm(ZompConf, Realm, ExAddress).
prompt_external_address() ->
Message = external_address_prompt(),
ok = io:format(Message),
case get_input() of
"" ->
ok = io:format("You need to enter an address.~n"),
prompt_external_address();
String ->
parse_address(String)
end.
prompt_external_address(Current) ->
XAString = XAString =
case inet:ntoa(XA) of case inet:ntoa(Current) of
{error, einval} -> XA; {error, einval} -> Current;
String -> String XAS -> XAS
end, end,
Message = Message =
external_address_prompt() ++
" [The current public address is: ~ts. Press <ENTER> to keep this address.]~n",
ok = io:format(Message, [XAString]),
case get_input() of
"" -> Current;
String -> parse_address(String)
end.
external_address_prompt() ->
"~n" "~n"
" Enter a static, valid hostname or IPv4 or IPv6 address at which this host " " Enter a static, valid hostname or IPv4 or IPv6 address at which this host "
"can be reached from the public internet (or internal network if it will never " "can be reached from the public internet (or internal network if it will never "
"need to be reached from the internet).~n" "need to be reached from the internet).~n"
" The current public address is: ~ts. Press <ENTER> to keep this address.~n" " DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n".
" DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n",
ok = io:format(Message, [XAString]),
Input = get_input(),
ExAddress =
case
parse_address(String) ->
case inet:parse_address(String) of
{Host, Port} =
case proplists:get(external_address, 1, ZompConf) of
{external_address, ExAddress} ->
{external_port, ExPort} = lists:keyfind(external_port, 1, ZompConf),
{ExAddress, ExPort};
false ->
prompt_prime(Realm)
end,
HostString =
Instructions =
"~n"
" Accept current config?~n"
" Host: ~ts Port: ~w~n",
ok = io:format(Instructions, [Host, Port]),
case string:trim(io:get_line("(^C to quit) [Y]/n: ")) of
"" -> create_realm(Realm, Prime);
"Y" -> create_realm(Realm, Prime);
"y" -> create_realm(Realm, Prime);
_ -> prompt_prime(Realm)
end.
prompt_prime(Realm) ->
HostInstructions =
"~n"
" Enter the prime (permanent) server's publicly accessible hostname or "
"address. If prime is identified by an address and not a name, enter it "
"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; {ok, Address} -> Address;
{error, einval} -> HostString {error, einval} -> String
end.
create_realm(ZompConf, Realm, ExAddress) ->
Current =
case lists:keyfind(external_port, 1, ZompConf) of
false -> 11311;
{external_port, none} -> 11311;
{external_port, P} -> P
end, end,
Message =
create_prime(Realm, Host) -> "~n"
ok = io:format("~n Enter the local port number on which the host listen.~n"), " Enter the public (external) port number at which this service should be "
case prompt_port_number() of "available. (This might be different from the local port number if you are "
{ok, ExPort} -> create_prime(Realm, Host, ExPort); "forwarding ports or have a complex network layout.)~n",
error -> create_prime(Realm, Host)
end.
create_realm(Realm, Host, InPort) ->
Message = "~n Enter the global port number on which the host will be available.~n",
ok = io:format(Message), ok = io:format(Message),
case prompt_port_number() of ExPort = prompt_port_number(Current),
{ok, ExPort} -> create_prime(Realm, Host, ExPort, InPort); create_realm(ZompConf, Realm, ExAddress, ExPort).
error -> create_prime(Realm, Host, ExPort)
end.
prompt_port_number() -> create_realm(ZompConf, Realm, ExAddress, ExPort) ->
Current =
case lists:keyfind(internal_port, 1, ZompConf) of
false -> 11311;
{internal_port, none} -> 11311;
{internal_port, P} -> P
end,
Message =
"~n"
" 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"
"if you are port forwarding or have a complex network layout.)~n",
ok = io:format(Message),
InPort = prompt_port_number(Current),
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort).
prompt_port_number(Current) ->
Instructions = Instructions =
" A port can be any number from 1 to 65535.~n" " A valid port is any number from 1 to 65535."
" [Press enter to accept the default port: 11311]~n", " [Press enter to accept the current setting: ~tw]~n",
ok = io:format(Instructions), ok = io:format(Instructions, [Current]),
case get_input() of case get_input() of
"" -> "" ->
{ok, 11311}; Current;
S -> S ->
try try
case list_to_integer(S) of case list_to_integer(S) of
Port when 16#ffff >= Port, Port > 0 -> Port when 16#ffff >= Port, Port > 0 ->
{ok, Port}; Port;
Illegal -> Illegal ->
Whoops = "~p is out of bounds (1~65535). Try again...", Whoops = "Whoops! ~tw is out of bounds (1~65535). Try again...~n",
error ok = io:format(Whoops, [Illegal]),
prompt_port_number(Current)
end end
catch error:badarg -> catch error:badarg ->
ok = io:format("~tp is not a port number. Try again...", [S]), ok = io:format("~tp is not a port number. Try again...", [S]),
error prompt_port_number(Current)
end end
end. end.
create_realm(Realm, Prime) -> create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) ->
Instructions = Instructions =
"~n" "~n"
" Enter a username for the realm sysop.~n" " Enter a username for the realm sysop.~n"
@ -1536,13 +1546,13 @@ create_realm(Realm, Prime) ->
UserName = get_input(), UserName = get_input(),
case valid_lower0_9(UserName) of case valid_lower0_9(UserName) of
true -> true ->
create_realm(Realm, Prime, UserName); create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName);
false -> false ->
ok = io:format("Bad username ~tp. Try again.~n", [UserName]), ok = io:format("Bad username ~tp. Try again.~n", [UserName]),
create_realm(Realm, Prime) create_realm(ZompConf, Realm, ExAddress, ExPort, InPort)
end. end.
create_realm(Realm, Prime, UserName) -> create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) ->
Instructions = Instructions =
"~n" "~n"
" Enter an email address for the realm sysop.~n" " Enter an email address for the realm sysop.~n"
@ -1554,22 +1564,22 @@ create_realm(Realm, Prime, UserName) ->
[User, Host] = string:lexemes(Email, "@"), [User, Host] = string:lexemes(Email, "@"),
case {valid_lower0_9(User), valid_label(Host)} of case {valid_lower0_9(User), valid_label(Host)} of
{true, true} -> {true, true} ->
create_realm(Realm, Prime, UserName, Email); create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email);
{false, true} -> {false, true} ->
Message = "The user part of the email address seems invalid. Try again.~n", Message = "The user part of the email address seems invalid. Try again.~n",
ok = io:format(Message), ok = io:format(Message),
create_realm(Realm, Prime, UserName); create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName);
{true, false} -> {true, false} ->
Message = "The host part of the email address seems invalid. Try again.~n", Message = "The host part of the email address seems invalid. Try again.~n",
ok = io:format(Message), ok = io:format(Message),
create_realm(Realm, Prime, UserName); create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName);
{false, false} -> {false, false} ->
Message = "This email address seems like its totally bonkers. Try again.~n", Message = "This email address seems like its totally bonkers. Try again.~n",
ok = io:format(Message), ok = io:format(Message),
create_realm(Realm, Prime, UserName) create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName)
end. end.
create_realm(Realm, Prime, UserName, Email) -> create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
Instructions = Instructions =
"~n" "~n"
" Enter the real name (or whatever name people recognize) for the sysop.~n" " Enter the real name (or whatever name people recognize) for the sysop.~n"
@ -1604,6 +1614,21 @@ create_realm(Realm, Prime, UserName, Email) ->
end, end,
ok = lists:foreach(Copy, AllKeys), ok = lists:foreach(Copy, AllKeys),
ok = lists:foreach(Drop, DangerousKeys), ok = lists:foreach(Drop, DangerousKeys),
Timestamp = calendar:now_to_universal_time(erlang:timestamp()),
{ok, RealmPubData} = file:read_file(RealmPub),
RealmPubRecord =
{{Realm, filename:basename(RealmPub)},
realm,
{realm, Realm},
crypto:hash(sha512, RealmPubData),
Timestamp},
{ok, PackagePubData} = file:read_file(PackagePub),
PackagePubRecord =
{{Realm, filename:basename(PackagePub)},
package,
{realm, Realm},
crypto:hash(sha512, PackagePubData),
Timestamp},
Message = Message =
"~n" "~n"
" All of the keys generated have been moved to the current directory.~n" " All of the keys generated have been moved to the current directory.~n"
@ -1616,27 +1641,33 @@ create_realm(Realm, Prime, UserName, Email) ->
" The package and sysop keys will need to be copied to the ~~/.zomp/keys/~s/~n" " The package and sysop keys will need to be copied to the ~~/.zomp/keys/~s/~n"
" 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]),
Timestamp = calendar:now_to_universal_time(erlang:timestamp()),
UserRecord = {{UserName, Realm}, [SysopPub], Email, RealName, 1, Timestamp}, UserRecord = {{UserName, Realm}, [SysopPub], Email, RealName, 1, Timestamp},
RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"), RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"),
RealmMeta = RealmMeta =
[{realm, Realm}, [{realm, Realm},
{revision, 0}, {revision, 0},
{prime, Prime}, {prime, {ExAddress, ExPort}},
{private, []}, {private, []},
{mirrors, []}, {mirrors, []},
{sysops, [UserRecord]}, {sysops, [UserRecord]},
{realm_keys, [RealmPub]}, {realm_keys, [RealmPubRecord]},
{package_keys, [PackagePub]}], {package_keys, [PackagePubRecord]}],
Realms =
case lists:keyfind(managed, 1, ZompConf) of
{managed, M} -> [Realm | M];
false -> [Realm]
end,
ZompFile = filename:join(zomp_dir(), "zomp.conf"), ZompFile = filename:join(zomp_dir(), "zomp.conf"),
ZompConf = Update = fun({K, V}, ZC) -> lists:keystore(K, 1, ZC, {K, V}) end,
[{prime, Prime}, NewConf =
[{managed, Realms},
{external_address, ExAddress},
{external_port, ExPort}, {external_port, ExPort},
{internal_port, InPort}], {internal_port, InPort}],
NewZompConf = lists:foldl(Update, ZompConf, NewConf),
ok = write_terms(RealmFile, RealmMeta), ok = write_terms(RealmFile, RealmMeta),
ok = write_terms(ZompFile, ZompConf), ok = write_terms(ZompFile, NewZompConf),
ok = log(info, "Wrote to ~ts:~n ~tp", [RealmFile, RealmMeta]), ok = log(info, "Realm ~ts created.", [Realm]),
ok = log(info, "Wrote to ~ts:~n ~tp", [ZompFile, ZompConf]),
halt(0). halt(0).