summaryrefslogtreecommitdiffstats
path: root/ipa-server/ipa-gui/ipagui/subcontrollers/user.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipa-server/ipa-gui/ipagui/subcontrollers/user.py')
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/user.py537
1 files changed, 537 insertions, 0 deletions
diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/user.py b/ipa-server/ipa-gui/ipagui/subcontrollers/user.py
new file mode 100644
index 00000000..40d57510
--- /dev/null
+++ b/ipa-server/ipa-gui/ipagui/subcontrollers/user.py
@@ -0,0 +1,537 @@
+import os
+import re
+import random
+from pickle import dumps, loads
+from base64 import b64encode, b64decode
+
+import cherrypy
+import turbogears
+from turbogears import controllers, expose, flash
+from turbogears import validators, validate
+from turbogears import widgets, paginate
+from turbogears import error_handler
+from turbogears import identity
+
+from ipacontroller import IPAController
+import ipa.config
+import ipa.ipaclient
+import ipa.user
+from ipa.entity import utf8_encode_values
+from ipa import ipaerror
+import ipagui.forms.user
+
+ipa.config.init_config()
+client = ipa.ipaclient.IPAClient(True)
+
+password_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+user_new_form = ipagui.forms.user.UserNewForm()
+user_edit_form = ipagui.forms.user.UserEditForm()
+
+user_fields = ['*', 'nsAccountLock']
+
+class UserController(IPAController):
+
+ @expose()
+ def index(self):
+ raise turbogears.redirect("/user/list")
+
+ @expose("ipagui.templates.usernew")
+ @identity.require(identity.not_anonymous())
+ def new(self, tg_errors=None):
+ """Displays the new user form"""
+ if tg_errors:
+ turbogears.flash("There was a problem with the form!")
+
+ return dict(form=user_new_form, user={})
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def create(self, **kw):
+ """Creates a new user"""
+ self.restrict_post()
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ if kw.get('submit') == 'Cancel':
+ turbogears.flash("Add user cancelled")
+ raise turbogears.redirect('/user/list')
+
+ tg_errors, kw = self.usercreatevalidate(**kw)
+ if tg_errors:
+ return dict(form=user_new_form, user=kw,
+ tg_template='ipagui.templates.usernew')
+
+ #
+ # Create the user itself
+ #
+ try:
+ new_user = ipa.user.User()
+ new_user.setValue('title', kw.get('title'))
+ new_user.setValue('givenname', kw.get('givenname'))
+ new_user.setValue('sn', kw.get('sn'))
+ new_user.setValue('cn', kw.get('cn'))
+ new_user.setValue('displayname', kw.get('displayname'))
+ new_user.setValue('initials', kw.get('initials'))
+
+ new_user.setValue('uid', kw.get('uid'))
+ new_user.setValue('loginshell', kw.get('loginshell'))
+ new_user.setValue('gecos', kw.get('gecos'))
+
+ new_user.setValue('mail', kw.get('mail'))
+ new_user.setValue('telephonenumber', kw.get('telephonenumber'))
+ new_user.setValue('facsimiletelephonenumber',
+ kw.get('facsimiletelephonenumber'))
+ new_user.setValue('mobile', kw.get('mobile'))
+ new_user.setValue('pager', kw.get('pager'))
+ new_user.setValue('homephone', kw.get('homephone'))
+
+ new_user.setValue('street', kw.get('street'))
+ new_user.setValue('l', kw.get('l'))
+ new_user.setValue('st', kw.get('st'))
+ new_user.setValue('postalcode', kw.get('postalcode'))
+
+ new_user.setValue('ou', kw.get('ou'))
+ new_user.setValue('businesscategory', kw.get('businesscategory'))
+ new_user.setValue('description', kw.get('description'))
+ new_user.setValue('employeetype', kw.get('employeetype'))
+ # new_user.setValue('manager', kw.get('manager'))
+ new_user.setValue('roomnumber', kw.get('roomnumber'))
+ # new_user.setValue('secretary', kw.get('secretary'))
+
+ new_user.setValue('carlicense', kw.get('carlicense'))
+ new_user.setValue('labeleduri', kw.get('labeleduri'))
+
+ if kw.get('nsAccountLock'):
+ new_user.setValue('nsAccountLock', 'true')
+
+ rv = client.add_user(new_user)
+ except ipaerror.exception_for(ipaerror.LDAP_DUPLICATE):
+ turbogears.flash("Person with login '%s' already exists" %
+ kw.get('uid'))
+ return dict(form=user_new_form, user=kw,
+ tg_template='ipagui.templates.usernew')
+ except ipaerror.IPAError, e:
+ turbogears.flash("User add failed: " + str(e))
+ return dict(form=user_new_form, user=kw,
+ tg_template='ipagui.templates.usernew')
+
+ #
+ # NOTE: from here on, the user account now exists.
+ # on any error, we redirect to the _edit_ user page.
+ # this code does data setup, similar to useredit()
+ #
+ user = client.get_user_by_uid(kw['uid'], user_fields)
+ user_dict = user.toDict()
+
+ user_groups_dicts = []
+ user_groups_data = b64encode(dumps(user_groups_dicts))
+
+ # store a copy of the original user for the update later
+ user_data = b64encode(dumps(user_dict))
+ user_dict['user_orig'] = user_data
+ user_dict['user_groups_data'] = user_groups_data
+
+ # preserve group add info in case of errors
+ user_dict['dnadd'] = kw.get('dnadd')
+ user_dict['dn_to_info_json'] = kw.get('dn_to_info_json')
+
+ #
+ # Password change
+ # TODO
+ #
+
+ #
+ # Add groups
+ #
+ failed_adds = []
+ try:
+ dnadds = kw.get('dnadd')
+ if dnadds != None:
+ if not(isinstance(dnadds,list) or isinstance(dnadds,tuple)):
+ dnadds = [dnadds]
+ failed_adds = client.add_groups_to_user(
+ utf8_encode_values(dnadds), user.dn)
+ kw['dnadd'] = failed_adds
+ except ipaerror.IPAError, e:
+ failed_adds = dnadds
+
+ if len(failed_adds) > 0:
+ message = "Person successfully created.<br />"
+ message += "There was an error adding groups.<br />"
+ message += "Failures have been preserved in the add/remove lists."
+ turbogears.flash(message)
+ return dict(form=user_edit_form, user=user_dict,
+ user_groups=user_groups_dicts,
+ tg_template='ipagui.templates.useredit')
+
+ turbogears.flash("%s added!" % kw['uid'])
+ raise turbogears.redirect('/user/show', uid=kw['uid'])
+
+ @expose("ipagui.templates.dynamiceditsearch")
+ @identity.require(identity.not_anonymous())
+ def edit_search(self, **kw):
+ """Searches for groups and displays list of results in a table.
+ This method is used for the ajax search on the user edit page."""
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ groups = []
+ groups_counter = 0
+ searchlimit = 100
+ criteria = kw.get('criteria')
+ if criteria != None and len(criteria) > 0:
+ try:
+ groups = client.find_groups(criteria.encode('utf-8'), None,
+ searchlimit)
+ groups_counter = groups[0]
+ groups = groups[1:]
+ except ipaerror.IPAError, e:
+ turbogears.flash("search failed: " + str(e))
+
+ return dict(users=None, groups=groups, criteria=criteria,
+ counter=groups_counter)
+
+
+ @expose("ipagui.templates.useredit")
+ @identity.require(identity.not_anonymous())
+ def edit(self, uid, tg_errors=None):
+ """Displays the edit user form"""
+ if tg_errors:
+ turbogears.flash("There was a problem with the form!")
+
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ try:
+ user = client.get_user_by_uid(uid, user_fields)
+ user_dict = user.toDict()
+ # Edit shouldn't fill in the password field.
+ if user_dict.has_key('userpassword'):
+ del(user_dict['userpassword'])
+
+ user_groups = client.get_groups_by_member(user.dn, ['dn', 'cn'])
+ user_groups.sort(self.sort_by_cn)
+ user_groups_dicts = map(lambda group: group.toDict(), user_groups)
+ user_groups_data = b64encode(dumps(user_groups_dicts))
+
+ # store a copy of the original user for the update later
+ user_data = b64encode(dumps(user_dict))
+ user_dict['user_orig'] = user_data
+ user_dict['user_groups_data'] = user_groups_data
+
+ return dict(form=user_edit_form, user=user_dict,
+ user_groups=user_groups_dicts)
+ except ipaerror.IPAError, e:
+ turbogears.flash("User edit failed: " + str(e))
+ raise turbogears.redirect('/user/show', uid=kw.get('uid'))
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def update(self, **kw):
+ """Updates an existing user"""
+ self.restrict_post()
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ if kw.get('submit') == 'Cancel Edit':
+ turbogears.flash("Edit user cancelled")
+ raise turbogears.redirect('/user/show', uid=kw.get('uid'))
+
+ # Decode the group data, in case we need to round trip
+ user_groups_dicts = loads(b64decode(kw.get('user_groups_data')))
+
+ tg_errors, kw = self.userupdatevalidate(**kw)
+ if tg_errors:
+ return dict(form=user_edit_form, user=kw,
+ user_groups=user_groups_dicts,
+ tg_template='ipagui.templates.useredit')
+
+ password_change = False
+ user_modified = False
+
+ #
+ # Update the user itself
+ #
+ try:
+ orig_user_dict = loads(b64decode(kw.get('user_orig')))
+
+ new_user = ipa.user.User(orig_user_dict)
+ new_user.setValue('title', kw.get('title'))
+ new_user.setValue('givenname', kw.get('givenname'))
+ new_user.setValue('sn', kw.get('sn'))
+ new_user.setValue('cn', kw.get('cn'))
+ new_user.setValue('displayname', kw.get('displayname'))
+ new_user.setValue('initials', kw.get('initials'))
+
+ new_user.setValue('loginshell', kw.get('loginshell'))
+ new_user.setValue('gecos', kw.get('gecos'))
+
+ new_user.setValue('mail', kw.get('mail'))
+ new_user.setValue('telephonenumber', kw.get('telephonenumber'))
+ new_user.setValue('facsimiletelephonenumber',
+ kw.get('facsimiletelephonenumber'))
+ new_user.setValue('mobile', kw.get('mobile'))
+ new_user.setValue('pager', kw.get('pager'))
+ new_user.setValue('homephone', kw.get('homephone'))
+
+ new_user.setValue('street', kw.get('street'))
+ new_user.setValue('l', kw.get('l'))
+ new_user.setValue('st', kw.get('st'))
+ new_user.setValue('postalcode', kw.get('postalcode'))
+
+ new_user.setValue('ou', kw.get('ou'))
+ new_user.setValue('businesscategory', kw.get('businesscategory'))
+ new_user.setValue('description', kw.get('description'))
+ new_user.setValue('employeetype', kw.get('employeetype'))
+ # new_user.setValue('manager', kw.get('manager'))
+ new_user.setValue('roomnumber', kw.get('roomnumber'))
+ # new_user.setValue('secretary', kw.get('secretary'))
+
+ new_user.setValue('carlicense', kw.get('carlicense'))
+ new_user.setValue('labeleduri', kw.get('labeleduri'))
+
+
+ if kw.get('nsAccountLock'):
+ new_user.setValue('nsAccountLock', 'true')
+ else:
+ new_user.setValue('nsAccountLock', None)
+ if kw.get('editprotected') == 'true':
+ if kw.get('userpassword'):
+ password_change = True
+ new_user.setValue('uidnumber', str(kw.get('uidnumber')))
+ new_user.setValue('gidnumber', str(kw.get('gidnumber')))
+ new_user.setValue('homedirectory', str(kw.get('homedirectory')))
+
+ rv = client.update_user(new_user)
+ #
+ # If the user update succeeds, but below operations fail, we
+ # need to make sure a subsequent submit doesn't try to update
+ # the user again.
+ #
+ user_modified = True
+ kw['user_orig'] = b64encode(dumps(new_user.toDict()))
+ except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST), e:
+ # could be a password change
+ # could be groups change
+ # too much work to figure out unless someone really screams
+ pass
+ except ipaerror.IPAError, e:
+ turbogears.flash("User update failed: " + str(e))
+ return dict(form=user_edit_form, user=kw,
+ user_groups=user_groups_dicts,
+ tg_template='ipagui.templates.useredit')
+
+ #
+ # Password change
+ #
+ try:
+ if password_change:
+ rv = client.modifyPassword(kw['krbprincipalname'], "", kw.get('userpassword'))
+ except ipaerror.IPAError, e:
+ turbogears.flash("User password change failed: " + str(e))
+ return dict(form=user_edit_form, user=kw,
+ user_groups=user_groups_dicts,
+ tg_template='ipagui.templates.useredit')
+
+ #
+ # Add groups
+ #
+ failed_adds = []
+ try:
+ dnadds = kw.get('dnadd')
+ if dnadds != None:
+ if not(isinstance(dnadds,list) or isinstance(dnadds,tuple)):
+ dnadds = [dnadds]
+ failed_adds = client.add_groups_to_user(
+ utf8_encode_values(dnadds), new_user.dn)
+ kw['dnadd'] = failed_adds
+ except ipaerror.IPAError, e:
+ failed_adds = dnadds
+
+ #
+ # Remove groups
+ #
+ failed_dels = []
+ try:
+ dndels = kw.get('dndel')
+ if dndels != None:
+ if not(isinstance(dndels,list) or isinstance(dndels,tuple)):
+ dndels = [dndels]
+ failed_dels = client.remove_groups_from_user(
+ utf8_encode_values(dndels), new_user.dn)
+ kw['dndel'] = failed_dels
+ except ipaerror.IPAError, e:
+ failed_dels = dndels
+
+ if (len(failed_adds) > 0) or (len(failed_dels) > 0):
+ message = "There was an error updating groups.<br />"
+ message += "Failures have been preserved in the add/remove lists."
+ if user_modified:
+ message = "User Details successfully updated.<br />" + message
+ if password_change:
+ message = "User password successfully updated.<br />" + message
+ turbogears.flash(message)
+ return dict(form=user_edit_form, user=kw,
+ user_groups=user_groups_dicts,
+ tg_template='ipagui.templates.useredit')
+
+ turbogears.flash("%s updated!" % kw['uid'])
+ raise turbogears.redirect('/user/show', uid=kw['uid'])
+
+
+ @expose("ipagui.templates.userlist")
+ @identity.require(identity.not_anonymous())
+ def list(self, **kw):
+ """Searches for users and displays list of results"""
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ users = None
+ counter = 0
+ uid = kw.get('uid')
+ if uid != None and len(uid) > 0:
+ try:
+ users = client.find_users(uid.encode('utf-8'), None, 0, 2)
+ counter = users[0]
+ users = users[1:]
+ if counter == -1:
+ turbogears.flash("These results are truncated.<br />" +
+ "Please refine your search and try again.")
+ except ipaerror.IPAError, e:
+ turbogears.flash("User list failed: " + str(e))
+ raise turbogears.redirect("/user/list")
+
+ return dict(users=users, uid=uid, fields=ipagui.forms.user.UserFields())
+
+
+ @expose("ipagui.templates.usershow")
+ @identity.require(identity.not_anonymous())
+ def show(self, uid):
+ """Retrieve a single user for display"""
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ try:
+ user = client.get_user_by_uid(uid, user_fields)
+ user_groups = client.get_groups_by_member(user.dn, ['cn'])
+ user_groups.sort(self.sort_by_cn)
+ user_reports = client.get_users_by_manager(user.dn,
+ ['givenname', 'sn', 'uid'])
+ user_reports.sort(self.sort_group_member)
+
+ user_manager = None
+ try:
+ if user.manager:
+ user_manager = client.get_user_by_dn(user.manager,
+ ['givenname', 'sn', 'uid'])
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ pass
+
+ return dict(user=user.toDict(), fields=ipagui.forms.user.UserFields(),
+ user_groups=user_groups, user_reports=user_reports,
+ user_manager=user_manager)
+ except ipaerror.IPAError, e:
+ turbogears.flash("User show failed: " + str(e))
+ raise turbogears.redirect("/")
+
+ @validate(form=user_new_form)
+ @identity.require(identity.not_anonymous())
+ def usercreatevalidate(self, tg_errors=None, **kw):
+ return tg_errors, kw
+
+ @validate(form=user_edit_form)
+ @identity.require(identity.not_anonymous())
+ def userupdatevalidate(self, tg_errors=None, **kw):
+ return tg_errors, kw
+
+ # @expose()
+ def generate_password(self):
+ password = ""
+ generator = random.SystemRandom()
+ for char in range(8):
+ index = generator.randint(0, len(password_chars) - 1)
+ password += password_chars[index]
+
+ return password
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def suggest_uid(self, givenname, sn):
+ # filter illegal uid characters out
+ givenname = re.sub(r'[^a-zA-Z_\-0-9]', "", givenname)
+ sn = re.sub(r'[^a-zA-Z_\-0-9]', "", sn)
+
+ if (len(givenname) == 0) or (len(sn) == 0):
+ return ""
+
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ givenname = givenname.lower()
+ sn = sn.lower()
+
+ uid = givenname[0] + sn[:7]
+ try:
+ client.get_user_by_uid(uid)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return uid
+
+ uid = givenname[:7] + sn[0]
+ try:
+ client.get_user_by_uid(uid)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return uid
+
+ uid = (givenname + sn)[:8]
+ try:
+ client.get_user_by_uid(uid)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return uid
+
+ uid = sn[:8]
+ try:
+ client.get_user_by_uid(uid)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return uid
+
+ suffix = 2
+ template = givenname[0] + sn[:7]
+ while suffix < 20:
+ uid = template[:8 - len(str(suffix))] + str(suffix)
+ try:
+ client.get_user_by_uid(uid)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ return uid
+ suffix += 1
+
+ return ""
+
+ @expose()
+ @identity.require(identity.not_anonymous())
+ def suggest_email(self, givenname, sn):
+ # remove illegal email characters
+ givenname = re.sub(r'[^a-zA-Z0-9!#\$%\*/?\|\^\{\}`~&\'\+\-=_]', "", givenname)
+ sn = re.sub(r'[^a-zA-Z0-9!#\$%\*/?\|\^\{\}`~&\'\+\-=_]', "", sn)
+
+ if (len(givenname) == 0) or (len(sn) == 0):
+ return ""
+
+ client.set_krbccache(os.environ["KRB5CCNAME"])
+ givenname = givenname.lower()
+ sn = sn.lower()
+
+ # TODO - get from config
+ domain = "freeipa.org"
+
+ return "%s.%s@%s" % (givenname, sn, domain)
+
+
+ # TODO - mail is currently not indexed nor searchable.
+ # implement when it's done
+ # email = givenname + "." + sn + domain
+ # users = client.find_users(email, ['mail'])
+ # if len(filter(lambda u: u['mail'] == email, users[1:])) == 0:
+ # return email
+
+ # email = self.suggest_uid(givenname, sn) + domain
+ # users = client.find_users(email, ['mail'])
+ # if len(filter(lambda u: u['mail'] == email, users[1:])) == 0:
+ # return email
+
+ # suffix = 2
+ # template = givenname + "." + sn
+ # while suffix < 20:
+ # email = template + str(suffix) + domain
+ # users = client.find_users(email, ['mail'])
+ # if len(filter(lambda u: u['mail'] == email, users[1:])) == 0:
+ # return email
+ # suffix += 1
+
+ # return ""