Browse Source

WiP: fix linting errors (#14)

* fix linting errors

* add circle file

* update rrs to py3

* convert scripts to py3

* get rrs working

* fix shell reset
pull/21/head
Cian Butler 4 years ago
committed by GitHub
parent
commit
9e5af54ed9
23 changed files with 1699 additions and 1709 deletions
  1. +16
    -0
      .circleci/config.yml
  2. +7
    -0
      README.md
  3. +0
    -937
      rrs.py
  4. +8
    -7
      scripts/fixuidNumber.py
  5. +4
    -4
      scripts/fixup-rb-dcu-tree.py
  6. +3
    -3
      scripts/ldif_founder_soc_fix.py
  7. +31
    -30
      scripts/newyear_ldapmodify_ldif.py
  8. +3
    -3
      scripts/newyear_ldif.py
  9. +19
    -19
      scripts/rebuild_userdb_reserved.py
  10. +1
    -1
      scripts/remove_dcutree_ldif.py
  11. +9
    -9
      scripts/vote.py
  12. +0
    -0
      useradm/common.css
  13. +73
    -72
      useradm/rbaccount.py
  14. +41
    -42
      useradm/rbconfig.py
  15. +1
    -1
      useradm/rberror.py
  16. +1
    -1
      useradm/rbopt.py
  17. +73
    -75
      useradm/rbuser.py
  18. +351
    -417
      useradm/rbuserdb.py
  19. +1
    -1
      useradm/rrs.cgi
  20. +944
    -0
      useradm/rrs.py
  21. +22
    -0
      useradm/server.py
  22. +1
    -1
      useradm/useradm
  23. +90
    -86
      useradm/useradm.py

+ 16
- 0
.circleci/config.yml View File

@@ -0,0 +1,16 @@
version: 2
jobs:
build:
docker:
- image: python:3.5
working_directory: /home/ubuntu/useradm
steps:
- checkout
- run: pip install -r requirements.txt
- run: pip install pylint
- run: pylint useradm/*
- store_artifacts:
path: test-reports/coverage
destination: reports
- store_test_results:
path: "test-reports/"

+ 7
- 0
README.md View File

@@ -3,6 +3,13 @@

Useradm is used to manage Redbrick's membership.

## Testing RRS
To test rrs run
```
python server.py
```
Then open [localhost:8000/rrs.cgi](http://localhost:8000/rrs.cgi)

## Functions
### New User Creation



+ 0
- 937
rrs.py View File

@@ -1,937 +0,0 @@
#-----------------------------------------------------------------------------#
# MODULE DESCRIPTION #
#-----------------------------------------------------------------------------#

"""RedBrick Registration System CGI."""

# System modules

import atexit
import cgi
import cgitb
import os
import re
import sys
from xml.sax.saxutils import quoteattr

# RedBrick modules

from rbuserdb import *

#-----------------------------------------------------------------------------#
# DATA #
#-----------------------------------------------------------------------------#

__version__ = '$Revision: 1.6 $'
__author__ = 'Cillian Sharkey'

cmds = {
'card': 'Card reader interface',
'add': 'Add new user',
'delete': 'Delete user',
'renew': 'Renew user',
'update': 'Update user',
'rename': 'Rename user',
'convert': 'Convert user to new usertype',
'show': 'Show user information',
'freename': 'Check if a username is free',
'search': 'Search user and DCU databases',
'stats': 'Database statistics',
'log': 'Log of all actions'
}

cmds_list = ('card', 'add', 'delete', 'renew', 'update', 'rename', 'convert', 'show', 'freename', 'search', 'stats', 'log')

cmds_noform = {
'stats': 1,
'log': 1
}

cmds_custom = {
'show': 1,
'search': 1
}

fields = (
('updatedby', 'Updated By', ('card', 'add', 'delete', 'renew', 'update', 'rename', 'convert')),
('cardid', 'DCU card id', ('card',)),
('uid', 'Username', ('card', 'add', 'delete', 'renew', 'update', 'rename', 'convert', 'show', 'search')),
('newuid', 'New username', ('renew', 'rename', 'freename')),
('newbie', 'New user?', ('update',)),
('birthday', 'Birthday', ('add', 'renew', 'update')),
('id', 'DCU ID', ('add', 'renew', 'update', 'search')),
('usertype', 'Usertype', ('add', 'renew', 'convert')),
('cn', 'Name', ('add', 'renew', 'update', 'search')),
('altmail', 'Email', ('add', 'renew', 'update')),
('course', 'Course Code', ('add', 'renew', 'update')),
('year', 'Course Year', ('add', 'renew', 'update')),
('yearsPaid', 'Years Paid', ('add', 'renew', 'update')),
('setpasswd', 'Set new password?', ('renew',)),
('override', 'Override errors?', ('card', 'add', 'renew', 'update', 'rename')),
('dummyid', "Use 'dummy' ID?", ('card',)),
)

# Optional side note for form fields. For a particular mode only, use
# "fieldname.mode".
#
fields_note = {
'updatedby': 'your RedBrick username',
'birthday': 'DD-MM-YYYY',
'year': "'X' for exchange students",
'yearsPaid': '5 is only for associates',
'dummyid': 'New members only',
'uid.search': 'Search user database',
'id.search': 'Search user & DCU databases',
'cn.search': 'Search user & DCU databases'
}

# Fields that are simple yes/no choices. These are implemented using radio
# dialogs instead of a checkbox, as they allow neither yes nor no to be set
# (i.e. None).
#
fields_yesno = {
'setpasswd': 1,
'override': 1,
'dummyid': 1,
'newbie': 1
}

# HTML for custom form input fields.
#
fields_input = {
'cardid': 'class=fixed size=18 maxlength=16',
'cn': 'size=30',
'altmail': 'size=30',
'course': 'size=10 maxlength=50',
'year': 'size=10 maxlength=10'
}
# Global variables.
#
usr = RBUser()
opt = RBOpt()
udb = form = None # Initalised later in main()
okay = 0
start_done = end_done = 0
error_string = notice_string = okay_string = ''

#-----------------------------------------------------------------------------#
# MAIN #
#-----------------------------------------------------------------------------#

def main():
"""Program entry function."""

# XXX: Stupid Apache on shrapnel has TZ set to US/Eastern, no idea why!
os.environ['TZ'] = 'Eire'
print "Content-type: text/html"
print
atexit.register(shutdown)

# Sets up an exception handler for uncaught exceptions and saves
# traceback information locally.
#
cgitb.enable(logdir = '%s/tracebacks' % os.getcwd())

global form
form = cgi.FieldStorage()

opt.mode = form.getfirst('mode')
if not cmds.has_key(opt.mode):
opt.mode = 'card'
opt.action = form.getfirst('action')
#XXX remove usr.override
#usr.override = opt.override = form.getfirst('override') == '1'
opt.override = form.getfirst('override') == '1'

# Start HTML now only for modes that print output *before* html_form is
# called (which calls start_html itself). We delay the printing of the
# header for all other modes as mode switching may occur (e.g.
# cardid <-> add/renew).
#
if cmds_noform.has_key(opt.mode) or (cmds_custom.has_key(opt.mode) and opt.action):
html_start()

global udb
udb = RBUserDB()
udb.setopt(opt)
# Open database and call function for specific command only if action
# is required or the command needs no user input (i.e. no blank form
# stage).
#
if cmds_noform.has_key(opt.mode) or opt.action:
try:
udb.connect()
except ldap.LDAPError, e:
error(e, 'Could not connect to user database')
# not reached
try:
eval(opt.mode + '()')
except (ldap.LDAPError, RBError), e:
error(e)
# not reached
html_form()

sys.exit(0)

def shutdown():
"""Cleanup function registered with atexit."""

html_end()
if udb: udb.close()

def html_start():
"""Start HTML output."""

global start_done
if start_done:
return
start_done = 1

print \
"""<html>
<head>
<title>RedBrick Registration System v3.0 - %s</title>
<link rel="stylesheet" href="common.css" type="text/css">
<script language="JavaScript" type="text/javascript">
<!--
function page_load()
{
if (document.mainform)
f = document.mainform;
else
return;

if (f.updatedby && f.updatedby.value.length == 0)
f.updatedby.focus();
else if (f.cardid && f.cardid.value.length == 0)
f.cardid.focus();
else if (f.uid && f.uid.value.length == 0)
f.uid.focus();
else if (f.newuid && f.newuid.value.length == 0)
f.newuid.focus();
else if (f.cardid)
f.cardid.focus();
else if (f.uid)
f.uid.focus();
else if (f.newuid)
f.newuid.focus();
}

function radio_value(r)
{
for (var i = 0; i < r.length; i++)
if (r[i].checked == true)
return (r[i].value);
return (null);
}

function check_form(f)
{
if (f.updatedby && f.updatedby.value.length == 0) {
alert("updatedby must be given");
f.updatedby.focus();
return false;
}

return true;
}
// -->
</script>
</head>
<body text=black bgcolor=white onLoad="javascript:page_load()">

<div id=top>RedBrick Registration System v3.0</div>

<div id=menu>
<form name=menuform action='rrs.cgi' method=get>""" % opt.mode.capitalize()
if form.getfirst('updatedby'):
print "<input type=hidden name=updatedby value=%s>" % quoteattr(form.getfirst('updatedby') or '')

for i in cmds_list:
print "<input id=button type=submit name=mode value=%s> " % i
print \
"""</form>
</div>

<div id=top>%s</div>

<div id=main>
""" % cmds[opt.mode]

def html_form():
"""Output HTML form for current mode."""

global usr

html_start()
if notice_string or error_string or okay_string:
print "<table align=center id=msgs><tr><td>"
if error_string:
print "<span id=warn>%s</span>" % error_string.replace('\n', '<br>\n')
if notice_string:
print "<span id=notice>%s</span>" % notice_string.replace('\n', '<br>\n')
if okay_string:
print "<span id=okay>%s</span>" % okay_string.replace('\n', '<br>\n')
print "</td></tr></table>"
# Modes that never use a form or don't want a form when action has been
# requested and successful.
#
if cmds_noform.has_key(opt.mode) or (cmds_custom.has_key(opt.mode) and opt.action and okay):
return

if okay:
# Need a blank form, so create new user but keep updatedby set.
# Set override & setpassword options back to default (off).
#
usr = RBUser(updatedby = form.getfirst('updatedby'))
opt.override = 0
opt.setpasswd = 0
else:
# We want to preserve the form input so fill in as much data on
# the form as possible.
#
for k in form.keys():
if hasattr(usr, k) and getattr(usr, k) == None:
setattr(usr, k, form.getfirst(k))
print \
"""<form name=mainform onSubmit="javascript:return check_form(this)" action="rrs.cgi" method=get>
<input type=hidden name=mode value=%s>
<input type=hidden name=action value=1>""" % opt.mode

print '<table align=center class=main border=0 cellpadding=1 cellspacing=5>'

for field, desc, modes in fields:
if opt.mode not in modes:
# If updatedby isn't an actual visible field on the
# form, add it as a hidden field so its value gets
# passed on.
#
if field == 'updatedby' and form.getfirst('updatedby'):
print '<input type=hidden name=updatedby value=%s>' % quoteattr(form.getfirst('updatedby') or '')
else:
usrval = ''
if hasattr(usr, field) and getattr(usr, field) != None:
usrval = getattr(usr, field)
if field == 'override':
usrval = opt.override
elif field == 'cardid' and not usrval and usr.id:
usrval = usr.id

print '<tr>'
print ' <td class=side>%s</td>' % desc
print ' <td>',

if fields_input.has_key(field):
print '<input %s name=%s value=%s>' % (fields_input[field], field, quoteattr(str(usrval)))
elif fields_yesno.has_key(field):
print '<input name=%s type=radio value=1%s> Yes <input name=%s type=radio value=0%s> No' % (field, usrval == 1 and ' checked' or '', field, usrval == 0 and ' checked' or '')
elif field == 'usertype':
# Show default usertype of member if none set.
if not usr.usertype:
usr.usertype = 'member'

print '<select name=usertype>'
for i in rbconfig.usertypes_paying:
print '<option value=%s' % i,
if usr.usertype == i:
print ' selected',
print '>', i.capitalize()
print '</select>'
elif field == 'birthday':
if usr.birthday:
res = re.search(r'^(\d{4})-(\d{2})-(\d{2})', usr.birthday)
if res:
usr.bday = res.group(3)
usr.bmonth = res.group(2)
usr.byear = res.group(1)
print "<input size=2 maxlength=2 name=bday value='%s'>-<input size=2 maxlength=2 name=bmonth value='%s'>-<input size=4 maxlength=4 name=byear value='%s'>" % (usr.bday or '', usr.bmonth or '', usr.byear or '')
else:
print "<input class=fixed size=10 maxlength=8 name=%s value=%s" % (field, quoteattr(str(usrval))),
if field == 'uid' and usr.uid and opt.mode in ('renew', 'update'):
print ' readonly',
print '>'

print '</td>'
print ' <td><span id=note>',
if fields_note.has_key('%s.%s' % (field, opt.mode)):
print fields_note['%s.%s' % (field, opt.mode)],
elif fields_note.has_key(field):
print fields_note[field],
print '</span></td>'
print '</tr>'
print \
"""</table>
<p><input id=button type=submit value='%s &gt;&gt;'></p>
</form>""" % opt.mode.capitalize()

def html_end():
"""Finish HTML output."""

global end_done
if end_done:
return
end_done = 1

print \
"""</div>
</body>
</html>"""

#-----------------------------------------------------------------------------#
# MAIN FUNCTIONS #
#-----------------------------------------------------------------------------#

def card():
"""Process input from card reader form. Mode will be switched to add or
renew as appropriate if there were no problems with user input."""

get_updatedby(usr)
get_cardid(usr)
newmode = None

# We have an ID, is it a newbie or a renewal?
#
if usr.id != None:
try:
udb.check_user_byid(usr.id)
except RBError:
# Doesn't exist, must be new user.
newmode = 'add'
else:
# Exists, must be renewal.
newmode = 'renew'
elif form.getfirst('dummyid'):
get_dummyid(usr)
newmode = 'add'
elif form.getfirst('uid'):
usr.uid = form.getfirst('uid')
udb.check_username(usr.uid)
try:
udb.check_user_byname(usr.uid)
except RBError:
# Doesn't exist, must be new user.
newmode = 'add'
else:
# Exists, must be renewal.
newmode = 'renew'
else:
raise RBFatalError("DCU Card ID, username or dummy ID must be given")

if newmode == 'add':
if usr.id != None:
udb.get_userinfo_new(usr)
udb.get_userdefaults_new(usr)
elif newmode == 'renew':
curusr = RBUser()
udb.get_userinfo_renew(usr, curusr, override = 1)
udb.check_unpaid(curusr)
udb.get_userdefaults_renew(usr)
if newmode:
opt.mode = newmode

def add():
"""Add a new user."""

global okay, okay_string

get_updatedby(usr)
get_usertype(usr)
get_newusername(usr)
get_id(usr)

udb.get_userinfo_new(usr)
udb.get_userdefaults_new(usr)

get_name(usr)
get_email(usr)
get_course(usr)
get_year(usr)
get_years_paid(usr)
get_birthday(usr)

# Add user to database.
#
udb.add(usr)

# If we reached here, operation was successful, so show result of
# operation, log it and switch back to card mode.
#
okay = 1
okay_string += 'OKAY: User added: %s %s (%s)' % (usr.usertype, usr.uid, usr.cn)
rrs_log_add('add:%s:%s:%s:%s:%s:%s:%s:%s:%s' % (usr.uid, usr.usertype, usr.id != None and usr.id or '', usr.cn, usr.course or '', usr.year or '', usr.altmail, usr.birthday or '', usr.yearsPaid))
opt.mode = 'card'

def delete():
"""Delete user."""

global okay, okay_string

get_updatedby(usr)
get_username(usr)

udb.delete(usr)

okay = 1
okay_string += 'OKAY: User deleted: %s\n' % usr.uid
rrs_log_add('delete:%s' % (usr.uid))

def renew():
"""Renew user."""

global okay, okay_string

newusr = RBUser()
curusr = RBUser()

get_updatedby(usr)
get_username(usr)
get_newusername(newusr)

udb.get_userinfo_renew(usr, curusr)
udb.get_userdefaults_renew(usr)

get_setpasswd(usr)
get_usertype(usr)
get_id(usr)

udb.get_userinfo_renew(usr)

get_name(usr)
get_email(usr)
get_course(usr)
get_year(usr)
get_years_paid(usr)
get_birthday(usr)
udb.renew(usr)
okay_string += 'OKAY: User renewed: %s %s%s\n' % (usr.oldusertype, usr.uid, opt.setpasswd and ' [new password set]' or '')
rrs_log_add('renew:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s' % (usr.uid, newusr.uid or '', opt.setpasswd and 1 or 0, usr.usertype, usr.id != None and usr.id or '', usr.cn, usr.course or '', usr.year != None and usr.year or '', usr.altmail, usr.birthday or '', usr.yearsPaid))

# NOTE: We don't actually generate/set a password here, just flag it in
# the 'transaction log' so that sync_renew in useradm will set it
# instead.
# NOTE: If a renewal changed usertype, convert it and log it.
# sync_renew in useradm will detect the usertype change and convert the
# account.
#
if usr.oldusertype != usr.usertype:
udb.convert(curusr, usr)
okay_string += 'OKAY: User converted: %s -> %s\n' % (usr.uid, usr.usertype)
rrs_log_add('convert:%s:%s' % (usr.uid, usr.usertype))
# NOTE: If new username is given, rename database entry and log it.
# sync_rename in useradm will use this log entry to rename the account
# but only if it's a rename of an existing user only (i.e newbie is
# false).
#
if newusr.uid:
udb.rename(usr, newusr)
okay_string += 'OKAY: User renamed: %s -> %s\n' % (usr.uid, newusr.uid)
rrs_log_add('rename-%s:%s:%s' % (usr.newbie and 'new' or 'existing', usr.uid, newusr.uid))

okay = 1
opt.mode = 'card'

def update():
"""Update user."""

global okay, okay_string

get_updatedby(usr)
get_username(usr)
udb.get_user_byname(usr)
get_newbie(usr)
get_id(usr)
get_name(usr)
get_email(usr)
get_course(usr)
get_year(usr)
get_years_paid(usr)
get_birthday(usr)

udb.update(usr)

okay = 1
okay_string += 'OKAY: User updated: %s\n' % usr.uid
rrs_log_add('update:%s:%s:%s:%s:%s:%s:%s:%s:%s' % (usr.uid, usr.newbie and 1 or 0, usr.id != None and usr.id or '', usr.cn, usr.course or '', usr.year != None and usr.year or '', usr.altmail, usr.birthday or '', usr.yearsPaid))

def rename():
"""Rename user."""

global okay, okay_string

newusr = RBUser()
get_updatedby(usr)
get_username(usr)
udb.get_user_byname(usr)
get_newusername(newusr)

udb.rename(usr, newusr)

okay = 1
okay_string += 'OKAY: User renamed: %s -> %s\n' % (usr.uid, newusr.uid)
rrs_log_add('rename-%s:%s:%s' % (usr.newbie and 'new' or 'existing', usr.uid, newusr.uid))

def convert():
"""Convert user."""

global okay, okay_string
newusr = RBUser()
get_updatedby(usr)
get_username(usr)
get_usertype(newusr)

udb.convert(usr, newusr)

okay = 1
okay_string += 'OKAY: User converted: %s -> %s\n' % (usr.uid, newusr.usertype)
rrs_log_add('convert:%s:%s' % (usr.uid, newusr.usertype))

def show():
"""Show user's details."""
global okay

get_username(usr)
udb.get_user_byname(usr)
print '<pre>'
udb.show(usr)
print '</pre>'
okay = 1

def freename():
"""Check if a username is free."""
global okay_string
get_newusername(usr)
if usr.uid:
okay_string += "OKAY: Username '%s' is free.\n" % usr.uid

def search():
"""Search user and/or DCU databases."""
global okay

if form.getfirst('uid'):
uid = form.getfirst('uid')
res = udb.search_users_byusername(uid)
print "<p align=center>User database search for username '%s' - %d match%s</p>" % (uid, len(res), len(res) != 1 and 'es' or '')
show_search_results(res)
okay = 1
elif form.has_key('id') or form.has_key('cn'):
id = form.getfirst('id')
cn = form.getfirst('cn')
if id != None:
res = udb.search_users_byid(id)
print "<p align=center>User database search for ID '%s' - %d match%s</p>" % (id, len(res), len(res) != 1 and 'es' or '')
else:
res = udb.search_users_byname(cn)
print "<p align=center>User database search for name '%s' - %d match%s</p>" % (cn, len(res), len(res) != 1 and 'es' or '')

show_search_results(res)

if id != None:
res = udb.search_dcu_byid(id)
print "<p align=center>DCU database search for ID '%s' - %d match%s</p>" % (id, len(res), len(res) != 1 and 'es' or '')
else:
res = udb.search_dcu_byname(cn)
print "<p align=center>DCU database search for name '%s' - %d match%s</p>" % (cn, len(res), len(res) != 1 and 'es' or '')
show_search_results(res)
okay = 1
else:
raise RBFatalError('No search term given!')

def show_search_results(res):
"""Actual routine to display search results."""

if res:
print '<table align=center class=search>'
print '<tr><td></td><td class=top>Username</td><td class=top>Usertype</td><td class=top>Id</td><td class=top>Name</td><td class=top>Course</td><td class=top>Year</td><td class=top>Email</td></tr>'
for uid, usertype, id, cn, course, year, altmail in res:
print '<tr><td class=button>',
if uid:
print '<form action=rrs.cgi method=get><input type=hidden name=updatedby value=%s><input type=hidden name=uid value=%s><input type=hidden name=action value=1><input id=button type=submit name=mode value=show></form>' % (quoteattr(form.getfirst('updatedby') or ''), uid),
print '</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' % (uid or '-', usertype or '-', id or '-', cn, course or '-', year or '-', altmail)
print '</table>'

def stats():
"""Show database statistics."""

print "<pre>"
udb.stats()
print "</pre>"

def log():
"""Show contents of rrs log file."""
try:
fd = open('rrs.log', 'r')
except IOError, e:
error(e, 'Could not open rrs.log')
print '<pre>\n'
if os.path.getsize('rrs.log') == 0:
print 'Logfile is empty.'
else:
for line in fd.xreadlines():
print line,
print '</pre>'
fd.close()

#-----------------------------------------------------------------------------#
# GET USER DATA FUNCTIONS #
#-----------------------------------------------------------------------------#

def get_username(usr):
"""Get an existing username."""

if form.getfirst('uid'):
usr.uid = form.getfirst('uid')
else:
raise RBFatalError('Username must be given')

udb.check_username(usr.uid)
udb.check_user_byname(usr.uid)

def get_newusername(usr):
"""Get a new (free) username."""

if opt.mode == 'add':
if form.getfirst('uid'):
usr.uid = form.getfirst('uid')
else:
if form.getfirst('newuid'):
usr.uid = form.getfirst('newuid')

# New username is optional for renewals but compulsory for all other
# modes that require it (add, rename, freename).
#
if opt.mode == 'renew' and not usr.uid:
return

if not usr.uid:
raise RBFatalError('New username must be given')

try:
udb.check_username(usr.uid)
udb.check_userfree(usr.uid)
except RBWarningError, e:
error(e)

def get_cardid(usr):
"""Set usr.id to DCU ID number in cardid field.
The ID will either be the 8 digit number when entered manually or the
13 digit code produced by barcode and magnetic readers of the form
xxIDNUMBERnnn with possible start and/or end sentinel characters such
as ';' or '?'. Some readers will output a code number at the start,
(to indicate the type of barcode or something) so we assume the 13 digit
number is at the end (i.e. right-hand side) of the string.

If invalid input is given, raises RBFatalError.

NOTE: It is up to the caller to check if usr.id has been set,
get_cardid does not require it to be set.
"""

usr.id = form.getfirst('cardid')
if usr.id != None:
res = re.search(r'\d{2}(\d{8})\d{3}\D*$', usr.id)
if res:
usr.id = int(res.group(1))
return
res = re.search(r'^(\d{8})$', usr.id)
if res:
usr.id = int(usr.id)
return
raise RBFatalError('Invalid ID number/card reader input')

def get_updatedby(usr):
"""Get username of who is performing the action."""

if form.getfirst('updatedby'):
usr.updatedby = form.getfirst('updatedby')
else:
raise RBFatalError('Updated by must be given')
if usr.updatedby == 'root':
raise RBFatalError('root not allowed for updatedby')

udb.check_updatedby(usr.updatedby)

def get_usertype(usr):
"""Get usertype."""

usr.oldusertype = usr.usertype

if form.getfirst('usertype'):
usr.usertype = form.getfirst('usertype')
else:
raise RBFatalError('Usertype must be given')

udb.check_usertype(usr.usertype)

def get_id(usr):
"""Get DCU ID."""
if usr.usertype in rbconfig.usertypes_dcu:
if form.getfirst('id'):
usr.id = int(form.getfirst('id'))
else:
raise RBFatalError('ID must be given')

udb.check_id(usr)

def get_dummyid(usr):
"""Get 'dummy' DCU ID."""

if form.getfirst('dummyid'):
udb.get_dummyid(usr)
#XXX remove usr.override
#usr.override = opt.override = 1
opt.override = 1

def get_name(usr):
"""Get name."""

if form.getfirst('cn'):
usr.cn = form.getfirst('cn')
else:
raise RBFatalError('Name must be given')

udb.check_name(usr)

def get_years_paid(usr):
"""Get years paid."""

if not usr.usertype in rbconfig.usertypes_paying:
return
if form.getfirst('yearsPaid'):
usr.yearsPaid = int(form.getfirst('yearsPaid'))
else:
raise RBFatalError('Years paid must be given')

udb.check_years_paid(usr)

def get_course(usr):
"""Get DCU course."""

if not usr.usertype in ('member', 'committe'):
return
if form.getfirst('course'):
usr.course = form.getfirst('course')
else:
raise RBFatalError('Course must be given')

def get_year(usr):
"""Get DCU year."""

if not usr.usertype in ('member', 'committe'):
return
if form.getfirst('year'):
usr.year = form.getfirst('year')
else:
raise RBFatalError('Year must be given')

def get_email(usr):
"""Get alternative email address."""

if form.getfirst('altmail'):
usr.altmail = form.getfirst('altmail')
else:
raise RBFatalError('Email must be given')
try:
udb.check_email(usr)
except RBWarningError, e:
error(e)

def get_birthday(usr):
"""Get (optional) birthday."""

if form.getfirst('byear') or form.getfirst('bmonth') or form.getfirst('bday'):
if not (form.getfirst('byear') and form.getfirst('bmonth') and form.getfirst('bday')):
raise RBFatalError('Incomplete birthday given')
try:
usr.birthday = '%.4d-%0.2d-%0.2d' % (int(form.getfirst('byear')), int(form.getfirst('bmonth')), int(form.getfirst('bday')))
except ValueError:
raise RBFatalError('Invalid birthday given')
udb.check_birthday(usr)

def get_setpasswd(usr):
"""Get set new password boolean."""

if form.getfirst('setpasswd') != None:
opt.setpasswd = form.getfirst('setpasswd') == '1'

def get_newbie(usr):
"""Get newbie boolean."""

if form.getfirst('newbie') != None:
usr.newbie = form.getfirst('newbie') == '1'
#-----------------------------------------------------------------------------#
# LOGFILE HANDLING #
#-----------------------------------------------------------------------------#

def rrs_log_add(msg):
"""Add an entry for the current command to the logfile."""

if not msg:
msg = "%s:EMPTY MESSAGE" % opt.mode
msg = "%s:%s:%s" % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), usr.updatedby, msg)
try:
fd = open('rrs.log', 'a')
except IOError, e:
error('Could not write to rrs.log', e)
print >> fd, msg
fd.close()

#-----------------------------------------------------------------------------#
# ERROR HANDLING #
#-----------------------------------------------------------------------------#

def error(e, mesg = ''):
"""Handle (mainly) RBError exceptions."""

if not isinstance(e, RBError):
prefix = 'FATAL: %s' % (mesg and mesg + '\n' or '')
elif isinstance(e, RBWarningError) and opt.override:
prefix = 'IGNORED: '
else:
prefix = ''

global error_string
error_string += '%s%s\n' % (prefix, e)
if isinstance(e, RBWarningError) and opt.override:
return

# If we reach here the override option wasn't set, so all errors result
# in program exit.
#
html_form()
sys.exit(1)
#-----------------------------------------------------------------------------#
# If module is called as script, run main() #
#-----------------------------------------------------------------------------#

if __name__ == "__main__":
main()

+ 8
- 7
scripts/fixuidNumber.py View File

@@ -1,15 +1,16 @@
#! /usr/bin/env python
#! /usr/bin/env python3

from rbuserdb import *
import sys
from ..useradm.rbuserdb import RBUserDB
udb = RBUserDB()
udb.connect()
fd, n = udb.uidNumber_getnext()
for line in sys.stdin:
if line == 'uidNumber: -1\n':
print 'uidNumber:', n
n += 1
else:
print line,
if line == 'uidNumber: -1\n':
print('uidNumber:', n)
n += 1
else:
print(line, end=' ')

udb.uidNumber_savenext(fd, n)
udb.uidNumber_unlock(fd)

+ 4
- 4
scripts/fixup-rb-dcu-tree.py View File

@@ -7,8 +7,8 @@ re_dn = re.compile(r'^(dn: cn=.*?,).*(ou=.*?),o=DCU$')
for i in sys.stdin:
i = i.rstrip()
if i.startswith("dn:"):
print re.sub(re_dn, r'\1\2,ou=dcu,o=redbrick', i)
print "objectClass: top"
print "objectClass: dcuAccount"
print(re.sub(re_dn, r'\1\2,ou=dcu,o=redbrick', i))
print("objectClass: top")
print("objectClass: dcuAccount")
elif not i.startswith('objectClass:'):
print i
print(i)

+ 3
- 3
scripts/ldif_founder_soc_fix.py View File

@@ -3,8 +3,8 @@ import sys
for i in sys.stdin:
i = i.rstrip()
if i.startswith("yearsPaid:"):
print "yearsPaid:", int(i.split()[1]) + 9
print("yearsPaid:", int(i.split()[1]) + 9)
elif i.startswith("newbie:"):
print "newbie: FALSE"
print("newbie: FALSE")
else:
print i
print(i)

+ 31
- 30
scripts/newyear_ldapmodify_ldif.py View File

@@ -1,5 +1,6 @@
#!/usr/bin/python
import sys,string
#!/usr/bin/python3
import sys
import string

"""
-mak
@@ -10,40 +11,40 @@ To be used with the ldap modify query below
ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /etc/ldap.secret -f [LDIF_FROM_THIS_SCRIPT]
"""

yearsPaid = ''
years_paid = ''
uid = ''

#print modify ldif template
def modifyTemplate(uid,yearsPaid,newbie,reserved):
if uid != '' and yearsPaid != '' and reserved == False:
modTemp = "dn: uid="+uid.strip()+"\nchangetype: modify\nreplace: yearsPaid\nyearsPaid: "+yearsPaid.strip()+"\n"
if newbie == '1':
modTemp += "-\nreplace: newbie\nnewbie: FALSE\n\n"
else:
modTemp += "\n"
print modTemp
def modifyTemplate(uid, years_paid, newbie, reserved):
if uid != '' and years_paid != '' and reserved == False:
modTemp = "dn: uid="+uid.strip()+"\nchangetype: modify\nreplace: years_paid\nyears_paid: "+years_paid.strip()+"\n"
if newbie == '1':
modTemp += "-\nreplace: newbie\nnewbie: FALSE\n\n"
else:
modTemp += "\n"
print(modTemp)

#open ldif
with open(sys.argv[1], 'r') as content:
ldif = content.read()
#split by user
getdn = string.split(ldif, 'dn: uid=')
for i in range(1,len(getdn)):
thisdn = getdn[i].split('\n')
newbie = 'NONE'
reserved = False
#split by users variables
for j in range(0,len(thisdn)):
x = thisdn[j].rstrip()
uid = thisdn[0].rstrip()
if 'reserved' in uid:
reserved = True
try:
if x.startswith("yearsPaid:"):
yearsPaid = str(int(x.split()[1])).strip()
elif x.startswith("newbie:"):
newbie = '1'
else: continue
except IndexError:
break
modifyTemplate(uid,yearsPaid,newbie,reserved)
for i in range(1, len(getdn)):
thisdn = getdn[i].split('\n')
newbie = 'NONE'
reserved = False
#split by users variables
for j in range(0, len(thisdn)):
x = thisdn[j].rstrip()
uid = thisdn[0].rstrip()
if 'reserved' in uid:
reserved = True
try:
if x.startswith("years_paid:"):
years_paid = str(int(x.split()[1])).strip()
elif x.startswith("newbie:"):
newbie = '1'
else: continue
except IndexError:
break
modifyTemplate(uid, years_paid, newbie, reserved)

+ 3
- 3
scripts/newyear_ldif.py View File

@@ -3,8 +3,8 @@ import sys
for i in sys.stdin:
i = i.rstrip()
if i.startswith("yearsPaid:"):
print "yearsPaid:", int(i.split()[1]) - 1
print("yearsPaid:", int(i.split()[1]) - 1)
elif i.startswith("newbie:"):
print "newbie: FALSE"
print("newbie: FALSE")
else:
print i
print(i)

+ 19
- 19
scripts/rebuild_userdb_reserved.py View File

@@ -40,9 +40,9 @@ ldap_users = {}
def add_entry(name, desc):
"""Aggregate descriptions for multiple entries."""

if ldap_users.has_key(name):
if name in ldap_users:
return
if entries.has_key(name):
if name in entries:
entries[name] += ', ' + desc
else:
entries[name] = desc
@@ -62,11 +62,11 @@ def main():
udb.setopt(opt)

print 'userdb/reserved:',
print('userdb/reserved:', end=' ')

# Gather new entries.
#
print 'Gather',
print('Gather', end=' ')

# Get copy of all LDAP user, group and reserved entries in one go to
# speedup queries later on.
@@ -102,7 +102,7 @@ def main():
res = re_dns.search(line)
if res:
name = res.group(2).lower()
if dns_entries.has_key(name):
if name in dns_entries:
continue
dns_entries[name] = 1
add_entry(name, 'DNS entry')
@@ -120,7 +120,7 @@ def main():
if not res:
continue
for name in res.group(1).split():
if name and '.' not in name and len(name) <= rbconfig.maxlen_uname and not dns_entries.has_key(name):
if name and '.' not in name and len(name) <= rbconfig.maxlen_uname and name not in dns_entries:
dns_entries[name] = 1
add_entry(name, '%s Host entry' % host)

@@ -130,51 +130,51 @@ def main():
fd = open(file)
for line in fd.readlines():
grp = line.split(':')[0].lower()
if len(grp) <= rbconfig.maxlen_uname and not ldap_groups.has_key(grp):
if len(grp) <= rbconfig.maxlen_uname and grp not in ldap_groups:
add_entry(grp, '%s Unix group' % host)

print '[%d].' % len(entries.keys()),
print('[%d].' % len(list(entries.keys())), end=' ')

# Delete any dynamic entries in LDAP reserved tree that are not in the
# list we built i.e. unused.
#
print 'Purge',
print('Purge', end=' ')

purge_dn = []
res = udb.list_reserved_dynamic()
for uid in res:
if not entries.has_key(uid):
if uid not in entries:
purge_dn.append('uid=%s,%s' % (uid, rbconfig.ldap_reserved_tree))

for i in purge_dn:
if not opt.test:
udb.ldap.delete_s(i)
else:
print 'delete', i
print '[%d]' % len(purge_dn),
print('delete', i)
print('[%d]' % len(purge_dn), end=' ')

# Now add/update entries.
#
print 'Populate.',
print('Populate.', end=' ')

total_mods = total_adds = 0

for k, v in entries.items():
if ldap_reserveds.has_key(k):
if not ldap_reserveds_static.has_key(k) and v != ldap_reserveds[k]:
for k, v in list(entries.items()):
if k in ldap_reserveds:
if k not in ldap_reserveds_static and v != ldap_reserveds[k]:
if not opt.test:
udb.ldap.modify_s('uid=%s,%s' % (k, rbconfig.ldap_reserved_tree), ((ldap.MOD_REPLACE, 'description', v),))
else:
print 'modify %-8s [%s] [%s]' % (k, v, ldap_reserveds[k])
print('modify %-8s [%s] [%s]' % (k, v, ldap_reserveds[k]))
total_mods += 1
else:
if not opt.test:
udb.ldap.add_s('uid=%s,%s' % (k, rbconfig.ldap_reserved_tree), (('uid', k), ('description', v), ('objectClass', ('reserved', 'top'))))
else:
print 'add %-8s [%s]' % (k, v)
print('add %-8s [%s]' % (k, v))
total_adds += 1

print 'Done [%d adds, %d mods]' % (total_adds, total_mods)
print('Done [%d adds, %d mods]' % (total_adds, total_mods))

udb.close()



+ 1
- 1
scripts/remove_dcutree_ldif.py View File

@@ -7,5 +7,5 @@ for line in sys.stdin:
if line.startswith('dn:'):
dcu = line.find('ou=dcu') != -1
if not dcu:
print line,
print(line, end=' ')


+ 9
- 9
scripts/vote.py View File

@@ -34,16 +34,16 @@ def main():
usr = RBUser()
tmp = None
while not tmp:
tmp = raw_input("Please enter Username/Student ID/Student Card: ")
tmp = input("Please enter Username/Student ID/Student Card: ")
res = re.search(r'\D*\d{2}(\d{8})\d{3}\D*', tmp)
if res:
usr.id = int(res.group(1))
print 'CARD', usr.id
print('CARD', usr.id)
else:
res = re.search(r'^(\d{8})$', tmp)
if res:
usr.id = int(tmp)
print 'ID', usr.id
print('ID', usr.id)
try:
if usr.id:
udb.get_user_byid(usr)
@@ -54,21 +54,21 @@ def main():
udb.show(usr)
except RBError:

print 'NO SUCH USER YOU FUCKING DICKHEAD'
print('NO SUCH USER YOU FUCKING DICKHEAD')
else:
if voted.has_key(usr.uid):
print '\nGO FUCK YOUSELF YOU TWO-VOTING PRICK\n'
if usr.uid in voted:
print('\nGO FUCK YOUSELF YOU TWO-VOTING PRICK\n')
continue

if usr.usertype not in ('member', 'committe', 'staff'):
print '\nTELL THE COCKMUCH TO GET A REAL MEMBER ACCOUNT\n'
print('\nTELL THE COCKMUCH TO GET A REAL MEMBER ACCOUNT\n')
elif usr.yearsPaid <= 0:
print '\nTELL THE SCABBY BASTARD TO PISS OFF\n'
print('\nTELL THE SCABBY BASTARD TO PISS OFF\n')
else:
fd.write('%s\n' % usr.uid)
fd.flush()
voted[usr.uid] = 1
print '\nBIG VOTE GO NOW!\n'
print('\nBIG VOTE GO NOW!\n')

fd.close()
sys.exit(0)


docs/common.css → useradm/common.css View File


+ 73
- 72
useradm/rbaccount.py View File

@@ -5,11 +5,7 @@
"""RedBrick Account Module; contains RBAccount class."""

# System modules

import grp
import os
import pwd
import random
import re
import shutil
import sys
@@ -17,16 +13,15 @@ import sys
# RedBrick modules

import rbconfig
from rberror import *
from rbopt import *
from rbuser import *
from rberror import RBFatalError, RBWarningError
from rbopt import RBOpt

#-----------------------------------------------------------------------------#
# DATA #
#-----------------------------------------------------------------------------#

__version__ = '$Revision: 1.8 $'
__author__ = 'Cillian Sharkey'
__author__ = 'Cillian Sharkey'

#-----------------------------------------------------------------------------#
# CLASSES #
@@ -62,7 +57,8 @@ class RBAccount:
self.wrapper(os.symlink, webtree, os.path.join(usr.homeDirectory, 'public_html'))
#symlink vuln fix
try:
self.wrapper(os.chown, os.path.join(usr.homeDirectory, 'public_html'), usr.uidNumber, usr.gidNumber)
self.wrapper(os.chown, os.path.join(usr.homeDirectory, 'public_html'),
usr.uidNumber, usr.gidNumber)
except OSError:
pass

@@ -70,21 +66,23 @@ class RBAccount:
# alternate email address, but only if they're a dcu person and
# have an alternate email that's not a redbrick address.
#
if usr.usertype in rbconfig.usertypes_dcu and usr.altmail and not re.search(r'@.*redbrick\.dcu\.ie', usr.altmail):
if (usr.usertype in rbconfig.usertypes_dcu and usr.altmail and not
re.search(r'@.*redbrick\.dcu\.ie', usr.altmail)):
forward_file = os.path.join(usr.homeDirectory, '.forward')
fd = self.my_open(forward_file)
fd.write('%s\n' % usr.altmail)
self.my_close(fd)
forwards = self.my_open(forward_file)
forwards.write('%s\n' % usr.altmail)
self.my_close(forwards)
self.wrapper(os.chmod, forward_file, 0o600)

# Change user & group ownership recursively on home directory.
#
self.cmd('%s -Rh %s:%s %s' % (rbconfig.command_chown, usr.uidNumber, usr.usertype, self.shquote(usr.homeDirectory)))
self.cmd('%s -Rh %s:%s %s' % (rbconfig.command_chown, usr.uidNumber,
usr.usertype, self.shquote(usr.homeDirectory)))

# Set quotas for each filesystem.
#
for fs, (bqs, bqh, iqs, iqh) in list(rbconfig.gen_quotas(usr.usertype).items()):
self.quota_set(usr.uidNumber, fs, bqs, bqh, iqs, iqh)
for filesystem, (bqs, bqh, iqs, iqh) in list(rbconfig.gen_quotas(usr.usertype).items()):
self.quota_set(usr.uidNumber, filesystem, bqs, bqh, iqs, iqh)

# Add to redbrick announcement mailing lists.
#
@@ -96,8 +94,8 @@ class RBAccount:

# Zero out quotas.
#
for fs in list(rbconfig.gen_quotas().keys()):
self.quota_delete(usr.uidNumber, fs)
for user_key in list(rbconfig.gen_quotas().keys()):
self.quota_delete(usr.uidNumber, user_key)

# Remove home directory and webtree. Don't bomb out if the
# directories don't exist (i.e. ignore OSError).
@@ -113,8 +111,8 @@ class RBAccount:

# Remove from announce mailing lists.
#
self.list_delete('announce-redbrick', '%s@redbrick.dcu.ie' % usr.uid);
self.list_delete('redbrick-newsletter', '%s@redbrick.dcu.ie' % usr.uid);
self.list_delete('announce-redbrick', '%s@redbrick.dcu.ie' % usr.uid)
self.list_delete('redbrick-newsletter', '%s@redbrick.dcu.ie' % usr.uid)

for file in rbconfig.gen_extra_user_files(usr.uid):
try:
@@ -140,7 +138,8 @@ class RBAccount:
try:
self.wrapper(os.unlink, newusr.homeDirectory)
except OSError:
raise RBFatalError("New home directory '%s' already exists, could not unlink existing file." % newusr.homeDirectory)
raise RBFatalError(("New home directory '%s' already exists,"
" could not unlink existing file.") % newusr.homeDirectory)
else:
raise RBFatalError("New home directory '%s' already exists." % newusr.homeDirectory)

@@ -149,13 +148,13 @@ class RBAccount:

try:
self.wrapper(os.rename, oldusr.homeDirectory, newusr.homeDirectory)
except OSError as e:
raise RBFatalError("Could not rename home directory [%s]" % e)
except OSError as err:
raise RBFatalError("Could not rename home directory [%s]" % err)

try:
self.wrapper(os.rename, oldwebtree, newwebtree)
except OSError as e:
raise RBFatalError("Could not rename webtree directory [%s]" % e)
except OSError as err:
raise RBFatalError("Could not rename webtree directory [%s]" % err)

# Remove and then attempt to rename webtree symlink.
#
@@ -172,24 +171,24 @@ class RBAccount:
oldfiles = rbconfig.gen_extra_user_files(oldusr.uid)
newfiles = rbconfig.gen_extra_user_files(newusr.uid)

for i in range(len(oldfiles)):
for i, _ in enumerate(oldfiles):
oldf = oldfiles[i]
newf = newfiles[i]

try:
if os.path.isfile(oldf):
self.wrapper(os.rename, oldf, newf)
except OSError as e :
raise RBFatalError("Could not rename '%s' to '%s' [%s]" % (oldf, newf, e))
except OSError as err:
raise RBFatalError("Could not rename '%s' to '%s' [%s]" % (oldf, newf, err))

# XXX
# Rename their subscription to announce lists in case an email
# alias isn't put in for them or is later removed.
#
self.list_delete('announce-redbrick', "%s@redbrick.dcu.ie" % oldusr.uid);
self.list_delete('redbrick-newsletter', "%s@redbrick.dcu.ie" % oldusr.uid);
self.list_add('announce-redbrick', "%s@redbrick.dcu.ie" % newusr.uid);
self.list_add('redbrick-newsletter', "%s@redbrick.dcu.ie" % newusr.uid);
self.list_delete('announce-redbrick', "%s@redbrick.dcu.ie" % oldusr.uid)
self.list_delete('redbrick-newsletter', "%s@redbrick.dcu.ie" % oldusr.uid)
self.list_add('announce-redbrick', "%s@redbrick.dcu.ie" % newusr.uid)
self.list_add('redbrick-newsletter', "%s@redbrick.dcu.ie" % newusr.uid)

def convert(self, oldusr, newusr):
"""Convert account to a new usertype (Unix group)."""
@@ -217,7 +216,8 @@ class RBAccount:
try:
self.wrapper(os.unlink, newusr.homeDirectory)
except OSError:
raise RBFatalError("New home directory '%s' already exists, could not unlink existing file." % newusr.homeDirectory)
raise RBFatalError(("New home directory '%s' already exists, "
"could not unlink existing file.") % newusr.homeDirectory)
else:
raise RBFatalError("New home directory '%s' already exists." % newusr.homeDirectory)

@@ -233,7 +233,9 @@ class RBAccount:
# links themselves not the files they point to - very
# important!!
#
self.cmd("%s -Rh %s %s %s" % (rbconfig.command_chgrp, newusr.gidNumber, self.shquote(newusr.homeDirectory), self.shquote(rbconfig.gen_webtree(oldusr.uid))))
self.cmd("%s -Rh %s %s %s" % (rbconfig.command_chgrp, newusr.gidNumber,
self.shquote(newusr.homeDirectory),
self.shquote(rbconfig.gen_webtree(oldusr.uid))))

# Add/remove from committee mailing list as appropriate.
#
@@ -248,7 +250,7 @@ class RBAccount:
if newusr.usertype == 'admin':
self.list_add('rb-admins', "%s@redbrick.dcu.ie" % oldusr.uid)

def disuser(self, username, disuser_period = None):
def disuser(self, username, disuser_period=None):
"""Disable an account with optional automatic re-enabling after
given period."""

@@ -259,25 +261,25 @@ class RBAccount:

#TODO

def quota_set(self, username, fs, bqs, bqh, iqs, iqh):
def quota_set(self, username, filesystem, bqs, bqh, iqs, iqh):
"""Set given quota for given username on given filesystem.
Format for quota values is the same as that used for quotas
function in rbconfig module."""

self.cmd("%s -r %s %d %d %d %d %s" % (rbconfig.command_setquota, self.shquote(str(username)), bqs, bqh, iqs, iqh, fs))
#self.cmd("%s -b %d -B %d -i %d -I %d %s %s" % (rbconfig.command_setquota, bqs, bqh, iqs, iqh, fs, self.shquote(str(username))))
self.cmd("%s -r %s %d %d %d %d %s" % (
rbconfig.command_setquota, self.shquote(str(username)), bqs, bqh, iqs, iqh, filesystem))

def quota_delete(self, username, fs):
def quota_delete(self, username, filesystem):
"""Delete quota for given username on given filesystem."""

self.quota_set(username, fs, 0, 0, 0, 0)
#self.cmd('%s -d %s %s' % (rbconfig.command_setquota, fs, self.shquote(str(username))))
self.quota_set(username, filesystem, 0, 0, 0, 0)

#---------------------------------------------------------------------#
# SINGLE ACCOUNT INFORMATION METHODS #
#---------------------------------------------------------------------#

def show(self, usr):
@classmethod
def show(cls, usr):
"""Show account details on standard output."""

print("%13s:" % 'homedir mode', end=' ')
@@ -285,20 +287,23 @@ class RBAccount:
print('%04o' % (os.stat(usr.homeDirectory)[0] & 0o7777))
else:
print('Home directory does not exist')
print("%13s: %s" % ('logged in', os.path.exists(os.path.join(rbconfig.dir_signaway_state, usr.uid)) and 'true' or 'false'))
print("%13s: %s" % ('logged in', os.path.exists(os.path.join(
rbconfig.dir_signaway_state, usr.uid)) and 'true' or 'false'))

#---------------------------------------------------------------------#
# USER CHECKING AND INFORMATION RETRIEVAL METHODS #
#---------------------------------------------------------------------#

def check_accountfree(self, usr):
@classmethod
def check_accountfree(cls, usr):
"""Raise RBFatalError if given account name is not free i.e.
has a home directory."""

if os.path.exists(usr.homeDirectory):
raise RBFatalError("Account '%s' already exists (has a home directory)" % usr.uid)

def check_account_byname(self, usr):
@classmethod
def check_account_byname(cls, usr):
"""Raise RBFatalError if given account does not exist."""

if not os.path.exists(usr.homeDirectory):
@@ -308,23 +313,26 @@ class RBAccount:
# OTHER METHODS #
#---------------------------------------------------------------------#

def list_add(self, list, email):
def list_add(self, mail_list, email):
"""Add email address to mailing list."""

fd = self.my_popen("su -c '%s/bin/add_members -r - %s' list" % (rbconfig.dir_mailman, self.shquote(list)))
fd.write('%s\n' % email)
self.my_close(fd)
list_file = self.my_popen("su -c '%s/bin/add_members -r - %s' list" %
(rbconfig.dir_mailman, self.shquote(mail_list)))
list_file.write('%s\n' % email)
self.my_close(list_file)

def list_delete(self, list, email):
def list_delete(self, mail_list, email):
"""Delete email address from a mailing list."""

self.runcmd("su -c '%s/bin/remove_members %s %s' list" % (rbconfig.dir_mailman, self.shquote(list), self.shquote(email)))
self.runcmd("su -c '%s/bin/remove_members %s %s' list" %
(rbconfig.dir_mailman, self.shquote(mail_list), self.shquote(email)))

#--------------------------------------------------------------------#
# INTERNAL METHODS #
#--------------------------------------------------------------------#

def shquote(self, string):
@classmethod
def shquote(cls, string):
"""Return a quoted string suitable to use with shell safely."""

return "'" + string.replace("'", r"'\''") + "'"
@@ -333,16 +341,13 @@ class RBAccount:
"""runcmd(command) -> output, status

Run given command and return command output (stdout & stderr combined)
and exit status.

"""
and exit status."""

if self.opt.test:
print("TEST: runcmd:", cmd, file=sys.stderr)
return None, None
else:
fd = os.popen(cmd + ' 2>&1')
return fd.read(), fd.close()
cmd_run = os.popen(cmd + ' 2>&1')
return cmd_run.read(), cmd_run.close()

def cmd(self, cmd):
"""Run given command and raise a RBError exception returning
@@ -356,16 +361,14 @@ class RBAccount:
"""Wrapper method for executing other functions.

If test mode is set, print function name and arguments.
Otherwise call function with arguments.

"""
Otherwise call function with arguments."""

if self.opt.test:
sys.stderr.write("TEST: %s(" % function.__name__)
for i in keywords:
sys.stderr.write("%s, " % (i,))
for k, v in list(arguments.items()):
sys.stderr.write("%s = %s, " % (k, v))
for k, msg in list(arguments.items()):
sys.stderr.write("%s = %s, " % (k, msg))
sys.stderr.write(")\n")
else:
return function(*keywords, **arguments)
@@ -376,8 +379,7 @@ class RBAccount:
if self.opt.test:
print('TEST: open:', file, file=sys.stderr)
return sys.stderr
else:
return open(file, 'w')
return open(file, 'w')

def my_popen(self, cmd):
"""Return file descriptor to given command pipe for writing."""
@@ -385,27 +387,26 @@ class RBAccount:
if self.opt.test:
print('TEST: popen:', cmd, file=sys.stderr)
return sys.stderr
else:
return os.popen(cmd, 'w')
return os.popen(cmd, 'w')

def my_close(self, fd):
def my_close(self, open_file):
"""Close given pipe returned by _[p]open."""

if not self.opt.test:
fd.close()
open_file.close()

#--------------------------------------------------------------------#
# ERROR HANDLING #
#--------------------------------------------------------------------#

def rberror(self, e):
def rberror(self, err):
"""Handle errors."""

if self.opt.override and isinstance(e, RBWarningError):
if self.opt.override and isinstance(err, RBWarningError):
return

# If we reach here it's either a FATAL error or there was no
# override for a WARNING error, so raise it again to let the
# caller handle it.
#
raise e
raise err

+ 41
- 42
useradm/rbconfig.py View File

@@ -7,14 +7,13 @@
# System modules

import os
import random

#---------------------------------------------------------------------#
# DATA #
#---------------------------------------------------------------------#

__version__ = '$Revision: 1.11 $'
__author__ = 'Cillian Sharkey'
__author__ = 'Cillian Sharkey'

# Find out where the rrs directory is.

@@ -56,9 +55,9 @@ ldap_dcu_alumni_tree = 'OU=Alumni,DC=ad,DC=dcu,DC=ie'
# DNS zones RedBrick is authorative for.

dns_zones = (
'redbrick.dcu.ie',
'club.dcu.ie',
'soc.dcu.ie',
'redbrick.dcu.ie',
'club.dcu.ie',
'soc.dcu.ie',
)

# Mailman list suffixes.
@@ -87,21 +86,21 @@ shell_expired = '/usr/local/shells/expired'
# Unix group files: (group file, hostname) pairs.

files_group = (
('/etc/group', 'Deathray'),
('/local/share/var/carbon/group', 'Carbon')
('/etc/group', 'Deathray'),
('/local/share/var/carbon/group', 'Carbon')
)

# host files: (host file, hostname) pairs.

files_host = (
('/etc/hosts', 'Deathray'),
('/local/share/var/carbon/hosts', 'Carbon')
('/etc/hosts', 'Deathray'),
('/local/share/var/carbon/hosts', 'Carbon')
)

# Email alias files.

files_alias = (
('/etc/mail/exim_aliases.txt', 'Mail alias'),
('/etc/mail/exim_aliases.txt', 'Mail alias'),
)

# Commands.
@@ -115,26 +114,26 @@ command_sendmail = '/usr/sbin/sendmail'
# Valid account usertypes and descriptions.
#
usertypes = {
'founders': 'RedBrick founder',
'member': 'Normal member',
'associat': 'Graduate/associate member',
'staff': 'DCU staff member',
'society': 'DCU society',
'club': 'DCU club',
'projects': 'RedBrick/DCU/Course project account',
'guest': 'Guest account',
'intersoc': 'Account for society from another college',
'committe': 'Committee member or a position account',
'redbrick': 'RedBrick related account',
'dcu': 'DCU related account'
'founders': 'RedBrick founder',
'member': 'Normal member',
'associat': 'Graduate/associate member',
'staff': 'DCU staff member',
'society': 'DCU society',
'club': 'DCU club',
'projects': 'RedBrick/DCU/Course project account',
'guest': 'Guest account',
'intersoc': 'Account for society from another college',
'committe': 'Committee member or a position account',
'redbrick': 'RedBrick related account',
'dcu': 'DCU related account'
}

# "Ordered" list of usertypes for listing with the exception of founders.
#
usertypes_list = (
'member', 'associat', 'staff', 'committe',
'society', 'club', 'dcu',
'projects', 'redbrick', 'intersoc', 'guest'
'member', 'associat', 'staff', 'committe',
'society', 'club', 'dcu',
'projects', 'redbrick', 'intersoc', 'guest'
)

# List of paying usertypes.
@@ -148,9 +147,9 @@ usertypes_dcu = ('member', 'associat', 'staff', 'committe')
# Pseudo usertypes for conversion to committee positions.
#
convert_usertypes = {
'admin': 'Elected admin',
'webmaster': 'Elected webmaster',
'helpdesk': 'Elected helpdesk'
'admin': 'Elected admin',
'webmaster': 'Elected webmaster',
'helpdesk': 'Elected helpdesk'
}

# Supplementary groups when converting an account to given usertype.
@@ -158,9 +157,9 @@ convert_usertypes = {
# Format: 'usertype': 'a string of comma seperated groups with no spaces'
#
convert_extra_groups = {
'admin': 'root,log',
'webmaster': 'root,log,webgroup',
'helpdesk': 'helpdesk'
'admin': 'root,log',
'webmaster': 'root,log,webgroup',
'helpdesk': 'helpdesk'
}

# Actual primary group to use when converting an account to given usertype
@@ -169,9 +168,9 @@ convert_extra_groups = {
# Format: 'usertype': 'actual unix group name'
#
convert_primary_groups = {
'admin': 'committe',
'webmaster': 'committe',
'helpdesk': 'committe'
'admin': 'committe',
'webmaster': 'committe',
'helpdesk': 'committe'
}

#---------------------------------------------------------------------#
@@ -185,18 +184,18 @@ def gen_homedir(username, usertype):
"""Construct a user's home directory path given username and usertype."""

if usertype in ('member', 'associat'):
hash = username[0] + os.sep
letter = username[0] + os.sep
else:
hash = ''
letter = ''

return '%s/%s/%s%s' % (dir_home, usertype, hash, username)
return '%s/%s/%s%s' % (dir_home, usertype, letter, username)

def gen_webtree(username):
"""Generate a user's webtree path for given username."""

return os.path.join(dir_webtree, username[0], username)

def gen_quotas(usertype = None):
def gen_quotas():
"""Returns a dictionary of quota limits for filesystems (possibly
depending on the given usertype, if any).

@@ -211,7 +210,7 @@ def gen_quotas(usertype = None):
"""

return {
'/storage': (1000000, 1100000, 800000, 1000000)
'/storage': (1000000, 1100000, 800000, 1000000)
}

def gen_extra_user_files(username):
@@ -221,7 +220,7 @@ def gen_extra_user_files(username):
# XXX: need files for carbon now aswell.

return (
'%s/%s' % (dir_signaway_state, username),
'/var/mail/%s' % username,
'/var/spool/cron/crontabs/%s' % username
'%s/%s' % (dir_signaway_state, username),
'/var/mail/%s' % username,
'/var/spool/cron/crontabs/%s' % username
)

+ 1
- 1
useradm/rberror.py View File

@@ -20,7 +20,7 @@ class RBError(Exception):

def __init__(self, mesg):
"""Create new RBError object with given error message."""
super(RBError, self).__init__(mesg)
self.mesg = mesg

def __str__(self):


+ 1
- 1
useradm/rbopt.py View File

@@ -9,7 +9,7 @@
#-----------------------------------------------------------------------------#

__version__ = '$Revision: 1.5 $'
__author__ = 'Cillian Sharkey'
__author__ = 'Cillian Sharkey'

#-----------------------------------------------------------------------------#
# CLASSES #


+ 73
- 75
useradm/rbuser.py View File

@@ -9,7 +9,7 @@
#-----------------------------------------------------------------------------#

__version__ = '$Revision: 1.4 $'
__author__ = 'Cillian Sharkey'
__author__ = 'Cillian Sharkey'

#-----------------------------------------------------------------------------#
# CLASSES #
@@ -22,50 +22,50 @@ class RBUser:
# when displaying a user's information.
#
attr_list = (
# Attributes associated with user.
'uid', # Username
'usertype', # XXX NOT IN LDAP: contains primary
# usertype from objectClass list.
# Placed here so it's at start of
# output for user's information.
'objectClass', # List of classes.
'newbie', # New this year (boolean)
'cn', # Full name
'altmail', # Alternate email
'id', # DCU ID number (integer)
'course', # DCU course code
'year', # DCU course year number/code
'yearsPaid', # Number of years paid (integer)
'updatedby', # Username
'updated', # Timestamp
'createdby', # Username
'created', # Timestamp
'birthday', # Date
# Attributes associated with Unix account.
'uidNumber',
'gidNumber',
'gecos',
'loginShell',
'homeDirectory',
'userPassword', # Crypted password.
'host', # List of hosts.
'shadowLastChange' # Last time password was changed.
# Attributes associated with user.
'uid', # Username
'usertype', # XXX NOT IN LDAP: contains primary
# usertype from objectClass list.
# Placed here so it's at start of
# output for user's information.
'objectClass', # List of classes.
'newbie', # New this year (boolean)
'cn', # Full name
'altmail', # Alternate email
'id', # DCU ID number (integer)
'course', # DCU course code
'year', # DCU course year number/code
'yearsPaid', # Number of years paid (integer)
'updatedby', # Username
'updated', # Timestamp
'createdby', # Username
'created', # Timestamp
'birthday', # Date
# Attributes associated with Unix account.
'uidNumber',
'gidNumber',
'gecos',
'loginShell',
'homeDirectory',
'userPassword', # Crypted password.
'host', # List of hosts.
'shadowLastChange' # Last time password was changed.
)

# List of additional user attributes that are NOT in LDAP.
#
attr_misc_list = (
'passwd', # Plaintext password
'oldusertype', # Used when converting usertype?
'bday', # Birthday day
'bmonth', # Birthday month
'byear', # Birthday year
'disuser_period', # at(1) timespec
#XXX remove usr.override
#'override' # Boolean
'passwd', # Plaintext password
'oldusertype', # Used when converting usertype?
'bday', # Birthday day
'bmonth', # Birthday month
'byear', # Birthday year
'disuser_period', # at(1) timespec
#XXX remove usr.override
#'override' # Boolean
)

# Union of above lists.
@@ -73,43 +73,42 @@ class RBUser:
attr_list_all = attr_list + attr_misc_list

attr_list_info = (
# Attributes associated with user to be used for the useradm info command
'uid', # Username
'usertype', # XXX NOT IN LDAP: contains primary
# usertype from objectClass list.
# Placed here so it's at start of
# output for user's information.
'newbie', # New this year (boolean)
'cn', # Full name
'altmail', # Alternate email
'id', # DCU ID number (integer)
'course', # DCU course code
'year', # DCU course year number/code
'yearsPaid', # Number of years paid (integer)
'updatedby', # Username
'updated', # Timestamp
'createdby', # Username
'created', # Timestamp
'birthday', # Date
# Attributes associated with Unix account.
'gecos',
'loginShell',
'homeDirectory',
# Attributes associated with user to be used for the useradm info command
'uid', # Username
'usertype', # XXX NOT IN LDAP: contains primary
# usertype from objectClass list.
# Placed here so it's at start of
# output for user's information.