Reports for figuring out which accounts to freeze or boot; most of new SSL request...
authorAdam Chlipala <adamc@hcoop.net>
Wed, 9 Apr 2008 13:45:02 +0000 (13:45 +0000)
committerAdam Chlipala <adamc@hcoop.net>
Wed, 9 Apr 2008 13:45:02 +0000 (13:45 +0000)
12 files changed:
cert.mlt
cert.sml
chooseDomain.mlt [new file with mode: 0644]
chooseDomain.sig [new file with mode: 0644]
chooseDomain.sml [new file with mode: 0644]
contact/contact.sml
money.mlt
money.sig
money.sml
sign.mlt [new file with mode: 0644]
sign.sml [new file with mode: 0644]
tables.sql

index 36e908f..33cc148 100644 (file)
--- a/cert.mlt
+++ b/cert.mlt
@@ -3,57 +3,58 @@
 val admin = Group.inGroupName "server";
 
 if $"new" <> "" then
-        if $"kind" = "cert" then
-          if $"req" <> "" then
-             %><h3>You filled data in next to a textbox but didn't check the radio button next to it.</h3><%
-          elseif $"cert" = "" then
-             %><h3>Please fill in a path to a certificate.</h3><%
-          else
-              val cert = $"cert";
-             val msg = $"msg";
-
-             %>Are you sure you want to ask for permissions on an SSL certificate at <% cert %>?<br><br>
-             <a href="cert?cmd=request&req=Certificate+<% cert %>&msg=<% Web.urlEncode msg %>">Yes, I want to request that.</a><%
-          end
-        else
-          if $"cert" <> "" then
-             %><h3>You filled data in next to a textbox but didn't check the radio button next to it.</h3><%
-          elseif $"req" = "" then
-             %><h3>Please fill in a path to an OpenSSL request.</h3><%
-          else
-              val req = $"req";
-             val msg = $"msg";
-
-             %>Are you sure you want to ask for permissions on an SSL certificate to be created from an OpenSSL request at <% req %>?<br><br>
-             <a href="cert?cmd=request&req=OpenSSL+request+<% req %>&msg=<% Web.urlEncode msg %>">Yes, I want to request that.</a><%
-          end
-        end
-
+   val cert = $"cert";
+   val domain = $"domain";
+   val msg = $"msg";
+
+   if cert = "" then
+      %><h3>Please fill in a path to a certificate.</h3><%
+   elseif not (ChooseDomain.yourDomain {user = Init.getUserName (), domain = domain}) then
+      %><h3>You don't have permissions on domain <tt><% Web.html domain %></tt>.</h3><%
+   else
+      %>Are you sure you want to ask for permissions on an SSL certificate
+      <li> at <tt><% Web.html cert %></tt>,</li>
+      <li> for domain <tt><% domain %></tt>?</li>
+      <a href="?cmd=request&cert=<% Web.html cert %>&domain=<% Web.html domain %>&msg=<% Web.urlEncode msg %>">Yes, I want to request that.</a><%
+   end
 elseif $"cmd" = "request" then
-       val id = Cert.add (Init.getUserId (), $"req", $"msg");
-       if not (Cert.notifyNew id) then
-               %><h3>Error sending e-mail notification</h3><%
-       end
-       %><h3>Request added</h3><%
-
+   val cert = $"cert";
+   val domain = $"domain";
+   val msg = $"msg";
+
+   if cert = "" then
+      %><h3>Please fill in a path to a certificate.</h3><%
+   elseif not (ChooseDomain.yourDomain {user = Init.getUserName (), domain = domain}) then
+      %><h3>You don't have permissions on domain <tt><% Web.html domain %></tt>.</h3><%
+   else
+     val text = "/afs/hcoop.net/common/etc/scripts/ca-install "
+       ^ Init.getUserName () ^ " "
+       ^ domain ^ " "
+       ^ cert
+
+     val id = Cert.add (Init.getUserId (), text, msg);
+     if not (Cert.notifyNew id) then
+       %><h3>Error sending e-mail notification</h3><%
+     end
+     %><h3>Request added</h3><%
+  end
 elseif $"cmd" = "open" then
        %><h3>Open requests</h3>
-       <a href="cert?cmd=list">List all requests</a><%
+       <a href="?cmd=list">List all requests</a><%
 
        foreach (name, req) in Cert.listOpen () do %>
 <br><hr><br>
 <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 %> (<% Util.diffFromNow (#stamp req) %> ago)</td> </tr>
-<tr> <td>Request:</td> <td><% #data req %></td> </tr>
+<tr> <td>Request:</td> <td><tt><% #data req %></tt></td> </tr>
 <tr> <td>Reason:</td> <td><% Web.html (#msg req) %></td> </tr>
 </table>
 
 <% if admin then %>
        <br>
-       <a href="cert?mod=<% #id req %>">[Modify]</a>
-       <a href="cert?del=<% #id req %>">[Delete]</a><br>
-       To install, run: <i>tell adamc what text to put here</i>.
+       <a href="?mod=<% #id req %>">[Modify]</a>
+       <a href="?del=<% #id req %>">[Delete]</a><br>
 <% end %>
 
 <%     end
@@ -66,14 +67,14 @@ elseif $"cmd" = "list" then
 <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 %> (<% Util.diffFromNow (#stamp req) %> ago)</td> </tr>
-<tr> <td>Request:</td> <td><% #data req %></td> </tr>
+<tr> <td>Request:</td> <td><tt><% #data req %></tt></td> </tr>
 <tr> <td>Reason:</td> <td><% Web.html (#msg req) %></td> </tr>
 </table>
 
 <% if admin then %>
        <br>
-       <a href="cert?mod=<% #id req %>">[Modify]</a>
-       <a href="cert?del=<% #id req %>">[Delete]</a>
+       <a href="?mod=<% #id req %>">[Modify]</a>
+       <a href="?del=<% #id req %>">[Delete]</a>
 <% end %>
 
 <%     end
@@ -114,7 +115,7 @@ elseif $"mod" <> "" then
                end
        end
        %><h3>Request modified</h3>
-       Back to: <a href="cert?cmd=open">open requests</a>, <a href="cert?cmd=list">all requests</a>
+       Back to: <a href="?cmd=open">open requests</a>, <a href="?cmd=list">all requests</a>
 
 <% elseif $"del" <> "" then
        Group.requireGroupName "server";
@@ -122,24 +123,22 @@ elseif $"mod" <> "" then
        val req = Cert.lookup id;
        val user = Init.lookupUser (#usr req)
        %><h3>Are you sure you want to delete request by <% #name user %> for <tt><% #data req %></tt>?</h3>
-       <a href="cert?del2=<% id %>">Yes, I'm sure!</a>
+       <a href="?del2=<% id %>">Yes, I'm sure!</a>
 
 <% elseif $"del2" <> "" then
        Group.requireGroupName "server";
        val id = Web.stoi ($"del2");
        Cert.delete id
        %><h3>Request deleted</b><h3>
-       Back to: <a href="cert?cmd=open">open requests</a>, <a href="cert?cmd=list">all requests</a>
+       Back to: <a href="?cmd=open">open requests</a>, <a href="?cmd=list">all requests</a>
 
 <% else %>
 
-<h3>Request permissions on an SSL certificate</h3>
-
-<p>Use this form to request use Domtool permissions to use an SSL certificate.  Give the location of your certificate request or certificate within <tt>/afs/hcoop.net</tt>.</p>
+<h3>Request installation of an SSL certificate</h3>
 
-<p>If you have your own certificate signed by an outside provider like Verisign, then choose the second option and give the path to that certificate.</p>
+<p>Use this form to request Domtool permissions to use an SSL certificate.  Give the location of your certificate/key (<tt>.pem</tt>) file within <tt>/afs/hcoop.net</tt>.</p>
 
-<p>If you want your certificate authenticated by chaining through HCoop's root certificate, then choose the first option and give the path to an OpenSSL certificate request.   In the comments field, be sure to specify the number of days that you would like the certificate to be valid.  If you do not specify a value, we will use 3650 days.  Make sure that the key file is in the same directory as the certificate request, and that it has a "<tt>.key</tt>" extension.</p>
+<p>If you want your certificate authenticated by chaining through HCoop's root certificate, then <a href="sign">get it signed</a> before submitting this form.</p>
 
 <p>Note that you can't use SSL certificates very well over HTTPS without an IP address assigned to your web virtual host.  You can request one separately on <a href="ip">the IP address request page</a>.</p>
 
@@ -148,8 +147,8 @@ elseif $"mod" <> "" then
 <form method="post">
 <input type="hidden" name="new" value="1">
 <table class="blanks">
-<tr> <td>OpenSSL request: <input type="radio" name="kind" value="req" checked></td> <td><input name="req" size="60"></td> </tr>
-<tr> <td>OpenSSL certificate: <input type="radio" name="kind" value="cert"></td> <td><input name="cert" size="60"></td> </tr>
+<tr> <td>Domain:</td> <td><% @chooseDomain[] %></td> </tr>
+<tr> <td>OpenSSL certificate:</td> <td><input name="cert" size="60"></td> </tr>
 <tr> <td>Additional comments:</td> <td><textarea name="msg" rows="5" cols="80" wrap="soft"></textarea></td> </tr>
 <tr> <td><input type="submit" value="Request"></td> </tr>
 </table>
index 65ed392..d87ac2e 100644 (file)
--- a/cert.sml
+++ b/cert.sml
@@ -1,7 +1,7 @@
 structure Cert = Request(struct
                         val table = "Cert"
                         val adminGroup = "server"
-                        fun subject _ = "SSL certificate request"
+                        fun subject _ = "SSL certificate installation request"
                         val template = "cert"
                         val descr = "SSL certificate"
                                      
diff --git a/chooseDomain.mlt b/chooseDomain.mlt
new file mode 100644 (file)
index 0000000..255bb83
--- /dev/null
@@ -0,0 +1,5 @@
+<select name="domain">
+<% foreach dom in ChooseDomain.domains (Init.getUserName ()) do %>
+   <option><% dom %></option>
+<% end %>
+</select>
diff --git a/chooseDomain.sig b/chooseDomain.sig
new file mode 100644 (file)
index 0000000..68bbb0d
--- /dev/null
@@ -0,0 +1,4 @@
+signature CHOOSE_DOMAIN = sig
+    val domains : string -> string list
+    val yourDomain : {user : string, domain : string} -> bool
+end
diff --git a/chooseDomain.sml b/chooseDomain.sml
new file mode 100644 (file)
index 0000000..e2029d7
--- /dev/null
@@ -0,0 +1,22 @@
+structure ChooseDomain :> CHOOSE_DOMAIN = struct
+
+fun domains user =
+    let
+       val proc = Unix.execute ("/bin/sh", ["-c", "DOMTOOL_USER=hcoop /usr/local/bin/domtool-admin perms " ^ user])
+       val inf = Unix.textInstreamOf proc
+
+       fun loop () =
+           case TextIO.inputLine inf of
+               NONE => []
+             | SOME line =>
+               case String.tokens (fn ch => ch = #":") line of
+                   ["domain", domains] => String.tokens Char.isSpace domains
+                 | _ => loop ()
+    in
+       loop ()
+       before ignore (Unix.reap proc)
+    end
+
+fun yourDomain {user, domain} = List.exists (fn x => x = domain) (domains user)
+
+end
index ef787f3..68ca0c3 100644 (file)
@@ -13,7 +13,7 @@ fun main _ =
                  | s _ = raise Fail "Bad allEmails row"
            in
                C.map db s
-                     "SELECT v FROM Contact JOIN ContactKind ON knd = ContactKind.id AND ContactKind.name = 'Non-hcoop e-mail' ORDER BY v"
+                     "SELECT v FROM Contact JOIN ContactKind ON knd = ContactKind.id AND ContactKind.name = 'Non-HCoop e-mail' ORDER BY v"
            end
 
        fun kindRow [id, name, url, urlPrefix, urlPostfix] =
index 8e6f24c..793e2ed 100644 (file)
--- a/money.mlt
+++ b/money.mlt
@@ -476,7 +476,27 @@ elseif $"cmd" = "delinq" then
 
    %><h3>Pledges reset.</h3><%
 
-end %>
+elseif $"cmd" = "freezeworthy" then
+       showNormal := false;
+       val dqs = Money.freezeworthyPledgers () %>
+<table>
+<tr> <th>Member</th> <th>Balance</th> </tr>
+<% foreach dq in dqs do %>
+<tr> <td><a href="user?id=<% #id dq %>"><% #name dq %></a></td> <td>$<% #balance dq %></td> </tr>
+<% end %>
+</table>
+
+<% elseif $"cmd" = "bootworthy" then
+       showNormal := false;
+       val dqs = Money.bootworthyPledgers () %>
+<table>
+<tr> <th>Member</th> <th>Balance</th> </tr>
+<% foreach dq in dqs do %>
+<tr> <td><a href="user?id=<% #id dq %>"><% #name dq %></a></td> <td>$<% #balance dq %></td> </tr>
+<% end %>
+</table>
+
+<% end %>
 
 <% if showNormal then
    val you = Init.getUser();
@@ -493,6 +513,8 @@ Deposit: $<% deposit %></b> (3 months of dues at the minimal <a href="pledge">pl
 <a href="money?cmd=list">List all transactions</a><br>
 <a href="money?cmd=bals">List active balances</a><br>
 <a href="money?cmd=nbals">List negative active balances</a><br>
+<a href="money?cmd=freezeworthy">List members who deserve account freezing</a><br>
+<a href="money?cmd=bootworthy">List members who deserve to be kicked out</a><br>
 <a href="money?cmd=deadbals">List retired balances</a><br>
 
 <% if (Group.inGroupName "money" and $"lookback" = "") or $"audit" <> "" then %>
index 087666d..1abf0fa 100644 (file)
--- a/money.sig
+++ b/money.sig
@@ -36,4 +36,7 @@ sig
 
     val delinquentPledgers : unit -> { id : int, name : string, shares : int, balance : real } list
     val resetPledges : int list -> unit
+
+    val freezeworthyPledgers : unit -> { id : int, name : string, balance : real } list
+    val bootworthyPledgers : unit -> { id : int, name : string, balance : real } list
 end
index 1b569c8..e077fcc 100644 (file)
--- a/money.sml
+++ b/money.sml
@@ -294,4 +294,33 @@ fun delinquentPledgers () =
 fun resetPledges ids =
     raise Fail ($`UPDATE WebUser SET shares = 1 WHERE id IN (^(String.concatWith ", " (List.map C.intToSql ids)))`)
 
+fun freezeworthyPledgers () =
+    let
+       val costBase = costBase monthlyCost
+
+       fun makeRow [id, name, amount] = {id = C.intFromSql id, name = C.stringFromSql name,
+                                         balance = C.realFromSql amount}
+         | makeRow row = Init.rowError ("Bad freezeworthyPledgers", row)
+    in
+       C.map (getDb ()) makeRow ($`SELECT WebUserPaying.id, WebUserPaying.name, amount
+                                   FROM WebUserPaying JOIN Balance ON Balance.id = bal
+                                   WHERE amount >= ^(C.realToSql costBase) * ^(C.intToSql graceMonths)
+                                     AND amount < ^(C.realToSql costBase) * ^(C.intToSql (graceMonths + 1))
+                                   ORDER BY name`)
+    end
+
+fun bootworthyPledgers () =
+    let
+       val costBase = costBase monthlyCost
+
+       fun makeRow [id, name, amount] = {id = C.intFromSql id, name = C.stringFromSql name,
+                                         balance = C.realFromSql amount}
+         | makeRow row = Init.rowError ("Bad bootworthyPledgers", row)
+    in
+       C.map (getDb ()) makeRow ($`SELECT WebUserPaying.id, WebUserPaying.name, amount
+                                   FROM WebUserPaying JOIN Balance ON Balance.id = bal
+                                   WHERE amount < ^(C.realToSql costBase) * ^(C.intToSql graceMonths)
+                                   ORDER BY name`)
+    end
+
 end
diff --git a/sign.mlt b/sign.mlt
new file mode 100644 (file)
index 0000000..4126f93
--- /dev/null
+++ b/sign.mlt
@@ -0,0 +1,175 @@
+<% @header [("title", ["SSL certificate signing requests"])];
+
+val admin = Group.inGroupName "server";
+
+if $"new" <> "" then
+   val req = $"req";
+   val key = $"key";
+   val days = Web.stoi ($"days");
+   val domain = $"domain";
+   val msg = $"msg";
+
+   if req = "" then
+      %><h3>Please fill in a path to a certificate request.</h3><%
+   elseif key = "" then
+      %><h3>Please fill in a path to a key.</h3><%
+   elseif days <= 0 then
+      %><h3>Please give a positive number of days for the certificate to live.</h3><%
+   elseif not (ChooseDomain.yourDomain {user = Init.getUserName (), domain = domain}) then
+      %><h3>You don't have permissions on domain <tt><% Web.html domain %></tt>.</h3><%
+   else
+      %>Are you sure you want to ask for permissions on an SSL certificate
+      <li> with request <tt><% Web.html req %></tt>,</li>
+      <li> with key <tt><% Web.html key %></tt>,</li>
+      <li> for domain <tt><% domain %></tt>,</li>
+      <li> lasting <% days %> days?</li>
+      <a href="?cmd=request&req=<% Web.html req %>&key=<% Web.html key %>&days=<% days %>&domain=<% Web.html domain %>&msg=<% Web.urlEncode msg %>">Yes, I want to request that.</a><%
+   end
+elseif $"cmd" = "request" then
+   val req = $"req";
+   val key = $"key";
+   val days = Web.stoi ($"days");
+   val domain = $"domain";
+   val msg = $"msg";
+
+   if req = "" then
+      %><h3>Please fill in a path to a certificate request.</h3><%
+   elseif key = "" then
+      %><h3>Please fill in a path to a key.</h3><%
+   elseif days <= 0 then
+      %><h3>Please give a positive number of days for the certificate to live.</h3><%
+   elseif not (ChooseDomain.yourDomain {user = Init.getUserName (), domain = domain}) then
+      %><h3>You don't have permissions on domain <tt><% Web.html domain %></tt>.</h3><%
+   else
+     val gen = req;
+
+     val text = "/afs/hcoop.net/common/etc/scripts/ca-sign "
+       ^ Int.toString days ^ " "
+       ^ req ^ " "
+       ^ key ^ " "
+       ^ gen ^ " "
+       ^ domain;
+
+     val id = Sign.add (Init.getUserId (), text, msg);
+     if not (Sign.notifyNew id) then
+       %><h3>Error sending e-mail notification</h3><%
+     end
+     %><h3>Request added</h3><%
+  end
+elseif $"cmd" = "open" then
+       %><h3>Open requests</h3>
+       <a href="?cmd=list">List all requests</a><%
+
+       foreach (name, req) in Sign.listOpen () do %>
+<br><hr><br>
+<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 %> (<% Util.diffFromNow (#stamp req) %> ago)</td> </tr>
+<tr> <td>Request:</td> <td><tt><% #data req %></tt></td> </tr>
+<tr> <td>Reason:</td> <td><% Web.html (#msg req) %></td> </tr>
+</table>
+
+<% if admin then %>
+       <br>
+       <a href="?mod=<% #id req %>">[Modify]</a>
+       <a href="?del=<% #id req %>">[Delete]</a><br>
+<% end %>
+
+<%     end
+
+elseif $"cmd" = "list" then
+       %><h3>All requests</h3><%
+
+       foreach (name, req) in Sign.list () do %>
+<br><hr><br>
+<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 %> (<% Util.diffFromNow (#stamp req) %> ago)</td> </tr>
+<tr> <td>Request:</td> <td><tt><% #data req %></tt></td> </tr>
+<tr> <td>Reason:</td> <td><% Web.html (#msg req) %></td> </tr>
+</table>
+
+<% if admin then %>
+       <br>
+       <a href="?mod=<% #id req %>">[Modify]</a>
+       <a href="?del=<% #id req %>">[Delete]</a>
+<% end %>
+
+<%     end
+
+elseif $"mod" <> "" then
+       Group.requireGroupName "server";
+       val id = Web.stoi ($"mod");
+       val req = Sign.lookup id;
+       val user = Init.lookupUser (#usr req) %>
+<h3>Handle request</h3>
+
+<form method="post">
+<input type="hidden" name="save" value="<% id %>">
+<table class="blanks">
+<tr> <td>Requestor:</td> <td><a href="user?id=<% #usr req %>"><% #name user %></a></td> </tr>
+<tr> <td>Time:</td> <td><% #stamp req %> (<% Util.diffFromNow (#stamp req) %> ago)</td> </tr>
+<tr> <td>Status:</td> <td><select name="status">
+       <option value="0"<% if #status req = Sign.NEW then %> selected<% end %>>New</option>
+       <option value="1"<% if #status req = Sign.INSTALLED then %> selected<% end %>>Installed</option>
+       <option value="2"<% if #status req = Sign.REJECTED then %> selected<% end %>>Rejected</option>
+</select></td> </tr>
+<tr> <td>Request:</td> <td><input name="req" size="60" value="<% Web.html (#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>
+</table>
+</form>
+
+<% elseif $"save" <> "" then
+       Group.requireGroupName "server";
+       val id = Web.stoi ($"save");
+       val req = Sign.lookup id;
+       val oldStatus = #status req;
+       val newStatus = Sign.statusFromInt (Web.stoi ($"status"));
+       Sign.modify {req with data = $"req", msg = $"msg", status = newStatus};
+       if oldStatus <> newStatus then
+               if not (Sign.notifyMod (oldStatus, newStatus, Init.getUserName(), id)) then
+                       %><h3>Error sending e-mail notification</h3><%
+               end
+       end
+       %><h3>Request modified</h3>
+       Back to: <a href="?cmd=open">open requests</a>, <a href="?cmd=list">all requests</a>
+
+<% elseif $"del" <> "" then
+       Group.requireGroupName "server";
+       val id = Web.stoi ($"del");
+       val req = Sign.lookup id;
+       val user = Init.lookupUser (#usr req)
+       %><h3>Are you sure you want to delete request by <% #name user %> for <tt><% #data req %></tt>?</h3>
+       <a href="?del2=<% id %>">Yes, I'm sure!</a>
+
+<% elseif $"del2" <> "" then
+       Group.requireGroupName "server";
+       val id = Web.stoi ($"del2");
+       Sign.delete id
+       %><h3>Request deleted</b><h3>
+       Back to: <a href="?cmd=open">open requests</a>, <a href="?cmd=list">all requests</a>
+
+<% else %>
+
+<h3>Request SSL certificate signing</h3>
+
+<p>Use this form to request that we sign an an SSL certificate with our certificate authority.  Give the location of your certificate request (<tt>.csr</tt> file) and key (<tt>.key</tt> file) within <tt>/afs/hcoop.net</tt>.</p>
+
+<p>The <a href="http://wiki.hcoop.net/MemberManual/ServingWebsites/SslCert">instructions on our wiki for creating SSL certificates</a> may be helpful.</p>
+
+<form method="post">
+<input type="hidden" name="new" value="1">
+<table class="blanks">
+<tr> <td>Domain:</td> <td><% @chooseDomain[] %></td> </tr>
+<tr> <td>OpenSSL request file:</td> <td><input name="req" size="60"></td> </tr>
+<tr> <td>OpenSSL key file:</td> <td><input name="key" size="60"></td> </tr>
+<tr> <td>Certificate lifetime, in days:</td> <td><input name="days" size="5" value="3650"></td></tr>
+<tr> <td>Additional comments:</td> <td><textarea name="msg" rows="5" cols="80" wrap="soft"></textarea></td> </tr>
+<tr> <td><input type="submit" value="Request"></td> </tr>
+</table>
+</form>
+
+<% end %>
+
+<% @footer[] %>
diff --git a/sign.sml b/sign.sml
new file mode 100644 (file)
index 0000000..c73414d
--- /dev/null
+++ b/sign.sml
@@ -0,0 +1,12 @@
+structure Sign = Request(struct
+                        val table = "Sign"
+                        val adminGroup = "server"
+                        fun subject _ = "SSL certificate signing request"
+                        val template = "sign"
+                        val descr = "SSL certificate signing"
+                                     
+                        fun body (mail, data) =
+                            (Mail.mwrite (mail, " Request: ");
+                             Mail.mwrite (mail, data);
+                             Mail.mwrite (mail, "\n"))
+                        end)
index b0b64da..17d755b 100644 (file)
@@ -347,6 +347,18 @@ CREATE TABLE Cert(
 
 CREATE SEQUENCE CertSeq START 1;
 
+CREATE TABLE Sign(
+       id INTEGER PRIMARY KEY,
+       usr INTEGER NOT NULL,
+       data TEXT NOT NULL,
+       msg TEXT NOT NULL,
+       status INTEGER NOT NULL,
+       stamp TIMESTAMP NOT NULL,
+       cstamp TIMESTAMP,
+       FOREIGN KEY (usr) REFERENCES WebUser(id) ON DELETE CASCADE);
+
+CREATE SEQUENCE SignSeq START 1;
+
 CREATE TABLE Quota(
        id INTEGER PRIMARY KEY,
        usr INTEGER NOT NULL,