<% val you = Init.getUserId ();
val yourname = Init.getUserName ();
+val nodeNum = case $"node" of
+ "" => 2
+ | node => Web.stoi node;
+val nodeName = Init.nodeName nodeNum;
+
val uname = case $"uname" of
"" => yourname
| uname => uname;
-val socks = Sec.socketPerms uname;
-val tpe = Sec.isTpe uname;
-val cron = Sec.cronAllowed uname;
-val ftp = Sec.ftpAllowed uname;
+val socks = Sec.socketPerms {node = nodeNum, uname = uname};
+val tpe = Sec.isTpe {node = nodeNum, uname = uname};
+val cron = Sec.cronAllowed {node = nodeNum, uname = uname};
+val ftp = Sec.ftpAllowed {node = nodeNum, uname = uname};
ref showNormal = true;
if $"cmd" = "socks" then
showNormal := false;
val socks = $"socks";
- %>Are you sure you want to request that socket permissions for <b><% Web.html uname %></b> be changed to <b><% Web.html socks %></b>?<br>
- <a href="sec?cmd=socks2&uname=<% Web.urlEncode uname %>&socks=<% Web.urlEncode socks %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request that socket permissions for <b><% Web.html uname %></b> on <b><% Web.html nodeName %></b> be changed to <b><% Web.html socks %></b>?<br>
+ <a href="sec?cmd=socks2&node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&socks=<% Web.urlEncode socks %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
elseif $"cmd" = "socks2" then
- val id = Sec.Req.add (you, String.concat [uname, ": change socket permissions to ", $"socks"], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": change socket permissions to ", $"socks"], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
elseif $"cmd" = "tpe" then
showNormal := false;
val tpe = iff $"tpe" = "yes" then "on" else "off";
- %>Are you sure you want to request that trusted-path-executables-only for <b><% Web.html uname %></b> be turned <b><% tpe %></b>?<br>
- <a href="sec?cmd=tpe2&uname=<% Web.urlEncode uname %>&tpe=<% tpe %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request that trusted-path-executables-only for <b><% Web.html uname %></b> on <b><% Web.html nodeName %></b> be turned <b><% tpe %></b>?<br>
+ <a href="sec?cmd=tpe2&node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&tpe=<% tpe %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
elseif $"cmd" = "tpe2" then
- val id = Sec.Req.add (you, String.concat [uname, ": turn tpe ", $"tpe"], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": turn tpe ", $"tpe"], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
elseif $"cmd" = "cron" then
showNormal := false;
val cron = iff $"cron" = "yes" then "enabled" else "disabled";
- %>Are you sure you want to request that <tt>cron</tt> permissions for <b><% Web.html uname %></b> be <b><% cron %></b>?<br>
- <a href="sec?cmd=cron2&uname=<% Web.urlEncode uname %>&cron=<% cron %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request that <tt>cron</tt> permissions for <b><% Web.html uname %></b> on <b><% Web.html nodeName %></b> be <b><% cron %></b>?<br>
+ <a href="sec?cmd=cron2&node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&cron=<% cron %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
elseif $"cmd" = "cron2" then
val cron = iff $"cron" = "enabled" then "enable" else "disable";
- val id = Sec.Req.add (you, String.concat [uname, ": ", cron, " cron access"], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": ", cron, " cron access"], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
elseif $"cmd" = "ftp" then
showNormal := false;
val ftp = iff $"ftp" = "yes" then "enabled" else "disabled";
- %>Are you sure you want to request that FTP permissions for <b><% Web.html uname %></b> be <b><% ftp %></b>?<br>
- <a href="sec?cmd=ftp2&uname=<% Web.urlEncode uname %>&ftp=<% ftp %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request that FTP permissions for <b><% Web.html uname %></b> on <b><% Web.html nodeName %></b> be <b><% ftp %></b>?<br>
+ <a href="sec?cmd=ftp2&node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&ftp=<% ftp %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
elseif $"cmd" = "ftp2" then
val ftp = iff $"ftp" = "enabled" then "enable" else "disable";
- val id = Sec.Req.add (you, String.concat [uname, ": ", ftp, " FTP access"], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat [uname, ": ", ftp, " FTP access"], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
elseif $"cmd" = "rule" then
showNormal := false;
val rule = $"rule";
- %>Are you sure you want to request the firewall rule <b><% Web.html uname %> <% Web.html rule %></b>?<br>
- <a href="sec?cmd=rule2&uname=<% Web.urlEncode uname %>&rule=<% Web.urlEncode rule %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request the firewall rule <b><% Web.html uname %> <% Web.html rule %></b> on <b><% Web.html nodeName %></b>?<br>
+ <a href="sec?cmd=rule2&node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&rule=<% Web.urlEncode rule %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
elseif $"cmd" = "rule2" then
- val id = Sec.Req.add (you, String.concat ["Add firewall rule \"", uname, " ", $"rule", "\""], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat ["Add firewall rule \"", uname, " ", $"rule", "\""], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
if oldRule = rule then
%>You didn't modify the textbox for this rule before clicking the button, so there is no request to be made.<%
else
- %>Are you sure you want to request that firewall rule <b><% Web.html uname %> <% Web.html oldRule %></b> be replaced by <b><% Web.html uname %> <% Web.html rule %></b>?<br>
- <a href="sec?uname=<% Web.urlEncode uname %>&modRule2=<% Web.urlEncode oldRule %>&rule=<% Web.urlEncode rule %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request that firewall rule <b><% Web.html uname %> <% Web.html oldRule %></b> be replaced by <b><% Web.html uname %> <% Web.html rule %></b> on <b><% Web.html nodeName %></b>?<br>
+ <a href="sec?node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&modRule2=<% Web.urlEncode oldRule %>&rule=<% Web.urlEncode rule %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
end
elseif $"modRule2" <> "" then
- val id = Sec.Req.add (you, String.concat ["Change firewall rule \"", uname, " ", $"modRule2", "\" to \"", uname, " ", $"rule", "\""], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat ["Change firewall rule \"", uname, " ", $"modRule2", "\" to \"", uname, " ", $"rule", "\""], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
elseif $"delRule" <> "" then
showNormal := false;
val oldRule = $"delRule";
- %>Are you sure you want to request that firewall rule <b><% Web.html uname %> <% Web.html oldRule %></b> be <b>deleted</b>?<br>
- <a href="sec?uname=<% Web.urlEncode uname %>&delRule2=<% Web.urlEncode oldRule %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
+ %>Are you sure you want to request that firewall rule <b><% Web.html uname %> <% Web.html oldRule %></b> on <b><% Web.html nodeName %></b> be <b>deleted</bD>?<br>
+ <a href="sec?node=<% nodeNum %>&uname=<% Web.urlEncode uname %>&delRule2=<% Web.urlEncode oldRule %>&msg=<% Web.urlEncode ($"msg") %>">Yes, place the request!</a><%
elseif $"delRule2" <> "" then
- val id = Sec.Req.add (you, String.concat ["Delete firewall rule \"", uname, " ", $"delRule2", "\""], $"msg");
+ val id = Sec.Req.add {usr = you, node = nodeNum, data = String.concat ["Delete firewall rule \"", uname, " ", $"delRule2", "\""], msg = $"msg"};
if not (Sec.Req.notifyNew id) then
%><h3>Error sending e-mail notification</h3><%
end
<table class="blanks">
<tr> <td>By:</td> <td><a href="user?id=<% #usr req %>"><% name %></a></td> </tr>
<tr> <td>Time:</td> <td><% #stamp req %></td> </tr>
+<tr> <td>Node:</td> <td><% Web.html (Init.nodeName (#node req)) %></td> </tr>
<tr> <td>Request:</td> <td><% #data req %></td> </tr>
<tr> <td>Msg:</td> <td colspan="2"><% Web.html (#msg req) %></td> </tr>
</table>
<table class="blanks">
<tr> <td>By:</td> <td colspan="2"><a href="user?id=<% #usr req %>"><% name %></a></td> </tr>
<tr> <td>Time:</td> <td colspan="2"><% #stamp req %></td> </tr>
+<tr> <td>Node:</td> <td><% Web.html (Init.nodeName (#node req)) %></td> </tr>
<tr> <td>Request:</td> <td><% #data req %></td> </tr>
<tr> <td>Reason:</td> <td colspan="2"><% Web.html (#msg req) %></td> </tr>
</table>
<option value="1"<% if #status req = Sec.Req.INSTALLED then %> selected<% end %>>Installed</option>
<option value="2"<% if #status req = Sec.Req.REJECTED then %> selected<% end %>>Rejected</option>
</select></td> </tr>
+<tr> <td>Node:</td> <td><select name="node">
+<% foreach node in Init.listNodes () do %>
+ <option value="<% #id node %>"<% if nodeNum = #node req then %> selected<% end %>><% Web.html (#name node) %> (<% Web.html (#descr node) %>)</option>
+<% end %></select></td> </tr>
<tr> <td>Request:</td> <td><input name="req" value="<% #data req %>"></td> </tr>
<tr> <td>Message:</td> <td><textarea name="msg" rows="10" cols="80" wrap="soft"><% Web.html (#msg req) %></textarea></td> </tr>
<tr> <td><input type="submit" value="Save"></td> </tr>
val req = Sec.Req.lookup id;
val oldStatus = #status req;
val newStatus = Sec.Req.statusFromInt (Web.stoi ($"status"));
- Sec.Req.modify {req with data = $"req", msg = $"msg", status = newStatus};
+ Sec.Req.modify {req with node = nodeNum, data = $"req", msg = $"msg", status = newStatus};
if oldStatus <> newStatus then
- if not (Sec.Req.notifyMod (oldStatus, newStatus, Init.getUserName(), id)) then
+ if not (Sec.Req.notifyMod {old = oldStatus, new = newStatus, changer = Init.getUserName(), req = id}) then
%><h3>Error sending e-mail notification</h3><%
end
end
val id = Web.stoi ($"del");
val req = Sec.Req.lookup id;
val user = Init.lookupUser (#usr req)
- %><h3>Are you sure you want to delete request by <% #name user %> for "<% #data req %>"?</h3>
+ %><h3>Are you sure you want to delete request by <% #name user %> for "<% #data req %>" on <% Web.html (Init.nodeName (#node req)) %>?</h3>
<a href="sec?del2=<% id %>">Yes, I'm sure!</a>
<% elseif $"del2" <> "" then
if showNormal then %>
+<table class="blanks">
+<form action="sec" method="post">
+<input type="hidden" name="uname" value="<% Web.html uname %>">
+<tr> <td>Machines:</td> <td><select name="node">
+<% foreach node in Init.listNodes () do %>
+ <option value="<% #id node %>"<% if nodeNum = #id node then %> selected<% end %>><% Web.html (#name node) %> (<% Web.html (#descr node) %>)</option>
+<% end %></select></td>
+<td><input type="submit" value="Switch"></td> </tr>
+</form>
<form action="sec" method="post">
-<b>Your users:</b> <select name="uname">
+<input type="hidden" name="node" value="<% nodeNum %>">
+<tr> <td>Your users:</td> <td><select name="uname">
<% foreach name in (yourname :: Sec.findSubusers yourname) do %>
<option value="<% name %>"<% if uname = name then %> selected<% end %>><% name %></option>
-<% end %></select> <input type="submit" value="Switch"> </form>
+<% end %></select></td>
+<td><input type="submit" value="Switch"></td> </tr>
+</form>
+</table>
<h3>Request socket permissions change</h3>
<form action="sec" method="post">
+<input type="hidden" name="node" value="<% nodeNum %>">
<input type="hidden" name="uname" value="<% uname %>">
<input type="hidden" name="cmd" value="socks">
<table class="blanks">
<h3>Request change to your execute permissions</h3>
<form action="sec" method="post">
+<input type="hidden" name="node" value="<% nodeNum %>">
<input type="hidden" name="uname" value="<% uname %>">
<input type="hidden" name="cmd" value="tpe">
<table class="blanks">
<h3>Request change to your <tt>cron</tt> permissions</h3>
<form action="sec" method="post">
+<input type="hidden" name="node" value="<% nodeNum %>">
<input type="hidden" name="uname" value="<% uname %>">
<input type="hidden" name="cmd" value="cron">
<table class="blanks">
<p>Please read <a href="http://wiki.hcoop.net/wiki/FileTransfer">our wiki instructions on file transfer</a> before requesting FTP access. Almost everyone should use alternative protocols to FTP that provide superior security benefits.</p>
<form action="sec" method="post">
+<input type="hidden" name="node" value="<% nodeNum %>">
<input type="hidden" name="uname" value="<% uname %>">
<input type="hidden" name="cmd" value="ftp">
<table class="blanks">
</table>
</form>
-<% val rules = Sec.findFirewallRules uname;
+<% val rules = Sec.findFirewallRules {node = nodeNum, uname = uname};
switch rules of
_::_ => %>
<h3>Your firewall rules</h3>
<% foreach rule in rules do %>
<form action="sec" method="post">
+<input type="hidden" name="node" value="<% nodeNum %>">
<input type="hidden" name="uname" value="<% uname %>">
<input type="hidden" name="modRule" value="<% Web.html rule %>">
<input name="rule" value="<% Web.html rule %>">
<p>Please note that <b>your firewall rule will be useless</b> if you don't first request the corresponding socket privileges at the top of this page.</p>
<form action="sec" method="post">
+<input type="hidden" name="node" value="<% nodeNum %>">
<input type="hidden" name="uname" value="<% uname %>">
<input type="hidden" name="cmd" value="rule">
<table class="blanks">
-structure Sec :> SEC = struct
-
-open Init Util Sql
-
-structure Req = Request(struct
- val table = "Sec"
- val adminGroup = "server"
- fun subject _ = "Security permissions change request"
- val template = "sec"
- val descr = "change"
- fun body (mail, req) =
- (Mail.mwrite (mail, req);
- Mail.mwrite (mail, "\n"))
- end)
-
-fun findSubusers uname =
- let
- val uname_under = uname ^ "_"
- val inf = TextIO.openIn "/etc/passwd"
-
- fun loop subs =
- case TextIO.inputLine inf of
- NONE => ListMergeSort.sort (fn (x, y) => String.compare (x, y) = GREATER) subs
- | SOME line =>
- case String.fields (fn ch => ch = #":") line of
- uname'::_ =>
- if size uname' >= size uname_under
- andalso String.substring (uname', 0, size uname_under) = uname_under then
- loop (uname' :: subs)
- else
- loop subs
- | _ => loop subs
- in
- loop []
- before TextIO.closeIn inf
- end
-
-datatype socket_perms =
- ANY
- | CLIENT_ONLY
- | SERVER_ONLY
- | NADA
-
-fun inGroup (uname, grp) =
- let
- val uname_under = uname ^ "_"
- val inf = TextIO.openIn "/etc/group"
-
- fun loop () =
- case TextIO.inputLine inf of
- NONE => false
- | SOME line =>
- case String.fields (fn ch => ch = #":") line of
- [gname, _, _, members] =>
- if gname = grp then
- mem (uname, String.fields (fn ch => ch = #",") members)
- else
- loop ()
- | _ => loop ()
- in
- loop ()
- before TextIO.closeIn inf
- end
-
-fun socketPerms uname =
- if inGroup (uname, "no-sockets") then
- NADA
- else if inGroup (uname, "no-cli-sockets") then
- if inGroup (uname, "no-serv-sockets") then
- NADA
- else
- SERVER_ONLY
- else if inGroup (uname, "no-serv-sockets") then
- CLIENT_ONLY
- else
- ANY
-
-fun isTpe uname = inGroup (uname, "only-tpe")
-
-fun findFirewallRules uname =
- let
- val inf = TextIO.openIn "/etc/firewall/users.rules"
-
- fun loop rules =
- case TextIO.inputLine inf of
- NONE => List.rev rules
- | SOME line =>
- if String.sub (line, 0) = #"#" then
- loop rules
- else case String.tokens Char.isSpace line of
- uname'::rest =>
- if uname = uname' then
- loop (String.concatWith " " rest :: rules)
- else
- loop rules
- | _ => loop rules
- in
- loop []
- before TextIO.closeIn inf
- end
-
-fun somethingAllowed fname uname =
- let
- val inf = TextIO.openIn fname
- val uname' = uname ^ "\n"
-
- fun loop () =
- case TextIO.inputLine inf of
- NONE => false
- | SOME line => line = uname' orelse loop ()
- in
- loop ()
- before TextIO.closeIn inf
- end
-
-val cronAllowed = somethingAllowed "/etc/cron.allow"
-val ftpAllowed = somethingAllowed "/etc/ftpusers"
-
-end
+structure Sec :> SEC = struct
+
+open Init Util Sql
+
+structure Req = RequestH(struct
+ val table = "Sec"
+ val adminGroup = "server"
+ fun subject _ = "Security permissions change request"
+ val template = "sec"
+ val descr = "change"
+ fun body {node, mail, data = req} =
+ (Mail.mwrite (mail, req);
+ Mail.mwrite (mail, "\n"))
+ end)
+
+fun findSubusers uname =
+ let
+ val uname_under = uname ^ "_"
+ val inf = TextIO.openIn "/etc/passwd"
+
+ fun loop subs =
+ case TextIO.inputLine inf of
+ NONE => ListMergeSort.sort (fn (x, y) => String.compare (x, y) = GREATER) subs
+ | SOME line =>
+ case String.fields (fn ch => ch = #":") line of
+ uname'::_ =>
+ if size uname' >= size uname_under
+ andalso String.substring (uname', 0, size uname_under) = uname_under then
+ loop (uname' :: subs)
+ else
+ loop subs
+ | _ => loop subs
+ in
+ loop []
+ before TextIO.closeIn inf
+ end
+
+datatype socket_perms =
+ ANY
+ | CLIENT_ONLY
+ | SERVER_ONLY
+ | NADA
+
+fun socketPerms {node, uname} =
+ let
+ val proc = Unix.execute ("/bin/sh",
+ ["-c",
+ "DOMTOOL_USER=apache2.deleuze.hcoop.net /usr/local/bin/domtool-admin sockperm "
+ ^ Init.nodeName node ^ " " ^ uname])
+
+ val inf = Unix.textInstreamOf proc
+
+ val p = case TextIO.inputLine inf of
+ SOME "Any\n" => ANY
+ | SOME "Client\n" => CLIENT_ONLY
+ | SOME "Server\n" => SERVER_ONLY
+ | _ => NADA
+ in
+ TextIO.closeIn inf;
+ if OS.Process.isSuccess (Unix.reap proc) then
+ p
+ else
+ NADA
+ end
+
+fun checkIt cmd {node, uname} =
+ OS.Process.isSuccess (OS.Process.system
+ ("DOMTOOL_USER=apache2.deleuze.hcoop.net /usr/local/bin/domtool-admin "
+ ^ cmd ^ " " ^ Init.nodeName node ^ " " ^ uname ^ " >/dev/null 2>/dev/null"))
+
+val isTpe = checkIt "tpe"
+val cronAllowed = checkIt "cron"
+val ftpAllowed = checkIt "ftp"
+
+fun findFirewallRules {node, uname} =
+ let
+ val proc = Unix.execute ("/bin/sh",
+ ["-c",
+ "DOMTOOL_USER=apache2.deleuze.hcoop.net /usr/local/bin/domtool-admin firewall "
+ ^ Init.nodeName node ^ " " ^ uname])
+
+ val inf = Unix.textInstreamOf proc
+
+ fun readEm lines =
+ case TextIO.inputLine inf of
+ SOME line => readEm (String.substring (line, 0, size line - 1) :: lines)
+ | NONE => rev lines
+
+ val lines = readEm []
+ in
+ TextIO.closeIn inf;
+ if OS.Process.isSuccess (Unix.reap proc) then
+ lines
+ else
+ []
+ end
+
+end