summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt34
-rw-r--r--VERSION4
-rw-r--r--install/share/70ipaotp.ldif2
-rw-r--r--install/share/default-aci.ldif11
-rw-r--r--install/updates/40-otp.update16
-rw-r--r--ipalib/plugins/otptoken.py38
-rw-r--r--ipalib/plugins/user.py9
7 files changed, 88 insertions, 26 deletions
diff --git a/API.txt b/API.txt
index 84769522b..30974d92e 100644
--- a/API.txt
+++ b/API.txt
@@ -2226,7 +2226,7 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: PrimaryKey('value', None, None)
command: otptoken_add
-args: 1,21,3
+args: 1,22,3
arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=False, primary_key=True, required=False)
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
@@ -2244,6 +2244,7 @@ option: Str('ipatokenserial', attribute=True, autofill=True, cli_name='serial',
option: Int('ipatokentotpclockoffset', attribute=True, autofill=True, cli_name='offset', default=0, multivalue=False, required=False)
option: Int('ipatokentotptimestep', attribute=True, autofill=True, cli_name='interval', default=30, minvalue=5, multivalue=False, required=False)
option: Str('ipatokenvendor', attribute=True, autofill=True, cli_name='vendor', default=u'FreeIPA', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Flag('qrcode?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('setattr*', cli_name='setattr', exclude='webui')
@@ -2252,6 +2253,17 @@ option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: PrimaryKey('value', None, None)
+command: otptoken_add_managedby
+args: 1,5,3
+arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
command: otptoken_del
args: 1,2,3
arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=True, primary_key=True, query=True, required=True)
@@ -2261,7 +2273,7 @@ output: Output('result', <type 'dict'>, None)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: ListOfPrimaryKeys('value', None, None)
command: otptoken_find
-args: 1,21,4
+args: 1,22,4
arg: Str('criteria?', noextrawhitespace=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
@@ -2278,6 +2290,7 @@ option: Int('ipatokentotpclockoffset', attribute=True, autofill=False, cli_name=
option: Int('ipatokentotptimestep', attribute=True, autofill=False, cli_name='interval', default=30, minvalue=5, multivalue=False, query=True, required=False)
option: Str('ipatokenuniqueid', attribute=True, autofill=False, cli_name='id', multivalue=False, primary_key=True, query=True, required=False)
option: Str('ipatokenvendor', attribute=True, autofill=False, cli_name='vendor', default=u'FreeIPA', multivalue=False, query=True, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Int('sizelimit?', autofill=False, minvalue=0)
@@ -2289,7 +2302,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('truncated', <type 'bool'>, None)
command: otptoken_mod
-args: 1,16,3
+args: 1,17,3
arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=False, primary_key=True, query=True, required=True)
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
@@ -2302,6 +2315,7 @@ option: Str('ipatokennotbefore', attribute=True, autofill=False, cli_name='not_b
option: Str('ipatokenowner', attribute=True, autofill=False, cli_name='owner', multivalue=False, required=False)
option: Str('ipatokenserial', attribute=True, autofill=False, cli_name='serial', multivalue=False, required=False)
option: Str('ipatokenvendor', attribute=True, autofill=False, cli_name='vendor', default=u'FreeIPA', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('rename', cli_name='rename', multivalue=False, primary_key=True, required=False)
option: Flag('rights', autofill=True, default=False)
@@ -2310,10 +2324,22 @@ option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: PrimaryKey('value', None, None)
+command: otptoken_remove_managedby
+args: 1,5,3
+arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
command: otptoken_show
-args: 1,4,3
+args: 1,5,3
arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=False, primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Flag('rights', autofill=True, default=False)
option: Str('version?', exclude='webui')
diff --git a/VERSION b/VERSION
index 97b8f6fae..318b88a15 100644
--- a/VERSION
+++ b/VERSION
@@ -89,5 +89,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=88
-# Last change: mbasti - Added 'dns_name_values' capability
+IPA_API_VERSION_MINOR=89
+# Last change: npmccallum - Add support for managedBy to tokens
diff --git a/install/share/70ipaotp.ldif b/install/share/70ipaotp.ldif
index a40ad9ee0..0b9815704 100644
--- a/install/share/70ipaotp.ldif
+++ b/install/share/70ipaotp.ldif
@@ -23,7 +23,7 @@ attributeTypes: (2.16.840.1.113730.3.8.16.1.18 NAME 'ipatokenRadiusTimeout' DESC
attributeTypes: (2.16.840.1.113730.3.8.16.1.19 NAME 'ipatokenRadiusRetries' DESC 'Number of allowed Retries' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
attributeTypes: (2.16.840.1.113730.3.8.16.1.20 NAME 'ipatokenUserMapAttribute' DESC 'Attribute to map from the user entry for RADIUS server authentication' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA OTP')
attributeTypes: (2.16.840.1.113730.3.8.16.1.21 NAME 'ipatokenHOTPcounter' DESC 'HOTP counter' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP')
-objectClasses: (2.16.840.1.113730.3.8.16.2.1 NAME 'ipaToken' SUP top ABSTRACT DESC 'Abstract token class for tokens' MUST (ipatokenUniqueID) MAY (description $ ipatokenOwner $ ipatokenDisabled $ ipatokenNotBefore $ ipatokenNotAfter $ ipatokenVendor $ ipatokenModel $ ipatokenSerial) X-ORIGIN 'IPA OTP')
+objectClasses: (2.16.840.1.113730.3.8.16.2.1 NAME 'ipaToken' SUP top ABSTRACT DESC 'Abstract token class for tokens' MUST (ipatokenUniqueID) MAY (description $ managedBy $ ipatokenOwner $ ipatokenDisabled $ ipatokenNotBefore $ ipatokenNotAfter $ ipatokenVendor $ ipatokenModel $ ipatokenSerial) X-ORIGIN 'IPA OTP')
objectClasses: (2.16.840.1.113730.3.8.16.2.2 NAME 'ipatokenTOTP' SUP ipaToken STRUCTURAL DESC 'TOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenTOTPclockOffset $ ipatokenTOTPtimeStep) X-ORIGIN 'IPA OTP')
objectClasses: (2.16.840.1.113730.3.8.16.2.3 NAME 'ipatokenRadiusProxyUser' SUP top AUXILIARY DESC 'Radius Proxy User' MAY (ipatokenRadiusConfigLink $ ipatokenRadiusUserName) X-ORIGIN 'IPA OTP')
objectClasses: (2.16.840.1.113730.3.8.16.2.4 NAME 'ipatokenRadiusConfiguration' SUP top STRUCTURAL DESC 'Proxy Radius Configuration' MUST (cn $ ipatokenRadiusServer $ ipatokenRadiusSecret) MAY (description $ ipatokenRadiusTimeout $ ipatokenRadiusRetries $ ipatokenUserMapAttribute) X-ORIGIN 'IPA OTP')
diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 04fc185f7..a4a5d9954 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -84,8 +84,9 @@ aci: (target="ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX")(targetattr="use
dn: $SUFFIX
changetype: modify
add: aci
-aci: (targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)
-aci: (targetfilter = "(objectClass=ipaToken)")(targetattrs = "ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can write basic token info"; allow (write) userattr = "ipatokenOwner#USERDN";)
-aci: (targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPkey || ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPclockOffset || ipatokenTOTPtimeStep")(version 3.0; acl "Users can add TOTP token secrets"; allow (write, search) userattr = "ipatokenOwner#USERDN";)
-aci: (target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX")(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Users can create and delete tokens"; allow (add, delete) userattr = "ipatokenOwner#SELFDN";)
-aci: (targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPkey || ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenHOTPcounter")(version 3.0; acl "Users can add HOTP token secrets"; allow (write, search) userattr = "ipatokenOwner#USERDN";)
+aci: (targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || description || managedBy || ipatokenUniqueID || ipatokenDisabled || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial || ipatokenOwner")(version 3.0; acl "Users/managers can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN" or userattr = "managedBy#USERDN";)
+aci: (targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPtimeStep")(version 3.0; acl "Users/managers can see TOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN" or userattr = "managedBy#USERDN";)
+aci: (targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits")(version 3.0; acl "Users/managers can see HOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN" or userattr = "managedBy#USERDN";)
+aci: (targetfilter = "(objectClass=ipaToken)")(targetattrs = "description || ipatokenDisabled || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Managers can write basic token info"; allow (write) userattr = "managedBy#USERDN";)
+aci: (targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Managers can delete tokens"; allow (delete) userattr = "managedBy#USERDN";)
+aci: (target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX")(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Users can create self-managed tokens"; allow (add) userattr = "ipatokenOwner#SELFDN" and userattr = "managedBy#SELFDN";)
diff --git a/install/updates/40-otp.update b/install/updates/40-otp.update
index ba9be5f05..caa21c380 100644
--- a/install/updates/40-otp.update
+++ b/install/updates/40-otp.update
@@ -4,11 +4,17 @@ default: objectClass: top
default: cn: otp
dn: $SUFFIX
-add: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)'
-add: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can write basic token info"; allow (write) userattr = "ipatokenOwner#USERDN";)'
-add: aci:'(targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPkey || ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPclockOffset || ipatokenTOTPtimeStep")(version 3.0; acl "Users can add TOTP token secrets"; allow (write, search) userattr = "ipatokenOwner#USERDN";)'
-add: aci:'(target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX")(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Users can create and delete tokens"; allow (add, delete) userattr = "ipatokenOwner#SELFDN";)'
-add: aci:'(targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPkey || ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenHOTPcounter")(version 3.0; acl "Users can add HOTP token secrets"; allow (write, search) userattr = "ipatokenOwner#USERDN";)'
+remove: aci:'(target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX")(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Users can create and delete tokens"; allow (add, delete) userattr = "ipatokenOwner#SELFDN";)'
+remove: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)'
+remove: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can write basic token info"; allow (write) userattr = "ipatokenOwner#USERDN";)'
+remove: aci:'(targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPkey || ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPclockOffset || ipatokenTOTPtimeStep")(version 3.0; acl "Users can add TOTP token secrets"; allow (write, search) userattr = "ipatokenOwner#USERDN";)'
+remove: aci:'(targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPkey || ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenHOTPcounter")(version 3.0; acl "Users can add HOTP token secrets"; allow (write, search) userattr = "ipatokenOwner#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || description || managedBy || ipatokenUniqueID || ipatokenDisabled || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial || ipatokenOwner")(version 3.0; acl "Users/managers can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN" or userattr = "managedBy#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPtimeStep")(version 3.0; acl "Users/managers can see TOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN" or userattr = "managedBy#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits")(version 3.0; acl "Users/managers can see HOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN" or userattr = "managedBy#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "description || ipatokenDisabled || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Managers can write basic token info"; allow (write) userattr = "managedBy#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Managers can delete tokens"; allow (delete) userattr = "managedBy#USERDN";)'
+add: aci:'(target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX")(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Users can create self-managed tokens"; allow (add) userattr = "ipatokenOwner#SELFDN" and userattr = "managedBy#SELFDN";)'
dn: cn=radiusproxy,$SUFFIX
default: objectClass: nsContainer
diff --git a/ipalib/plugins/otptoken.py b/ipalib/plugins/otptoken.py
index 17644e0f1..9c7bd412b 100644
--- a/ipalib/plugins/otptoken.py
+++ b/ipalib/plugins/otptoken.py
@@ -17,7 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from ipalib.plugins.baseldap import DN, LDAPObject, LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, LDAPRetrieve
+from ipalib.plugins.baseldap import DN, LDAPObject, LDAPAddMember, LDAPRemoveMember
+from ipalib.plugins.baseldap import LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, LDAPRetrieve
from ipalib import api, Int, Str, Bool, Flag, Bytes, IntEnum, StrEnum, _, ngettext
from ipalib.plugable import Registry
from ipalib.errors import PasswordMismatch, ConversionError, LastMemberError, NotFound
@@ -109,8 +110,14 @@ class otptoken(LDAPObject):
default_attributes = [
'ipatokenuniqueid', 'description', 'ipatokenowner',
'ipatokendisabled', 'ipatokennotbefore', 'ipatokennotafter',
- 'ipatokenvendor', 'ipatokenmodel', 'ipatokenserial'
+ 'ipatokenvendor', 'ipatokenmodel', 'ipatokenserial', 'managedby'
]
+ attribute_members = {
+ 'managedby': ['user'],
+ }
+ relationships = {
+ 'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
+ }
rdn_is_primary_key = True
label = _('OTP Tokens')
@@ -138,6 +145,10 @@ class otptoken(LDAPObject):
cli_name='owner',
label=_('Owner'),
),
+ Str('managedby_user?',
+ label=_('Manager'),
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
Bool('ipatokendisabled?',
cli_name='disabled',
label=_('Disabled state')
@@ -245,11 +256,14 @@ class otptoken_add(LDAPCreate):
del entry_attrs[tattr]
# If owner was not specified, default to the person adding this token.
- if 'ipatokenowner' not in entry_attrs:
+ # If managedby was not specified, attempt a sensible default.
+ if 'ipatokenowner' not in entry_attrs or 'managedby' not in entry_attrs:
result = self.api.Command.user_find(whoami=True)['result']
if result:
cur_uid = result[0]['uid'][0]
- entry_attrs.setdefault('ipatokenowner', cur_uid)
+ prev_uid = entry_attrs.setdefault('ipatokenowner', cur_uid)
+ if cur_uid == prev_uid:
+ entry_attrs.setdefault('managedby', result[0]['dn'])
# Resolve the owner's dn
_normalize_owner(self.api.Object.user, entry_attrs)
@@ -326,9 +340,7 @@ class otptoken_mod(LDAPUpdate):
@register()
class otptoken_find(LDAPSearch):
__doc__ = _('Search for OTP token.')
- msg_summary = ngettext(
- '%(count)d OTP token matched', '%(count)d OTP tokens matched', 0
- )
+ msg_summary = ngettext('%(count)d OTP token matched', '%(count)d OTP tokens matched', 0)
def pre_callback(self, ldap, filters, *args, **kwargs):
# This is a hack, but there is no other way to
@@ -359,3 +371,15 @@ class otptoken_show(LDAPRetrieve):
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
_convert_owner(self.api.Object.user, entry_attrs, options)
return super(otptoken_show, self).post_callback(ldap, dn, entry_attrs, *keys, **options)
+
+@register()
+class otptoken_add_managedby(LDAPAddMember):
+ __doc__ = _('Add users that can manage this token.')
+
+ member_attributes = ['managedby']
+
+@register()
+class otptoken_remove_managedby(LDAPRemoveMember):
+ __doc__ = _('Remove hosts that can manage this host.')
+
+ member_attributes = ['managedby']
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index a1b0643a3..2f700b60f 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -855,12 +855,17 @@ class user_del(LDAPDelete):
assert isinstance(dn, DN)
check_protected_member(keys[-1])
- # Delete all tokens owned by this user
+ # Delete all tokens owned and managed by this user.
+ # Orphan all tokens owned but not managed by this user.
owner = self.api.Object.user.get_primary_key_from_dn(dn)
results = self.api.Command.otptoken_find(ipatokenowner=owner)['result']
for token in results:
+ orphan = not [x for x in token.get('managedby_user', []) if x == owner]
token = self.api.Object.otptoken.get_primary_key_from_dn(token['dn'])
- self.api.Command.otptoken_del(token)
+ if orphan:
+ self.api.Command.otptoken_mod(token, ipatokenowner=None)
+ else:
+ self.api.Command.otptoken_del(token)
return dn