This also constitutes a rethinking of the token ACIs after the
introduction of SELFDN support.

Admins, as before, have full access to all token permissions.

Normal users have read/search/compare access to all of the non-secret
data for tokens assigned to them, whether protected or non-protected.
Users can add or delete non-protected tokens and modify most of their
metadata. However they cannot create, delete or modify protected tokens.
Regardless of whether the token is protected or not, users cannot change
a token's ownership or unique identity.

In contrast, admins can create protected tokens. This protects the token
from deletion or modification when assigned to users. Additionally, when
a user account is deleted, the assigned non-protected tokens are deleted
but the protected tokens are merely orphaned. This permits the token to
be reassigned without having to recreate it. This last point is
particularly useful in the case of hardware tokens.

https://fedorahosted.org/freeipa/ticket/4228

NOTE: This patch depends on my patch 0048.
>From a95e3cee5fdd7b98635d984977cbb71ad5c7aa02 Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Fri, 2 May 2014 16:44:30 -0400
Subject: [PATCH] Add support for protected tokens

This also constitutes a rethinking of the token ACIs after the introduction
of SELFDN support.

Admins, as before, have full access to all token permissions.

Normal users have read/search/compare access to all of the non-secret data
for tokens assigned to them, whether protected or non-protected. Users can
add or delete non-protected tokens and modify most of their metadata.
However they cannot create, delete or modify protected tokens. Regardless
of whether the token is protected or not, users cannot change a token's
ownership or unique identity.

In contrast, admins can create protected tokens. This protects the token
from deletion or modification when assigned to users. Additionally, when
a user account is deleted, the assigned non-protected tokens are deleted
but the protected tokens are merely orphaned. This permits the token to
be reassigned without having to recreate it. This last point is
particularly useful in the case of hardware tokens.

https://fedorahosted.org/freeipa/ticket/4228
---
 install/share/70ipaotp.ldif    |  3 ++-
 install/share/default-aci.ldif | 10 +++++-----
 install/updates/40-otp.update  | 16 +++++++++++-----
 ipalib/plugins/otptoken.py     |  9 +++++++++
 ipalib/plugins/user.py         |  9 ++++++++-
 5 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/install/share/70ipaotp.ldif b/install/share/70ipaotp.ldif
index a40ad9ee0cfcf72ed6b79306396a29683f9e1a9d..08f639b6cd14b6dd1270a604fdd061cecb4a6482 100644
--- a/install/share/70ipaotp.ldif
+++ b/install/share/70ipaotp.ldif
@@ -23,7 +23,8 @@ 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')
+attributeTypes: (2.16.840.1.113730.3.8.16.1.22 NAME 'ipatokenProtected' DESC 'Optionally marks token as Protected' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 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 $ ipatokenProtected) 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 480facf3294c593c6a2bcf326e20c32157d6d3c6..2aa423b4a72170adde1327cd3aeee1ff4558d319 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -97,8 +97,8 @@ 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 || ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial || ipatokenProtected")(version 3.0; acl "Users can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)
+aci: (targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPtimeStep")(version 3.0; acl "Users can see TOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)
+aci: (targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits")(version 3.0; acl "Users can see HOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)
+aci: (targetfilter = "(&(objectClass=ipaToken)(!(ipatokenProtected=TRUE)))")(targetattrs = "description || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can write basic token info"; allow (write) userattr = "ipatokenOwner#USERDN";)
+aci: (target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX";)(targetfilter = "(&(objectClass=ipaToken)(!(ipatokenProtected=TRUE))))")(version 3.0; acl "Users can create and delete tokens"; allow (add, delete) userattr = "ipatokenOwner#SELFDN";)
diff --git a/install/updates/40-otp.update b/install/updates/40-otp.update
index ba9be5f0569edffea6fae6aaee031012414f9353..776c1f84d80053d3eddfb49f9d98d410020822d4 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 || ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial || ipatokenProtected")(version 3.0; acl "Users can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipatokenTOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits || ipatokenTOTPtimeStep")(version 3.0; acl "Users can see TOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)'
+add: aci:'(targetfilter = "(objectClass=ipatokenHOTP)")(targetattrs = "ipatokenOTPalgorithm || ipatokenOTPdigits")(version 3.0; acl "Users can see HOTP details"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)'
+add: aci:'(targetfilter = "(&(objectClass=ipaToken)(!(ipatokenProtected=TRUE)))")(targetattrs = "description || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can write basic token info"; allow (write) userattr = "ipatokenOwner#USERDN";)'
+add: aci:'(target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX";)(targetfilter = "(&(objectClass=ipaToken)(!(ipatokenProtected=TRUE))))")(version 3.0; acl "Users can create and delete tokens"; allow (add, delete) userattr = "ipatokenOwner#SELFDN";)'
+
 
 dn: cn=radiusproxy,$SUFFIX
 default: objectClass: nsContainer
diff --git a/ipalib/plugins/otptoken.py b/ipalib/plugins/otptoken.py
index 26a2275587f3a7ef97c65e8cea6b431c05c00157..f1e52fadab097c4dfccaf2be40c4d3dae7492313 100644
--- a/ipalib/plugins/otptoken.py
+++ b/ipalib/plugins/otptoken.py
@@ -144,6 +144,10 @@ class otptoken(LDAPObject):
             cli_name='disabled',
             label=_('Disabled state')
         ),
+        Bool('ipatokenprotected?',
+            cli_name='protected',
+            label=_('Protected')
+        ),
         Str('ipatokennotbefore?',
             cli_name='not_before',
             label=_('Validity start'),
@@ -248,6 +252,11 @@ class otptoken_add(LDAPCreate):
         if "ipatokenowner" not in entry_attrs:
             entry_attrs["ipatokenowner"] = cur_uid
 
+        # Determine if the token should be protected or not
+        if "ipatokenprotected" not in entry_attrs:
+            if cur_uid != entry_attrs.get('ipatokenowner', None):
+                entry_attrs["ipatokenprotected"] = True
+
         # Resolve the owner's dn
         _normalize_owner(self.api.Object.user, entry_attrs)
 
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
index 166955933b2fd8b1cd1cddd5e4d48f4c97b2d7cd..6a337ad88cec5690f583d1af9018ac131ac3b41b 100644
--- a/ipalib/plugins/user.py
+++ b/ipalib/plugins/user.py
@@ -667,7 +667,14 @@ class user_del(LDAPDelete):
         results = self.api.Command.otptoken_find(ipatokenowner=owner)['result']
         for token in results:
             token = self.api.Object.otptoken.get_primary_key_from_dn(token['dn'])
-            self.api.Command.otptoken_del(token)
+
+            # If the token is protected, orphan it.
+            if token.get('ipatokenprotected', False):
+                self.api.Command.otptoken_mod(token, ipatokenowner=None)
+
+            # Otherwise, delete it.
+            else:
+                self.api.Command.otptoken_del(token)
 
         return dn
 
-- 
1.9.0

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to