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() ->
ConfFile = filename:join(zomp_dir(), "zomp.conf"),
{ok, ZompConf} = file:consult(ConfFile),
create_realm(ZompConf).
case file:consult(ConfFile) of
{ok, ZompConf} -> create_realm(ZompConf);
{error, enoent} -> create_realm([])
end.
create_realm(ZompConf) ->
Instructions =
@ -1426,107 +1428,115 @@ create_realm(ZompConf) ->
create_realm(ZompConf)
end;
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)
end.
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 =
case inet:ntoa(XA) of
{error, einval} -> XA;
String -> String
case inet:ntoa(Current) of
{error, einval} -> Current;
XAS -> XAS
end,
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"
" 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 "
"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",
ok = io:format(Message, [XAString]),
Input = get_input(),
ExAddress =
case
" DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n".
{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
parse_address(String) ->
case inet:parse_address(String) of
{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,
create_prime(Realm, Host) ->
ok = io:format("~n Enter the local port number on which the host listen.~n"),
case prompt_port_number() of
{ok, ExPort} -> create_prime(Realm, Host, ExPort);
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",
Message =
"~n"
" Enter the public (external) port number at which this service should be "
"available. (This might be different from the local port number if you are "
"forwarding ports or have a complex network layout.)~n",
ok = io:format(Message),
case prompt_port_number() of
{ok, ExPort} -> create_prime(Realm, Host, ExPort, InPort);
error -> create_prime(Realm, Host, ExPort)
end.
ExPort = prompt_port_number(Current),
create_realm(ZompConf, Realm, ExAddress, ExPort).
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 =
" A port can be any number from 1 to 65535.~n"
" [Press enter to accept the default port: 11311]~n",
ok = io:format(Instructions),
" A valid port is any number from 1 to 65535."
" [Press enter to accept the current setting: ~tw]~n",
ok = io:format(Instructions, [Current]),
case get_input() of
"" ->
{ok, 11311};
Current;
S ->
try
case list_to_integer(S) of
Port when 16#ffff >= Port, Port > 0 ->
{ok, Port};
Port;
Illegal ->
Whoops = "~p is out of bounds (1~65535). Try again...",
error
Whoops = "Whoops! ~tw is out of bounds (1~65535). Try again...~n",
ok = io:format(Whoops, [Illegal]),
prompt_port_number(Current)
end
catch error:badarg ->
ok = io:format("~tp is not a port number. Try again...", [S]),
error
prompt_port_number(Current)
end
end.
create_realm(Realm, Prime) ->
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) ->
Instructions =
"~n"
" Enter a username for the realm sysop.~n"
@ -1536,13 +1546,13 @@ create_realm(Realm, Prime) ->
UserName = get_input(),
case valid_lower0_9(UserName) of
true ->
create_realm(Realm, Prime, UserName);
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName);
false ->
ok = io:format("Bad username ~tp. Try again.~n", [UserName]),
create_realm(Realm, Prime)
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort)
end.
create_realm(Realm, Prime, UserName) ->
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) ->
Instructions =
"~n"
" Enter an email address for the realm sysop.~n"
@ -1554,22 +1564,22 @@ create_realm(Realm, Prime, UserName) ->
[User, Host] = string:lexemes(Email, "@"),
case {valid_lower0_9(User), valid_label(Host)} of
{true, true} ->
create_realm(Realm, Prime, UserName, Email);
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, 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);
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, 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);
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName);
{false, false} ->
Message = "This email address seems like its totally bonkers. Try again.~n",
ok = io:format(Message),
create_realm(Realm, Prime, UserName)
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName)
end.
create_realm(Realm, Prime, UserName, Email) ->
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
Instructions =
"~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,
ok = lists:foreach(Copy, AllKeys),
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 =
"~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"
" directory on your personal or dev machine.~n",
ok = io:format(Message, [Realm]),
Timestamp = calendar:now_to_universal_time(erlang:timestamp()),
UserRecord = {{UserName, Realm}, [SysopPub], Email, RealName, 1, Timestamp},
RealmFile = filename:join(zomp_dir(), Realm ++ ".realm"),
RealmMeta =
[{realm, Realm},
{revision, 0},
{prime, Prime},
{prime, {ExAddress, ExPort}},
{private, []},
{mirrors, []},
{sysops, [UserRecord]},
{realm_keys, [RealmPub]},
{package_keys, [PackagePub]}],
{realm_keys, [RealmPubRecord]},
{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"),
ZompConf =
[{prime, Prime},
Update = fun({K, V}, ZC) -> lists:keystore(K, 1, ZC, {K, V}) end,
NewConf =
[{managed, Realms},
{external_address, ExAddress},
{external_port, ExPort},
{internal_port, InPort}],
NewZompConf = lists:foldl(Update, ZompConf, NewConf),
ok = write_terms(RealmFile, RealmMeta),
ok = write_terms(ZompFile, ZompConf),
ok = log(info, "Wrote to ~ts:~n ~tp", [RealmFile, RealmMeta]),
ok = log(info, "Wrote to ~ts:~n ~tp", [ZompFile, ZompConf]),
ok = write_terms(ZompFile, NewZompConf),
ok = log(info, "Realm ~ts created.", [Realm]),
halt(0).