passwd : string, status : status, applied : Init.C.timestamp,
ipaddr : string option,
confirmed : Init.C.timestamp option, decided : Init.C.timestamp option,
- msg : string }
+ msg : string, unix_passwd : string }
val lookupApp : int -> app
val listApps : status -> app list
val approve : int * string -> bool
val add : int -> unit
val abortAdd : int -> unit
-end
\ No newline at end of file
+ val welcome : int -> unit
+end
forward : bool, uses : string, other : string,
passwd : string, status : status, applied : C.timestamp, ipaddr : string option,
confirmed : C.timestamp option, decided : C.timestamp option,
- msg : string}
+ msg : string, unix_passwd : string}
fun mkAppRow [id, name, rname, gname, email, forward, uses, other, passwd, status,
- applied, ipaddr, confirmed, decided, msg] =
+ applied, ipaddr, confirmed, decided, msg, unix_passwd] =
{ id = C.intFromSql id, name = C.stringFromSql name, rname = C.stringFromSql rname,
gname = (if C.isNull gname then NONE else SOME (C.stringFromSql gname)),
email = C.stringFromSql email, forward = C.boolFromSql forward,
ipaddr = (if C.isNull ipaddr then NONE else SOME (C.stringFromSql ipaddr)),
confirmed = if C.isNull confirmed then NONE else SOME (C.timestampFromSql confirmed),
decided = if C.isNull decided then NONE else SOME (C.timestampFromSql decided),
- msg = C.stringFromSql msg}
+ msg = C.stringFromSql msg, unix_passwd = C.stringFromSql unix_passwd}
| mkAppRow r = rowError ("app", r)
fun lookupApp id =
- case C.oneOrNoRows (getDb ()) ($`SELECT id, name, rname, gname, email, forward, uses, other, passwd, status, applied, ipaddr, confirmed, decided, msg
+ case C.oneOrNoRows (getDb ()) ($`SELECT id, name, rname, gname, email, forward, uses, other, passwd, status, applied, ipaddr, confirmed, decided,
+ msg, unix_passwd
FROM MemberApp
WHERE id = ^(C.intToSql id)`) of
SOME row => mkAppRow row
| NONE => raise Fail "Membership application not found"
fun listApps status =
- C.map (getDb ()) mkAppRow ($`SELECT id, name, rname, gname, email, forward, uses, other, passwd, status, applied, ipaddr, confirmed, decided, msg
+ C.map (getDb ()) mkAppRow ($`SELECT id, name, rname, gname, email, forward, uses, other, passwd, status, applied, ipaddr, confirmed, decided,
+ msg, unix_passwd
FROM MemberApp
WHERE status = ^(statusToSql status)
+ AND NOT (status = 2 AND decided < CURRENT_TIMESTAMP - INTERVAL '1 MONTH')
ORDER BY applied`)
fun mkVoteRow [id, name] = (C.intFromSql id, C.stringFromSql name)
end
fun add app =
- ignore (C.dml (getDb ()) ($`UPDATE MemberApp
- SET status = 3
- WHERE id = ^(C.intToSql app)`))
+ let
+ val _ = C.dml (getDb ()) ($`UPDATE MemberApp
+ SET status = 3
+ WHERE id = ^(C.intToSql app)`)
+
+ val app = lookupApp app
+
+ val outf = TextIO.openOut (Config.passwordFiles ^ #name app)
+ in
+ TextIO.output (outf, #unix_passwd app);
+ TextIO.closeOut outf
+ end
+
+fun welcome app =
+ let
+ val app = lookupApp app
+
+ val mail = Mail.mopen ()
+ in
+ Mail.mwrite (mail, "To: ");
+ Mail.mwrite (mail, #email app);
+ Mail.mwrite (mail, "\n");
+ Mail.mwrite (mail, Util.readFile "/home/hcoop/portal/paid.txt");
+ ignore (Mail.mclose mail)
+ end
fun abortAdd app =
ignore (C.dml (getDb ()) ($`UPDATE MemberApp
val validUser : string -> bool
val userExists : string -> bool
- val confirm : int * string -> bool
-end
\ No newline at end of file
+ val confirm : int * string -> string option
+end
structure App :> APP =
struct
-val baseUrl = "http://join.hcoop.net/join/"
+val baseUrl = "https://join.hcoop.net/join/"
val portalUrl = "https://members2.hcoop.net/portal/"
open Sql
type application = { name : string, rname : string, gname : string option, email : string,
forward : bool, uses : string, other : string }
+fun randomPassword () =
+ let
+ val proc = Unix.execute ("/usr/bin/apg", ["/usr/bin/apg", "-n", "1", "-m", "10"])
+ in
+ case TextIO.inputLine (Unix.textInstreamOf proc) of
+ NONE => raise Fail "Couldn't execute apg"
+ | SOME line =>
+ case String.tokens Char.isSpace line of
+ [s] => s
+ | _ => raise Fail "Couldn't parse output of apg"
+ end
+
fun apply {name, rname, gname, email, forward, uses, other} =
let
val db = getDb ()
let
val id = C.intFromSql id
val passwd = Int.toString (Int.abs (Random.randInt (!rnd)))
+ val unix_passwd = randomPassword ()
in
- C.dml db ($`INSERT INTO MemberApp (id, name, rname, gname, email, forward, uses, other, passwd, status, applied, msg)
+ C.dml db ($`INSERT INTO MemberApp (id, name, rname, gname, email, forward, uses, other, passwd, status, applied, msg, unix_passwd)
VALUES (^(C.intToSql id), ^(C.stringToSql name), ^(C.stringToSql rname),
^(case gname of NONE => "NULL" | SOME gname => C.stringToSql gname),
^(C.stringToSql email), ^(C.boolToSql forward), ^(C.stringToSql uses),
- ^(C.stringToSql other), ^(C.stringToSql passwd), 0, CURRENT_TIMESTAMP, '')`);
+ ^(C.stringToSql other), ^(C.stringToSql passwd), 0, CURRENT_TIMESTAMP,
+ '', ^(C.stringToSql unix_passwd))`);
sendMail (email, "Confirm membership application",
- "We've received a request to join the Internet Hosting Cooperative (hcoop.net) with this e-mail address.",
+ "We've received a request to join the Internet Hosting Cooperative (hcoop.net) with this e-mail address.",
fn mwrite => (mwrite ("To confirm this application, visit ");
mwrite (baseUrl);
mwrite ("confirm?id=");
mwrite (Int.toString id);
mwrite ("&p=");
- mwrite (passwd);
+ mwrite passwd;
mwrite ("\n")),
- id)
+ id)
end
| _ => raise Fail "Bad next sequence val"
end
let
val db = getDb ()
in
- case C.oneOrNoRows db ($`SELECT id FROM MemberApp WHERE id = ^(C.intToSql id) AND passwd = ^(C.stringToSql passwd) AND status = 0`) of
- SOME _ =>
+ case C.oneOrNoRows db ($`SELECT unix_passwd FROM MemberApp WHERE id = ^(C.intToSql id) AND passwd = ^(C.stringToSql passwd) AND status = 0`) of
+ SOME [unix_passwd] =>
(C.dml db ($`UPDATE MemberApp SET status = 1, confirmed = CURRENT_TIMESTAMP WHERE id = ^(C.intToSql id)`);
- sendMail ("board@hcoop.net",
- "New membership application",
- "We've received a new request to join hcoop.",
+ if sendMail ("board@hcoop.net",
+ "New membership application",
+ "We've received a new request to join hcoop.",
fn mwrite => (mwrite ("Open applications: ");
mwrite (portalUrl);
mwrite ("apps")),
- id))
- | NONE => false
+ id) then
+ SOME (C.stringFromSql unix_passwd)
+ else
+ NONE)
+ | NONE => NONE
end
end
-<% @header [("title", ["Confirm application"])];
-
-val id = Web.stoi ($"id");
-val passwd = $"p";
-
-if App.confirm (id, passwd) then
- %><h3><b>Confirmation successful</b></h3>
- You should hear from us within a few days from now.<%
-else
- %><h3><b>Error confirming</b></h3>
- Did you already follow this confirmation link?<%
-end;
-
-@footer[] %>
\ No newline at end of file
+<% @header [("title", ["Confirm application"])];
+
+val id = Web.stoi ($"id");
+val passwd = $"p";
+
+switch App.confirm (id, passwd) of
+ SOME unix_passwd =>
+ %><h3><b>Confirmation successful</b></h3>
+ You should hear from us within a few days from now. Save this password, to use to access our servers if your application is approved:
+ <blockquote><tt><% Web.html unix_passwd %></tt></blockquote><%
+ | NONE =>
+ %><h3><b>Error confirming</b></h3>
+ Did you already follow this confirmation link?<%
+end;
+
+@footer[] %>
\ No newline at end of file
elseif not (App.validEmail email) then
%><h3>Invalid e-mail address</h3><%
elseif not (App.apply { name = name, rname = rname, email = email,
- gname = (case gname of "" => NONE | _ => SOME gname),
+ gname = (case gname of "" => NONE | _ => SOME gname),
forward = forward, uses = uses, other = other }) then
%><h3>Error sending confirmation e-mail</h3><%
else
- %><h3>Application recorded</h3>
+ %><h3>Application recorded</h3>
Check your e-mail for a message with further instructions.<%
end
else
val appl = App.lookupApp id;
App.add id %>
-First, create this UNIX user:
-<blockquote><tt>portal_adduser <% #name appl %> "<% #rname appl %>"<% if #forward appl then %> "<% #email appl %>"<% end %></tt></blockquote>
-
-<p>You should then run <tt>visudo</tt> to add <% #name appl %> to the <tt>MEMBERS</tt> group. If you're not transmitting <% #name appl %>'s password to him by other means, run <tt>savepass <% #name appl %> <password></tt> to save it in <tt>~<% #name appl %>/.pass</tt>. If <% #name appl %> is bootstrapping with public-key ssh, don't forget to run <tt>savekey <% #name appl %> <authorized_keys file location></tt>.</p>
+First, run this on deleuze:
+<blockquote><tt>magic-create-user-wrapper <% #name appl %> "<% #rname appl %>"<% if #forward appl then %> "<% #email appl %>"<% end %></tt></blockquote>
<form action="users" method="post">
<input type="hidden" name="app" value="<% #id appl %>">
val boardEmail : string
val dbstring : string
val kerberosSuffix : string
+val passwordFiles : string
end
val dbstring = "dbname='hcoop_hcoop' user='www-data'"
val kerberosSuffix = "@HCOOP.NET"
+
+val passwordFiles = "/var/lib/portal/"
end
--- /dev/null
+Bcc: payment@hcoop.net
+Subject: Your HCoop account has been created!
+From: HCoop Board of Directors <board@hcoop.net>
+
+Your new account at hcoop.net has been created.
+
+You should now read this wiki page, which contains important information for new
+members and describes how you can start using your account:
+ http://wiki2.hcoop.net/MemberManual/GettingStarted/AccountCreated
+
+It's a good idea to read it all the way through before proceeding.
ipaddr TEXT,
confirmed TIMESTAMP,
decided TIMESTAMP,
- msg TEXT NOT NULL);
+ msg TEXT NOT NULL,
+ unix_passwd TEXT NOT NULL);
CREATE SEQUENCE MemberAppSeq START 1;
if not (Pref.subscribe ("hcoop-announce", $"name" ^ Init.emailSuffix)) then
%><h3>Error subscribing to hcoop-announce</h3><%
end
- end %>
- <h3>Member added</h3>
+ end;
+
+ if $"nomail" = "" then
+ App.welcome ap
+ end
+
+ %><h3>Member added</h3>
<% end
end
elseif $"mod" <> "" then
Subject: Welcome to HCoop!
From: HCoop Board of Directors <board@hcoop.net>
-The HCoop Board of Directors has voted to accept your membership application. Welcome to our family! :-)
+The HCoop Board of Directors has voted to accept your membership application.
+Welcome to our family! :-)
-You should now read this wiki page, which describes how we can complete your joining process:
- http://wiki.hcoop.net/wiki/NewMember
+You should now read this wiki page, which describes how we can complete your
+joining process:
+ http://wiki2.hcoop.net/MemberManual/GettingStarted/NewMember
-It's a good idea to read it all the way through and then follow the instructions on payment and account creation.
+It's a good idea to read it all the way through and then follow the instructions
+on payment and account creation.