Added password reset function
authorDan Fuhry <dan@fuhry.us>
Fri, 11 Jan 2013 00:32:54 -0500
changeset 3 a044870a9d3d
parent 2 700d61d93b1b
child 4 2212b2ded8bf
Added password reset function
.hgignore
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/.htaccess
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/img/.htaccess
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/functions.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/smtp.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/starthere.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/template-wrapper.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/emails/lostpw.tpl
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/header.tpl
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/lostpw.tpl
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/resetpw.tpl
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/logout.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/lostpw.php
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/.htaccess
packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/ssoinabox.css
packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/login.tmpl
packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/logout.tmpl
resources/functions
--- a/.hgignore	Tue Jan 08 23:21:25 2013 -0500
+++ b/.hgignore	Fri Jan 11 00:32:54 2013 -0500
@@ -1,3 +1,4 @@
 ^packages/.*/.*\.deb$
 ^tarballs/
+^packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/compiled/
 ~$
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/.htaccess	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/.htaccess	Fri Jan 11 00:32:54 2013 -0500
@@ -5,8 +5,10 @@
 AuthDBMGroupFile /etc/apache2/ldap-groups
 Require group rtp
 
-<FilesMatch "^logout(\.php)?$">
+<FilesMatch "^((webauth-)?logout|lostpw|pw-strength)(\.php)?$">
 	Require valid-user
+	Satisfy Any
+	AuthType None
 </FilesMatch>
 
 RewriteEngine on
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/img/.htaccess	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,3 @@
+AuthType None
+Satisfy Any
+Require valid-user
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/functions.php	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/functions.php	Fri Jan 11 00:32:54 2013 -0500
@@ -29,7 +29,7 @@
 function load_credentials()
 {
 	$config = yaml_parse_file("/usr/local/etc/ssoinabox/webcreds.yml");
-	$keys = array('LDAP_BASEDN', 'UID_MIN', 'GID_MIN', 'ldap_server', 'ldap_manager', 'ldap_user_basedn', 'ldap_group_basedn', 'kerberos_admin', 'PHONE_EXT_MIN');
+	$keys = array('LDAP_BASEDN', 'UID_MIN', 'GID_MIN', 'ldap_server', 'ldap_manager', 'ldap_user_basedn', 'ldap_group_basedn', 'kerberos_admin', 'PHONE_EXT_MIN', 'hmac_secret');
 	
 	foreach ( $keys as $key )
 	{
@@ -42,3 +42,48 @@
 			$GLOBALS[$key] = $config[$key];
 	}
 }
+
+/**
+ * Test a password's policy compliance
+ * @param string password
+ * @return mixed true if compliant, otherwise a string describing why it isn't
+ */
+
+function test_password($str)
+{
+	if ( strlen($str) < 8 )
+		return 'must be at least 8 characters in length';
+	
+	if ( countUniqueChars($str) < 6 )
+		return 'must have at least 6 unique characters';
+	
+	if ( strlen($str) <= 16 )
+	{
+		if ( !preg_match('/[a-z]/', $str) )
+			return 'must contain at least one lowercase letter';
+		
+		if ( !preg_match('/[A-Z]/', $str) )
+			return 'must contain at least one lowercase letter';
+		
+		if ( !preg_match('/[0-9]/', $str) )
+			return 'must contain at least one lowercase letter';
+		
+		if ( !preg_match('/[^A-Za-z0-9]/', $str) )
+			return 'must contain at least one lowercase letter';
+	}
+	
+	return true;
+}
+
+function countUniqueChars($str)
+{
+	$count = 0;
+	$uniq = '';
+	for ( $i = 0; $i < strlen($str); $i++ )
+	{
+		if ( strpos($uniq, $str{$i}) === false )
+			$uniq .= $str{$i};
+	}
+	
+	return strlen($uniq);
+}
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php	Fri Jan 11 00:32:54 2013 -0500
@@ -353,3 +353,20 @@
 	
 	return ldap_delete($_ldapconn, ldap_make_group_dn($cn));
 }
+
+/**
+ * Is the given username in the specified LDAP group?
+ * @param string username
+ * @param string Group name
+ * @return bool
+ */
+
+function ldap_test_group_membership($username, $group)
+{
+	global $_ldapconn, $ldap_group_basedn;
+	
+	$filter = sprintf('(&(memberUid=%s)(cn=%s)(objectClass=posixGroup))', ldap_escape($username), ldap_escape($group));
+	
+	$result = ldap_search($_ldapconn, $ldap_group_basedn, $filter);
+	return ldap_count_entries($_ldapconn, $result) > 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/smtp.php	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Sends e-mail via SMTP.
+ * @param string Source address
+ * @param string Destination address
+ * @param string Subject
+ * @param string Message body
+ * @param string Additional headers
+ * @return bool
+ */
+
+function smtp_mail($from, $to, $subject, $body, $headers = '')
+{
+	list($domain) = array_reverse(explode('@', $to));
+	
+	// look up MX record for $domain
+	$record = dns_get_record($domain, DNS_MX);
+	
+	if ( !isset($record[0]['target']) )
+		// failed to get target server
+		return false;
+		
+	// open socket
+	$sock = fsockopen($record[0]['target'], 25, $errno, $errstr, 5);
+	if ( !$sock )
+		// failed to open socket
+		return false;
+	
+	try
+	{
+		// wait for 220
+		if ( _smtp_get_response($sock) !== 220 )
+			throw new Exception("Expected 220");
+		
+		// HELO
+		// get server's FQDN
+		$hostname = gethostname();
+		if ( strpos($hostname, '.') === false )
+		{
+			$hostname .= '.' . trim(`dnsdomainname`);
+		}
+		fputs($sock, "HELO $hostname\r\n");
+		if ( _smtp_get_response($sock) !== 250 )
+			throw new Exception("Expected 250");
+		
+		// from
+		fputs($sock, "MAIL FROM: <$from>\r\n");
+		if ( _smtp_get_response($sock) !== 250 )
+			throw new Exception("Expected 250");
+		
+		// to
+		fputs($sock, "RCPT TO: <$to>\r\n");
+		if ( _smtp_get_response($sock) !== 250 )
+			throw new Exception("Expected 250");
+		
+		// data
+		fputs($sock, "DATA\r\n");
+		if ( !in_array(_smtp_get_response($sock), array(354, 250)) )
+			throw new Exception("Expected 250 or 354");
+		
+		// send headers
+		$full_headers = "Subject: $subject\r\n";
+		$full_headers .= "From: <$from>\r\n";
+		if ( !strstr($headers, "To: ") )
+			$full_headers .= "To: <$to>\r\n";
+		if ( !empty($headers) )
+			$full_headers .= trim(str_replace("\n", "\r\n", str_replace("\r\n", "\n", $headers))) . "\r\n";
+		
+		$full_headers .= "\r\n";
+		fputs($sock, $full_headers);
+		
+		// send body
+		$body = str_replace("\n", "\r\n", str_replace("\r\n", "\n", $body));
+		fputs($sock, $body);
+		
+		// send end marker
+		fputs($sock, "\r\n.\r\n");
+		if ( _smtp_get_response($sock) !== 250 )
+			throw new Exception("Expected 250");
+		
+		// end session
+		fputs($sock, "QUIT\r\n");
+		if ( _smtp_get_response($sock) !== 221 )
+			throw new Exception("Expected 221");
+		
+		fclose($sock);
+	} catch ( Exception $e )
+	{
+		fputs($sock, "QUIT\r\n");
+		_smtp_get_response($sock);
+		fclose($sock);
+		return false;
+	}
+	return true;
+}
+
+function _smtp_get_response($sock)
+{
+	while ( !feof($sock) && ($line = fgets($sock, 8192)) )
+	{
+		if ( preg_match('/^([0-9]+)(\s.+)?$/', $line, $match) )
+		{
+			return intval($match[1]);
+		}
+	}
+	return false;
+}
+
+// smtp_mail('plates@csh.rit.edu', 'plates@csh.rit.edu', 'Test e-mail', 'Testing', 'From: Plates <plates@csh.rit.edu>');
+
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/starthere.php	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/starthere.php	Fri Jan 11 00:32:54 2013 -0500
@@ -15,8 +15,12 @@
 require_once(ACCOUNTS . 'includes/ldap.php');
 require_once(ACCOUNTS . 'includes/kadm5.php');
 require_once(ACCOUNTS . 'includes/users.php');
+require_once(ACCOUNTS . 'includes/smtp.php');
 
 session_start();
 
+$adm = !empty($_SERVER['REMOTE_USER']) && ldap_test_group_membership($_SERVER['REMOTE_USER'], 'rtp');
+define('IS_ADMIN', $adm);
+
 if ( !isset($_SESSION['messages']) )
 	$_SESSION['messages'] = array();
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/template-wrapper.php	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/template-wrapper.php	Fri Jan 11 00:32:54 2013 -0500
@@ -15,6 +15,8 @@
 	if ( isset($_SERVER['REMOTE_USER']) )
 		$smarty->assign('userinfo', $ui = ldap_get_user($_SERVER['REMOTE_USER']));
 	
+	$smarty->assign('is_admin', IS_ADMIN);
+	
 	if ( $ui === false )
 		redirect('/logout');
 	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/emails/lostpw.tpl	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,9 @@
+Hello {$userinfo['cn']},
+
+You're receiving this e-mail because someone (hopefully you) requested a password reset on your account. To reset your password, please click the link below:
+
+{$reset_link}
+
+This request was received from the IP address {$ip}. If you did not request this e-mail, you may safely ignore it as the link will expire 12 hours from this e-mail's issuance.
+
+If the name you were greeted with above does not match your real name, please report this e-mail to the administrators and do not click any links in it.
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/header.tpl	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/header.tpl	Fri Jan 11 00:32:54 2013 -0500
@@ -15,10 +15,17 @@
 			<div class="navbar-inner">
 				<a class="brand" href="/"><span class="siab-logo" title="SSO in a Box">SiaB</span> Control Panel</a>
 				<ul class="nav">
-					<li><a href="/">Home</a></li>
+					{if $user}
+						<li><a href="/">Home</a></li>
+					{else}
+						<li><a href="/lostpw">Password recovery</a></li>
+					{/if}
+					{if $is_admin}
 					<li><a href="/users">Users</a></li>
 					<li><a href="/groups">Groups</a></li>
+					{/if}
 				</ul>
+				{if $user}
 				<div class="pull-right">
 					<ul class="nav">
 						<li>
@@ -33,6 +40,7 @@
 						</li>
 					</ul>
 				</div>
+				{/if}
 			</div>
 		</div>
 		<div class="main container">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/lostpw.tpl	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,22 @@
+{assign var="title" value="Lost Password"}
+{include file="header.tpl"}
+
+<h1>Forgot password</h1>
+
+<p>If you've forgotten your password but set an e-mail address for yourself, you have the option of recovering your account via a link sent to your e-mail address. To
+	get started, enter either your username or e-mail address in the form below.</p>
+	
+<form method="post" class="form-horizontal">
+	<div class="control-group">
+		<label class="control-label">E-mail address or username:</label>
+		<div class="controls">
+			<input type="text" name="email_or_username" class="input-xlarge" />
+		</div>
+	</div>
+
+	<div class="form-actions">
+		<input type="submit" value="Send recovery e-mail" class="btn btn-primary" />
+	</div>
+</form>
+
+{include file="footer.tpl"}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/resetpw.tpl	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,42 @@
+{assign var="title" value="Lost Password"}
+{include file="header.tpl"}
+
+<h1>Reset password</h1>
+
+<p>Almost done! Complete the form below to reset your password.</p>
+	
+<form method="post" class="form-horizontal">
+	
+	<!-- Username -->
+	<div class="control-group">
+		<label class="control-label">Username:</label>
+		<div class="controls">
+			<input type="text" readonly="readonly" name="uid" value="{$userinfo['uid']|escape:'html'}" />
+		</div>
+	</div>
+	
+	<!-- Password -->
+	<div class="control-group">
+		<label class="control-label">New password:</label>
+		<div class="controls">
+			<input type="password" name="password" />
+			<p class="help-block">
+				Must meet <a href="/pw-strength" onclick="window.open(this.href); return false;">password security requirements</a>.
+			</p>
+		</div>
+	</div>
+	
+	<!-- Confirm password -->
+	<div class="control-group">
+		<label class="control-label">Confirm password:</label>
+		<div class="controls">
+			<input type="password" name="password_confirm" />
+		</div>
+	</div>
+
+	<div class="form-actions">
+		<input type="submit" value="Reset password" class="btn btn-primary" />
+	</div>
+</form>
+
+{include file="footer.tpl"}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/logout.php	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,3 @@
+<?php
+require('includes/starthere.php');
+redirect(sprintf('http://%s/webauth-logout', gethostname()));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/lostpw.php	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,135 @@
+<?php
+
+require('includes/starthere.php');
+
+if ( !empty($_SERVER['PATH_INFO']) )
+{
+	if ( count($params = explode('/', $_SERVER['PATH_INFO'])) !== 4 )
+	{
+		redirect('/lostpw');
+	}
+	
+	list(,$uid,$timestamp,$token) = $params;
+	
+	try
+	{
+		$do_redirect = true;
+		if ( !preg_match('/^[a-z0-9]{3,32}$/', $uid) ||
+			 !preg_match('/^[a-f0-9]{8}$/', $timestamp) ||
+		  	 !preg_match('/^[a-f0-9]{40}$/', $token) )
+			throw new Exception("Request format is invalid");
+		
+		$ts_dec = hexdec($timestamp);
+		
+		if ( hash_hmac('sha1', "$uid%$ts_dec", $hmac_secret) !== $token )
+			throw new Exception("Request token is invalid");
+		
+		if ( abs(time() - $ts_dec) > (12 * 3600) )
+			throw new Exception("This link has expired. Please request another password reset.");
+		
+		$userinfo = ldap_get_user($uid);
+		
+		// handle a submit?
+		$do_redirect = false;
+		if ( !empty($_POST['password']) )
+		{
+			if ( ($result = test_password($_POST['password'])) !== true )
+				throw new Exception("Your new password $result.");
+			
+			if ( $_POST['password'] !== $_POST['password_confirm'] )
+				throw new Exception("The passwords you entered did not match.");
+			
+			if ( reset_password($uid, $_POST['password']) )
+			{
+				queue_message(E_NOTICE, "Your password has been reset. You can now log into the control panel.");
+				redirect('/lostpw');
+			}
+			else
+			{
+				throw new Exception("Internal error when performing password reset.");
+			}
+		}
+	}
+	catch ( Exception $e )
+	{
+		queue_message(E_ERROR, $e->getMessage());
+		if ( $do_redirect )
+			redirect('/lostpw');
+	}
+	
+	display_template('resetpw', array(
+			'userinfo' => $userinfo
+		));
+	
+	exit;
+}
+
+if ( !empty($_POST['email_or_username']) )
+{
+	try
+	{
+		global $_ldapconn, $ldap_user_basedn;
+		
+		$field = strstr($_POST['email_or_username'], '@') ? 'mail' : 'uid';
+		if ( $field == 'uid' && !preg_match('/^[a-z0-9]{3,32}$/', $_POST['email_or_username']) )
+		{
+			throw new Exception("Invalid username. Usernames can only contain 3-32 a-z and 0-9 (all lowercase) characters.");
+		}
+		
+		$search_filter = sprintf('(&(%s=%s)(objectClass=posixAccount))', $field, ldap_escape($_POST['email_or_username']));
+		$search_result = ldap_search($_ldapconn, $ldap_user_basedn, $search_filter);
+		
+		if ( !$search_result )
+		{
+			throw new Exception(ldap_error($_ldapconn));
+		}
+		
+		if ( ldap_count_entries($_ldapconn, $search_result) == 0 )
+		{
+			throw new Exception("Could not find any accounts that matched that $field.");
+		}
+		else if ( ldap_count_entries($_ldapconn, $search_result) > 1 )
+		{
+			throw new Exception("LDAP search query erroneously returned multiple results.");
+		}
+		
+		$userinfo = ldap_array_cleanup(ldap_get_attributes($_ldapconn, ldap_first_entry($_ldapconn, $search_result)));
+		
+		if ( !isset($userinfo['mail']) )
+		{
+			throw new Exception("No e-mail address is registered to your account. Please contact the administrator to request a password reset.");
+		}
+		
+		$mail_censored = $mail = is_array($userinfo['mail']) ? $userinfo['mail'][ count($userinfo['mail']) - 1 ] : $userinfo['mail'];
+		
+		// smtp_mail($from, $to, $subject, $body, $headers = '')
+		$reset_link = sprintf('http://%s/lostpw/%s/%08x/%s', gethostname(), $userinfo['uid'], time(), hash_hmac('sha1', "{$userinfo['uid']}%" . time(), $hmac_secret));
+		$email_body = parse_template('emails/lostpw', array(
+				'userinfo' => $userinfo
+				, 'reset_link' => $reset_link
+				, 'ip' => $_SERVER['REMOTE_ADDR']
+			));
+			
+		$domainname = strtolower(get_default_kerberos_realm());
+		$mail_result = smtp_mail("accounts@$domainname", $mail, "$domainname password reset", $email_body, "From: $domainname accounts <accounts@$domainname>\r\nTo: {$userinfo['cn']} <$mail>");
+		
+		// censor e-mail address to keep deviants from scraping for it
+		if ( $field == 'uid' && preg_match('/^(.)(.*?)(.)@([a-z\.]+)$/', $mail, $match) )
+			$mail_censored = sprintf('%s%s%s@%s', $match[1], str_repeat('.', strlen($match[2])), $match[3], $match[4]);
+		
+		if ( $mail_result )
+		{
+			queue_message(E_NOTICE, "Password reset instructions have been mailed to $mail_censored.");
+		}
+		else
+		{
+			throw new Exception("Failed to send e-mail.");
+		}
+	}
+	catch ( Exception $e )
+	{
+		queue_message(E_ERROR, $e->getMessage());
+	}
+}
+
+display_template('lostpw');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/.htaccess	Fri Jan 11 00:32:54 2013 -0500
@@ -0,0 +1,3 @@
+AuthType None
+Satisfy Any
+Require valid-user
--- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/ssoinabox.css	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/ssoinabox.css	Fri Jan 11 00:32:54 2013 -0500
@@ -25,7 +25,7 @@
 
 div.navbar a.brand span.siab-logo {
 	font-family: TransportDOTMedium, arial;
-	text-transform: lowercase;
+	text-transform: uppercase;
 	letter-spacing: -1px;
 	position: relative;
 	top: 2px;
--- a/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/login.tmpl	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/login.tmpl	Fri Jan 11 00:32:54 2013 -0500
@@ -99,7 +99,7 @@
 			<br />
 			<p class="links">
 				<a href="/lostpw">Forgot password?</a> &bull;
-				<a href="/security.html">Account security</a> &bull;
+				<a href="/">Account Control Panel</a> &bull;
 				<a href="/help.html">Help</a>
 			</p>
 		</form>
--- a/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/logout.tmpl	Tue Jan 08 23:21:25 2013 -0500
+++ b/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/logout.tmpl	Fri Jan 11 00:32:54 2013 -0500
@@ -33,7 +33,7 @@
 				[% END %]
 				
 				<p class="links">
-					<a href="/security.html">Account security</a> &bull;
+					<a href="/">Account Control Panel</a> &bull;
 					<a href="/help.html">Help</a>
 				</p>
 			</div>
--- a/resources/functions	Tue Jan 08 23:21:25 2013 -0500
+++ b/resources/functions	Fri Jan 11 00:32:54 2013 -0500
@@ -171,6 +171,7 @@
 objectClass: posixGroup
 description: Default POSIX group for users
 gidNumber: 500
+memberUid: $username
 
 dn: cn=rtp,ou=Groups,$ldap_suffix
 cn: rtp
@@ -352,6 +353,8 @@
 
 PHONE_EXT_MIN: 500
 
+hmac_secret: `generate_password 40`
+
 EOF
 	
 	chown root:www-data /usr/local/etc/ssoinabox/webcreds.yml