summaryrefslogtreecommitdiffstats
path: root/ipa-server
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2007-11-20 22:45:29 -0500
committerRob Crittenden <rcritten@redhat.com>2007-11-20 22:45:29 -0500
commitf42f1f44c81e15ac9ecbc6684cbc4dfc9395fd42 (patch)
tree5e3907c33efe15f9a7f04bc973a341d0851b6dd4 /ipa-server
parent56d67b86e18112c9f059e7bcd3ac51fc21f941af (diff)
downloadfreeipa-f42f1f44c81e15ac9ecbc6684cbc4dfc9395fd42.tar.gz
freeipa-f42f1f44c81e15ac9ecbc6684cbc4dfc9395fd42.tar.xz
freeipa-f42f1f44c81e15ac9ecbc6684cbc4dfc9395fd42.zip
Enable group inactivation by using the Class of Service plugin.
This adds 2 new groups: activated and inactivated. If you, or a group you are a member of, is in inactivated then you are too. If you, or a group you are a member of, is in the activated group, then you are too. In a fight between activated and inactivated, activated wins. The DNs for doing this matching is case and white space sensitive. The goal is to never have to actually set nsAccountLock in a user directly but move them between these groups. We need to decide where in the CLI this will happen. Right it is split between ipa-deluser and ipa-usermod. To inactivate groups for now just add the group to inactivate or active.
Diffstat (limited to 'ipa-server')
-rw-r--r--ipa-server/ipa-gui/ipagui/forms/group.py4
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/group.py35
-rw-r--r--ipa-server/ipa-gui/ipagui/subcontrollers/user.py26
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupeditform.kid10
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/grouplist.kid12
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/groupshow.kid7
-rw-r--r--ipa-server/ipa-gui/ipagui/templates/usereditform.kid2
-rw-r--r--ipa-server/ipa-install/share/bootstrap-template.ldif39
-rw-r--r--ipa-server/ipaserver/dsinstance.py2
-rw-r--r--ipa-server/xmlrpc-server/funcs.py104
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py5
11 files changed, 214 insertions, 32 deletions
diff --git a/ipa-server/ipa-gui/ipagui/forms/group.py b/ipa-server/ipa-gui/ipagui/forms/group.py
index 04c0298ad..b67156641 100644
--- a/ipa-server/ipa-gui/ipagui/forms/group.py
+++ b/ipa-server/ipa-gui/ipagui/forms/group.py
@@ -9,6 +9,10 @@ class GroupFields():
editprotected_hidden = widgets.HiddenField(name="editprotected")
+ nsAccountLock = widgets.SingleSelectField(name="nsAccountLock",
+ label="Group Status",
+ options = [("", "active"), ("true", "inactive")])
+
group_orig = widgets.HiddenField(name="group_orig")
member_data = widgets.HiddenField(name="member_data")
dn_to_info_json = widgets.HiddenField(name="dn_to_info_json")
diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/group.py b/ipa-server/ipa-gui/ipagui/subcontrollers/group.py
index 7c05eebc0..dbcc77b9a 100644
--- a/ipa-server/ipa-gui/ipagui/subcontrollers/group.py
+++ b/ipa-server/ipa-gui/ipagui/subcontrollers/group.py
@@ -22,7 +22,7 @@ log = logging.getLogger(__name__)
group_new_form = ipagui.forms.group.GroupNewForm()
group_edit_form = ipagui.forms.group.GroupEditForm()
-group_fields = ['*']
+group_fields = ['*', 'nsAccountLock']
class GroupController(IPAController):
@@ -75,6 +75,9 @@ class GroupController(IPAController):
new_group.setValue('description', kw.get('description'))
rv = client.add_group(new_group)
+
+ if kw.get('nsAccountLock'):
+ client.mark_group_inactive(kw.get('cn'))
except ipaerror.exception_for(ipaerror.LDAP_DUPLICATE):
turbogears.flash("Group with name '%s' already exists" %
kw.get('cn'))
@@ -224,6 +227,12 @@ class GroupController(IPAController):
turbogears.flash("Edit group cancelled")
raise turbogears.redirect('/group/show', cn=cn[0])
+ if kw.get('editprotected') == '':
+ # if editprotected set these don't get sent in kw
+ orig_group_dict = loads(b64decode(kw.get('group_orig')))
+ kw['cn'] = orig_group_dict['cn']
+ kw['gidnumber'] = orig_group_dict['gidnumber']
+
# Decode the member data, in case we need to round trip
member_dicts = loads(b64decode(kw.get('member_data')))
@@ -251,6 +260,17 @@ class GroupController(IPAController):
if new_group.gidnumber != new_gid:
group_modified = True
new_group.setValue('gidnumber', new_gid)
+ else:
+ new_group.setValue('gidnumber', orig_group_dict.get('gidnumber'))
+ new_group.setValue('cn', orig_group_dict.get('cn'))
+ if new_group.cn != kw.get('cn'):
+ group_modified = True
+ new_group.setValue('cn', kw['cn'])
+
+ if group_modified:
+ rv = client.update_group(new_group)
+ #
+ # If the group update succeeds, but below operations fail, we
if new_group.cn != kw.get('cn'):
group_modified = True
new_group.setValue('cn', kw['cn'])
@@ -268,6 +288,17 @@ class GroupController(IPAController):
return dict(form=group_edit_form, group=kw, members=member_dicts,
tg_template='ipagui.templates.groupedit')
+ if kw.get('nsAccountLock') == '':
+ kw['nsAccountLock'] = "false"
+
+ modify_no_update = False
+ if kw.get('nsAccountLock') == "false" and new_group.getValues('nsaccountlock') == "true":
+ client.mark_group_active(kw.get('cn'))
+ modify_no_update = True
+ elif kw.get('nsAccountLock') == "true" and new_group.nsaccountlock != "true":
+ client.mark_group_inactive(kw.get('cn'))
+ modify_no_update = True
+
#
# Add members
#
@@ -326,7 +357,7 @@ class GroupController(IPAController):
cn0 = kw['cn'][0]
else:
cn0 = kw['cn']
- if group_modified == True:
+ if group_modified == True or modify_no_update == True:
turbogears.flash("%s updated!" % cn0)
else:
turbogears.flash("No modifications requested.")
diff --git a/ipa-server/ipa-gui/ipagui/subcontrollers/user.py b/ipa-server/ipa-gui/ipagui/subcontrollers/user.py
index 579379c43..39343b595 100644
--- a/ipa-server/ipa-gui/ipagui/subcontrollers/user.py
+++ b/ipa-server/ipa-gui/ipagui/subcontrollers/user.py
@@ -197,14 +197,14 @@ class UserController(IPAController):
new_user.setValue('carlicense', kw.get('carlicense'))
new_user.setValue('labeleduri', kw.get('labeleduri'))
- if kw.get('nsAccountLock'):
- new_user.setValue('nsAccountLock', 'true')
-
for custom_field in user_new_form.custom_fields:
new_user.setValue(custom_field.name,
kw.get(custom_field.name, ''))
rv = client.add_user(new_user)
+
+ if kw.get('nsAccountLock'):
+ client.mark_user_inactive(kw.get('uid'))
except ipaerror.exception_for(ipaerror.LDAP_DUPLICATE):
turbogears.flash("User with login '%s' already exists" %
kw.get('uid'))
@@ -482,12 +482,6 @@ class UserController(IPAController):
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
@@ -572,6 +566,20 @@ class UserController(IPAController):
user_groups=user_groups_dicts,
tg_template='ipagui.templates.useredit')
+ if kw.get('nsAccountLock') == '':
+ kw['nsAccountLock'] = "false"
+
+ try:
+ if kw.get('nsAccountLock') == "false" and new_user.getValues('nsaccountlock') == "true":
+ client.mark_user_active(kw.get('uid'))
+ elif kw.get('nsAccountLock') == "true" and new_user.nsaccountlock != "true":
+ client.mark_user_inactive(kw.get('uid'))
+ except ipaerror.IPAError, e:
+ turbogears.flash("User status change failed: " + str(e) + "<br/>" + e.detail[0]['desc'])
+ 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'])
diff --git a/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid b/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
index d46bc731e..6a5c5adb8 100644
--- a/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/groupeditform.kid
@@ -112,6 +112,16 @@ from ipagui.helpers import ipahelper
</script>
</td>
</tr>
+ <tr>
+ <th>
+ <label class="fieldlabel" for="${group_fields.nsAccountLock.field_id}" py:content="group_fields.nsAccountLock.label" />:
+ </th>
+ <td>
+ <span py:replace="group_fields.nsAccountLock.display(value_for(group_fields.nsAccountLock))" />
+ <span py:if="tg.errors.get('nsAccountLock')" class="fielderror"
+ py:content="tg.errors.get('nsAccountLock')" />
+ </td>
+ </tr>
</table>
<div>
diff --git a/ipa-server/ipa-gui/ipagui/templates/grouplist.kid b/ipa-server/ipa-gui/ipagui/templates/grouplist.kid
index 9f9bc4840..9489b3744 100644
--- a/ipa-server/ipa-gui/ipagui/templates/grouplist.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/grouplist.kid
@@ -20,7 +20,7 @@
</div>
<div py:if='(groups != None) and (len(groups) > 0)'>
<h2>${len(groups)} results returned:</h2>
- <table id="resultstable" class="details sortable resizable">
+ <table id="resultstable" class="details sortable resizable" cellspacing="0">
<thead>
<tr>
<th>
@@ -32,7 +32,15 @@
</tr>
</thead>
<tbody>
- <tr py:for="group in groups">
+ <tr py:for="group in groups" py:if="group.nsAccountLock != 'true'">
+ <td>
+ <a href="${tg.url('/group/show',cn=group.cn)}">${group.cn}</a>
+ </td>
+ <td>
+ ${group.description}
+ </td>
+ </tr>
+ <tr id="inactive" py:for="group in groups" py:if="group.nsAccountLock == 'true'">
<td>
<a href="${tg.url('/group/show',cn=group.cn)}">${group.cn}</a>
</td>
diff --git a/ipa-server/ipa-gui/ipagui/templates/groupshow.kid b/ipa-server/ipa-gui/ipagui/templates/groupshow.kid
index b2f37bf52..8713742d5 100644
--- a/ipa-server/ipa-gui/ipagui/templates/groupshow.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/groupshow.kid
@@ -11,6 +11,7 @@ cn = group.get('cn')
if isinstance(cn, list):
cn = cn[0]
edit_url = tg.url('/group/edit', cn=cn)
+from ipagui.helpers import userhelper
?>
<div id="details">
<h1>View Group</h1>
@@ -42,6 +43,12 @@ edit_url = tg.url('/group/edit', cn=cn)
</th>
<td>${group.get("gidnumber")}</td>
</tr>
+ <tr>
+ <th>
+ <label class="fieldlabel" py:content="fields.nsAccountLock.label" />:
+ </th>
+ <td>${userhelper.account_status_display(group.get("nsAccountLock"))}</td>
+ </tr>
</table>
<h2 class="formsection">Group Members</h2>
diff --git a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid
index c95b36e39..88b778d8c 100644
--- a/ipa-server/ipa-gui/ipagui/templates/usereditform.kid
+++ b/ipa-server/ipa-gui/ipagui/templates/usereditform.kid
@@ -787,7 +787,7 @@ from ipagui.helpers import ipahelper
group_dn = group.get('dn')
group_dn_esc = ipahelper.javascript_string_escape(group_dn)
- group_name = group.get('cn')[0]
+ group_name = group.get('cn')
group_descr = "[group]"
group_type = "group"
diff --git a/ipa-server/ipa-install/share/bootstrap-template.ldif b/ipa-server/ipa-install/share/bootstrap-template.ldif
index f443b6cb9..257f865be 100644
--- a/ipa-server/ipa-install/share/bootstrap-template.ldif
+++ b/ipa-server/ipa-install/share/bootstrap-template.ldif
@@ -116,3 +116,42 @@ ipaDefaultLoginShell: /bin/sh
ipaDefaultPrimaryGroup: ipausers
ipaMaxUsernameLength: 8
ipaPwdExpAdvNotify: 4
+
+dn: cn=account inactivation,cn=accounts,$SUFFIX
+description: Lock accounts based on group membership
+objectClass: top
+objectClass: ldapsubentry
+objectClass: cosSuperDefinition
+objectClass: cosClassicDefinition
+cosTemplateDn: cn=cosTemplates,cn=accounts,$SUFFIX
+cosAttribute: nsAccountLock operational
+cosAttribute: pager
+cosSpecifier: memberOf
+cn: Account Inactivation
+
+dn: cn=cosTemplates,cn=accounts,$SUFFIX
+objectclass: top
+objectclass: nsContainer
+cn: cosTemplates
+
+dn: cn="cn=inactivated,cn=account inactivation,cn=accounts,$SUFFIX", cn=cosTemplates,cn=accounts,$SUFFIX
+objectClass: top
+objectClass: cosTemplate
+objectClass: extensibleobject
+nsAccountLock: true
+cosPriority: 1
+
+dn: cn=inactivated,cn=account inactivation,cn=accounts,$SUFFIX
+objectclass: top
+objectclass: groupofuniquenames
+
+dn: cn="cn=activated,cn=account inactivation,cn=accounts,$SUFFIX", cn=cosTemplates,cn=accounts,$SUFFIX
+objectClass: top
+objectClass: cosTemplate
+objectClass: extensibleobject
+nsAccountLock: false
+cosPriority: 0
+
+dn: cn=Activated,cn=Account Inactivation,cn=accounts,$SUFFIX
+objectclass: top
+objectclass: groupofuniquenames
diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py
index d1e8f3abc..3cf80f46a 100644
--- a/ipa-server/ipaserver/dsinstance.py
+++ b/ipa-server/ipaserver/dsinstance.py
@@ -119,7 +119,7 @@ class DsInstance(service.Service):
def __setup_sub_dict(self):
server_root = find_server_root()
self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid,
- PASSWORD=self.dm_password, SUFFIX=self.suffix,
+ PASSWORD=self.dm_password, SUFFIX=self.suffix.lower(),
REALM=self.realm_name, USER=self.ds_user,
SERVER_ROOT=server_root)
diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index 85d22993a..5c9f0cf6b 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -36,6 +36,7 @@ import string
from types import *
import os
import re
+import logging
try:
from threading import Lock
@@ -49,6 +50,11 @@ ACIContainer = "cn=accounts"
DefaultUserContainer = "cn=users,cn=accounts"
DefaultGroupContainer = "cn=groups,cn=accounts"
+# FIXME: need to check the ipadebug option in ipa.conf
+logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s %(levelname)s %(message)s',
+ stream=sys.stderr)
+
#
# Apache runs in multi-process mode so each process will have its own
# connection. This could theoretically drive the total number of connections
@@ -674,26 +680,80 @@ class IPAServer:
else:
raise
- def mark_user_deleted (self, uid, opts=None):
- """Mark a user as inactive in LDAP. We aren't actually deleting
- users here, just making it so they can't log in, etc."""
- user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts)
+ def mark_entry_active (self, dn, opts=None):
+ """Mark an entry as active in LDAP."""
- # Are we doing an add or replace operation?
- if user.has_key('nsaccountlock'):
- if user['nsaccountlock'] == "true":
- return "already marked as deleted"
- has_key = True
- else:
- has_key = False
+ # This can be tricky. The entry itself can be marked inactive
+ # by being in the inactivated group. It can also be inactivated by
+ # being the member of an inactive group.
+ #
+ # First we try to remove the entry from the inactivated group. Then
+ # if it is still inactive we have to add it to the activated group
+ # which will override the group membership.
+
+ logging.debug("IPA: activating entry %s" % dn)
+
+ res = ""
+ # First, check the entry status
+ entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock'], opts)
+
+ if entry.get('nsaccountlock', 'false') == "false":
+ logging.debug("IPA: already active")
+ raise ipaerror.gen_exception(ipaerror.LDAP_EMPTY_MODLIST)
+
+ group = self.get_entry_by_cn("inactivated", None, opts)
+ res = self.remove_member_from_group(entry.get('dn'), group.get('dn'), opts)
+
+ # Now they aren't a member of inactivated directly, what is the status
+ # now?
+ entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock'], opts)
+
+ if entry.get('nsaccountlock', 'false') == "false":
+ # great, we're done
+ logging.debug("IPA: removing from inactivated did it.")
+ return res
+
+ # So still inactive, add them to activated
+ group = self.get_entry_by_cn("activated", None, opts)
+ res = self.add_member_to_group(dn, group.get('dn'), opts)
+ logging.debug("IPA: added to activated.")
- conn = self.getConnection(opts)
- try:
- res = conn.inactivateEntry(user['dn'], has_key)
- finally:
- self.releaseConnection(conn)
return res
+ def mark_entry_inactive (self, dn, opts=None):
+ """Mark an entry as inactive in LDAP."""
+
+ logging.debug("IPA: inactivating entry %s" % dn)
+
+ entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock', 'memberOf'], opts)
+
+ if entry.get('nsaccountlock', 'false') == "true":
+ logging.debug("IPA: already marked as inactive")
+ raise ipaerror.gen_exception(ipaerror.LDAP_EMPTY_MODLIST)
+
+ # First see if they are in the activated group as this will override
+ # the our inactivation.
+ group = self.get_entry_by_cn("activated", None, opts)
+ self.remove_member_from_group(dn, group.get('dn'), opts)
+
+ # Now add them to inactivated
+ group = self.get_entry_by_cn("inactivated", None, opts)
+ res = self.add_member_to_group(dn, group.get('dn'), opts)
+
+ return res
+
+ def mark_user_active(self, uid, opts=None):
+ """Mark a user as active"""
+
+ user = self.get_user_by_uid(uid, ['dn', 'uid'], opts)
+ return self.mark_entry_active(user.get('dn'))
+
+ def mark_user_inactive(self, uid, opts=None):
+ """Mark a user as inactive"""
+
+ user = self.get_user_by_uid(uid, ['dn', 'uid'], opts)
+ return self.mark_entry_inactive(user.get('dn'))
+
def delete_user (self, uid, opts=None):
"""Delete a user. Not to be confused with inactivate_user. This
makes the entry go away completely.
@@ -1215,6 +1275,18 @@ class IPAServer:
return entries
+ def mark_group_active(self, cn, opts=None):
+ """Mark a group as active"""
+
+ group = self.get_entry_by_cn(cn, ['dn', 'cn'], opts)
+ return self.mark_entry_active(group.get('dn'))
+
+ def mark_group_inactive(self, cn, opts=None):
+ """Mark a group as inactive"""
+
+ group = self.get_entry_by_cn(cn, ['dn', 'uid'], opts)
+ return self.mark_entry_inactive(group.get('dn'))
+
# Configuration support
def get_ipa_config(self, opts=None):
"""Retrieve the IPA configuration"""
diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py
index 23bdcec1e..789233c9f 100644
--- a/ipa-server/xmlrpc-server/ipaxmlrpc.py
+++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py
@@ -332,7 +332,10 @@ def handler(req, profiling=False):
h.register_function(f.find_users)
h.register_function(f.update_user)
h.register_function(f.delete_user)
- h.register_function(f.mark_user_deleted)
+ h.register_function(f.mark_user_active)
+ h.register_function(f.mark_user_inactive)
+ h.register_function(f.mark_group_active)
+ h.register_function(f.mark_group_inactive)
h.register_function(f.modifyPassword)
h.register_function(f.get_groups_by_member)
h.register_function(f.add_group)