1515 *
1616 * "30 * * * * /var/local/submitty/bin/accounts.php"
1717 *
18- * You may specify the term on the command line with "-t".
18+ * You may specify on the command line:
19+ * "-t [term code]" make auth accounts for [term code].
1920 * "-g" can be used to guess the term by the server's calendar month and year.
21+ * "-a" will make auth accounts for all instructors and all active courses.
22+ * "-r" will remove grader and student auth accounts from inactive courses.
2023 * For example:
2124 *
2225 * ./accounts.php -t s18
2629 * @author Peter Bailie, Systems Programmer (RPI dept of computer science)
2730 */
2831
29- error_reporting (0 );
30- ini_set ('display_errors ' , 0 );
32+ error_reporting (null );
33+ ini_set ('display_errors ' , ' 0 ' );
3134
3235//Database access
33- define ('DB_LOGIN ' , 'hsdbu ' );
34- define ('DB_PASSWD ' , 'hsdbu_pa55w0rd ' );
36+ define ('DB_LOGIN ' , 'submitty_dbuser ' );
37+ define ('DB_PASSWD ' , 'submitty_dbuser_pa55W0rd ' );
3538define ('DB_HOST ' , 'localhost ' );
36- define ('DB_NAME ' , 'submitty ' );
3739
3840//Location of accounts creation error log file
39- define ('ERROR_LOG_FILE ' , '/var/local/submitty/bin/accounts_errors .log ' );
41+ define ('ERROR_LOG_FILE ' , 'accounts_script_error .log ' );
4042
4143//Where to email error messages so they can get more immediate attention.
4244//Set to null to not send email.
4345define (
'ERROR_EMAIL ' ,
'[email protected] ' );
4446
47+
4548/* SUGGESTED SETTINGS FOR TIMEZONES IN USA -------------------------------------
4649 *
4750 * Eastern ........... America/New_York
6164date_default_timezone_set ('America/New_York ' );
6265
6366//Start process
64- main ();
65- exit (0 );
66-
67- /** Main process */
68- function main () {
69- //IMPORTANT: This script needs to be run as root!
70- if (posix_getuid () !== 0 ) {
71- exit ("This script must be run as root. " . PHP_EOL );
67+ new make_accounts ();
68+
69+ /** Class constructor manages script workflow */
70+ class make_accounts {
71+
72+ /** @static @var resource pgsql database connection */
73+ private static $ db_conn ;
74+ /** @static @var string what workflow to process */
75+ private static $ workflow ;
76+ /** @static @var array paramater list for DB query */
77+ private static $ db_params ;
78+ /** @static @var string DB query to be run */
79+ private static $ db_query ;
80+ /** @static @var string function to call to process $workflow */
81+ private static $ workflow_function ;
82+ /** @static @var array user_id list of 'auth only accounts', read from /etc/passwd */
83+ private static $ auth_only_accounts ;
84+
85+ public function __construct () {
86+ //IMPORTANT: This script needs to be run as root!
87+ if (posix_getuid () !== 0 ) {
88+ exit ("This script must be run as root. " . PHP_EOL );
89+ }
90+
91+ //Init class properties, quit on error.
92+ if ($ this ->init () === false ) {
93+ exit (1 );
94+ }
95+
96+ //Do workflow, quit on error.
97+ if ($ this ->process () === false ) {
98+ exit (1 );
99+ }
100+
101+ //All done.
102+ exit (0 );
72103 }
73104
74- //Check for semester among CLI arguments.
75- $ semester = cli_args::parse_args ();
76- if ($ semester === false ) {
77- exit (1 );
105+ public function __destruct () {
106+ //Close DB connection, if it exists.
107+ if (pg_connection_status (self ::$ db_conn ) === PGSQL_CONNECTION_OK ) {
108+ pg_close (self ::$ db_conn );
109+ }
110+ }
111+
112+ /**
113+ * Initialize class properties, based on self::$workflow
114+ *
115+ * @access private
116+ * @return boolean TRUE on success, FALSE when there is a problem.
117+ */
118+ private function init () {
119+ //Check CLI args.
120+ if (($ cli_args = cli_args::parse_args ()) === false ) {
121+ return false ;
122+ }
123+ self ::$ workflow = $ cli_args [0 ];
124+ self ::$ db_params = is_null ($ cli_args [1 ]) ? array () : array ($ cli_args [1 ]);
125+
126+ //Define database query AND system call based on workflow.
127+ switch (self ::$ workflow ) {
128+ case 'term ' :
129+ self ::$ db_query = <<<SQL
130+ SELECT DISTINCT user_id
131+ FROM courses_users
132+ WHERE semester=$1
133+ SQL ;
134+ self ::$ workflow_function = 'add_user ' ;
135+ break ;
136+
137+ case 'active ' :
138+ self ::$ db_query = <<<SQL
139+ SELECT DISTINCT cu.user_id
140+ FROM courses_users as cu
141+ LEFT OUTER JOIN courses as c ON cu.course=c.course AND cu.semester=c.semester
142+ WHERE cu.user_group=1 OR (cu.user_group<>1 AND c.status=1)
143+ SQL ;
144+ self ::$ workflow_function = 'add_user ' ;
145+ break ;
146+
147+ case 'clean ' :
148+ //'clean' workflow requires list of 'auth only accounts' from /etc/passwd.
149+ self ::$ auth_only_accounts = array ();
150+ if (($ fh = fopen ('/etc/passwd ' , 'r ' )) === false ) {
151+ $ this ->logit ("Cannot open '/etc/passwd' to check for auth only accounts. " );
152+ return false ;
153+ }
154+ while (($ row = fgetcsv ($ fh , 0 , ': ' )) !== false ) {
155+ if (strpos ($ row [4 ], 'auth only account ' ) !== false ) {
156+ self ::$ auth_only_accounts [] = $ row [0 ];
157+ }
158+ }
159+ fclose ($ fh );
160+ self ::$ db_query = <<<SQL
161+ SELECT DISTINCT cu.user_id
162+ FROM courses_users as cu
163+ LEFT OUTER JOIN courses as c ON cu.course=c.course AND cu.semester=c.semester
164+ WHERE cu.user_group<>1 AND c.status<>1
165+ SQL ;
166+ self ::$ workflow_function = 'remove_user ' ;
167+ break ;
168+
169+ default :
170+ $ this ->log_it ("Invalid self:: $ workflow during init() " );
171+ return false ;
172+ }
173+
174+ //Signal success
175+ return true ;
78176 }
79177
80- $ user_list = get_user_list ($ semester );
81- if ($ user_list !== false ) {
82- foreach ($ user_list as $ user ) {
83- //We don't care if user already exists as adduser will skip over any account that already exists.
84- system ("/usr/sbin/adduser --quiet --home /tmp --gecos 'RCS auth account' --no-create-home --disabled-password --shell /usr/sbin/nologin {$ user } > /dev/null 2>&1 " );
178+ /**
179+ * Process workflow
180+ *
181+ * @access private
182+ * @return boolean TRUE on success, FALSE when there is a problem.
183+ */
184+ private function process () {
185+ //Connect to database. Quit on failure.
186+ if ($ this ->db_connect () === false ) {
187+ $ this ->log_it ("Submitty Auto Account Creation: Cannot connect to DB {$ db_name }. " );
188+ return false ;
85189 }
190+
191+ //Get user list based on command. Quit on failure.
192+ if (($ result = pg_query_params (self ::$ db_conn , self ::$ db_query , self ::$ db_params )) === false ) {
193+ $ this ->log_it ("Submitty Auto Account Creation: Cannot read user list from {$ db_name }. " );
194+ return false ;
195+ }
196+
197+ $ num_rows = pg_num_rows ($ result );
198+ for ($ i = 0 ; $ i < $ num_rows ; $ i ++) {
199+ $ user = pg_fetch_result ($ result , $ i , 'user_id ' );
200+ call_user_func (array ($ this , self ::$ workflow_function ), $ user );
201+ }
202+
203+ //Signal success
204+ return true ;
86205 }
87- }
88206
89- /**
90- * Retrieve user list from courses_users
91- *
92- * @param string $semester
93- * @return array of user ids on success, boolean false on failure.
94- */
95- function get_user_list ($ semester ) {
96- $ db_user = DB_LOGIN ;
97- $ db_pass = DB_PASSWD ;
98- $ db_host = DB_HOST ;
99- $ db_name = DB_NAME ;
100- $ db_conn = pg_connect ("host= {$ db_host } dbname= {$ db_name } user= {$ db_user } password= {$ db_pass }" );
101- if ($ db_conn === false ) {
102- log_it ("Submitty Auto Account Creation: Cannot connect to DB {$ db_name }. " );
103- return false ;
207+ /**
208+ * Establish connection to Submitty Database
209+ *
210+ * @access private
211+ * @return boolean TRUE on success, FALSE when there is a problem.
212+ */
213+ private function db_connect () {
214+ $ db_user = DB_LOGIN ;
215+ $ db_pass = DB_PASSWD ;
216+ $ db_host = DB_HOST ;
217+ self ::$ db_conn = pg_connect ("host= {$ db_host } dbname=submitty user= {$ db_user } password= {$ db_pass } sslmode=prefer " );
218+ if (pg_connection_status (self ::$ db_conn ) !== PGSQL_CONNECTION_OK ) {
219+ $ this ->log_it (pg_last_error (self ::$ db_conn ));
220+ return false ;
221+ }
222+
223+ //Signal success
224+ return true ;
225+ }
226+
227+ /**
228+ * Add a user for authentication with PAM.
229+ *
230+ * @access private
231+ * @param string $user User ID to added.
232+ */
233+ private function add_user ($ user ) {
234+ system ("/usr/sbin/adduser --quiet --home /tmp --gecos 'auth only account' --no-create-home --disabled-password --shell /usr/sbin/nologin {$ user } > /dev/null 2>&1 " );
104235 }
105236
106- $ db_query = pg_query_params ($ db_conn , "SELECT DISTINCT user_id FROM courses_users WHERE semester=$1; " , array ($ semester ));
107- if ($ db_query === false ) {
108- log_it ("Submitty Auto Account Creation: Cannot read user list from {$ db_name }. " );
109- return false ;
237+ /**
238+ * Remove an 'auth only user' from authenticating with PAM
239+ *
240+ * @access private
241+ * @param string $user User ID to be checked/removed.
242+ */
243+ private function remove_user ($ user ) {
244+ //Make sure $user is an "auth only account" before removing.
245+ if (array_search ($ user , self ::$ auth_only_accounts ) !== false ) {
246+ system ("/usr/sbin/deluser --quiet {$ user } > /dev/null 2>&1 " );
247+ }
110248 }
111249
112- $ user_list = pg_fetch_all_columns ($ db_query , 0 );
113- pg_close ($ db_conn );
114- return $ user_list ;
115- }
250+ /**
251+ * Log message to email and text files
252+ *
253+ * @access private
254+ * @param string $msg
255+ */
256+ private function log_it ($ msg ) {
257+ $ msg = date ('m/d/y H:i:s : ' , time ()) . $ msg . PHP_EOL ;
258+ error_log ($ msg , 3 , ERROR_LOG_FILE );
259+
260+ if (!is_null (ERROR_EMAIL )) {
261+ error_log ($ msg , 1 , ERROR_EMAIL );
262+ }
263+ }
264+ } //END class make_accounts
116265
117266/**
118- * Log message to email and text files
267+ * class to parse command line arguments
119268 *
120- * @param string $msg
269+ * @static
121270 */
122- function log_it ($ msg ) {
123- $ msg = date ('m/d/y H:i:s : ' , time ()) . $ msg . PHP_EOL ;
124- error_log ($ msg , 3 , ERROR_LOG_FILE );
125-
126- if (!is_null (ERROR_EMAIL )) {
127- error_log ($ msg , 1 , ERROR_EMAIL );
128- }
129- }
130-
131- /** @static class to parse command line arguments */
132271class cli_args {
133272
134- /** @var array holds all CLI argument flags and their values */
135- private static $ args ;
136273 /** @var string usage help message */
137- private static $ help_usage = "Usage: accounts.php [-h | --help] (-t [term code] | -g) " . PHP_EOL ;
274+ private static $ help_usage = "Usage: accounts.php [-h | --help] (-a | - t [term code] | -g | -r ) " . PHP_EOL ;
138275 /** @var string short description help message */
139276 private static $ help_short_desc = "Read student enrollment from Submitty DB and create accounts for PAM auth. " . PHP_EOL ;
140277 /** @var string argument list help message */
141278 private static $ help_args_list = <<<HELP
142279Arguments
143280-h --help Show this help message.
144- -t [term code] Term code associated with student enrollment.
145- -g Guess the term code based on calendar month and year.
281+ -a Make auth accounts for all active courses.
282+ -t [term code] Make auth accounts for specified term code.
283+ -g Make auth accounts for guessed term code, based on calendar
284+ month and year.
285+ -r Remove auth accounts from inactive courses.
146286
147- NOTE: -t and -g are mutally exclusive . One is required.
287+ NOTE: Argument precedence order is -a, -t, -g, -r . One is required.
148288
149289HELP ;
150290
@@ -154,32 +294,29 @@ class cli_args {
154294 * Called with 'cli_args::parse_args()'
155295 *
156296 * @access public
157- * @return mixed term code as string or boolean false when no term code is present .
297+ * @return array consisting of process command ( string) and possibly associated term code (string or null) or boolean false on error .
158298 */
159299 public static function parse_args () {
160-
161- self ::$ args = getopt ('hgt: ' , array ('help ' ));
300+ $ args = getopt ('t:agrh ' , array ('help ' ));
162301
163302 switch (true ) {
164- case array_key_exists ('h ' , self :: $ args ):
165- case array_key_exists ('help ' , self :: $ args ):
303+ case array_key_exists ('h ' , $ args ):
304+ case array_key_exists ('help ' , $ args ):
166305 self ::print_help ();
167306 return false ;
168- case array_key_exists ('g ' , self ::$ args ):
169- if (array_key_exists ('t ' , self ::$ args )) {
170- //-t and -g are mutually exclusive
171- print "-g and -t cannot be used together. " . PHP_EOL ;
172- return false ;
173- } else {
174- //Guess current term
175- //(s)pring is month <= 5, (f)all is month >= 8, s(u)mmer are months 6 and 7.
176- //if ($month <= 5) {...} else if ($month >= 8) {...} else {...}
177- $ month = intval (date ("m " , time ()));
178- $ year = date ("y " , time ());
179- return ($ month <= 5 ) ? "s {$ year }" : (($ month >= 8 ) ? "f {$ year }" : "u {$ year }" );
180- }
181- case array_key_exists ('t ' , self ::$ args ):
182- return self ::$ args ['t ' ];
307+ case array_key_exists ('a ' , $ args ):
308+ return array ("active " , null );
309+ case array_key_exists ('t ' , $ args ):
310+ return array ("term " , $ args ['t ' ]);
311+ case array_key_exists ('g ' , $ args ):
312+ //Guess current term
313+ //(s)pring is month <= 5, (f)all is month >= 8, s(u)mmer are months 6 and 7.
314+ //if ($month <= 5) {...} else if ($month >= 8) {...} else {...}
315+ $ month = intval (date ("m " , time ()));
316+ $ year = date ("y " , time ());
317+ return ($ month <= 5 ) ? array ("term " , "s {$ year }" ) : (($ month >= 8 ) ? array ("term " , "f {$ year }" ) : array ("term " , "u {$ year }" ));
318+ case array_key_exists ('r ' , $ args ):
319+ return array ("clean " , null );
183320 default :
184321 print self ::$ help_usage . PHP_EOL ;
185322 return false ;
@@ -200,7 +337,7 @@ private static function print_help() {
200337 //Arguments list
201338 print self ::$ help_args_list . PHP_EOL ;
202339 }
203- }
340+ } //END class parse_args
204341
205342/* EOF ====================================================================== */
206343?>
0 commit comments