mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
sudorule: Allow using hostmasks for setting allowed hosts
Adds a new --hostmasks option to sudorule-add-host and sudorule-remove-host commands, which allows setting a range of hosts specified by a hostmask. https://fedorahosted.org/freeipa/ticket/4274 Reviewed-By: Petr Viktorin <pviktori@redhat.com>
This commit is contained in:
committed by
Petr Viktorin
parent
5a1207cb6e
commit
a228d7a3cb
6
API.txt
6
API.txt
@@ -3474,11 +3474,12 @@ 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: sudorule_add_host
|
||||
args: 1,6,3
|
||||
args: 1,7,3
|
||||
arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=True)
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Str('host*', alwaysask=True, cli_name='hosts', csv=True)
|
||||
option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups', csv=True)
|
||||
option: Str('hostmask?', multivalue=True)
|
||||
option: Flag('no_members', autofill=True, default=False, exclude='webui')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('version?', exclude='webui')
|
||||
@@ -3627,11 +3628,12 @@ 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: sudorule_remove_host
|
||||
args: 1,6,3
|
||||
args: 1,7,3
|
||||
arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=True)
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
|
||||
option: Str('host*', alwaysask=True, cli_name='hosts', csv=True)
|
||||
option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups', csv=True)
|
||||
option: Str('hostmask?', multivalue=True)
|
||||
option: Flag('no_members', autofill=True, default=False, exclude='webui')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
|
||||
option: Str('version?', exclude='webui')
|
||||
|
||||
@@ -86,6 +86,7 @@ add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%
|
||||
add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%deref_rf(\"memberHost\",\"(&(objectclass=ipaHostGroup)(!(objectclass=mepOriginEntry)))\",\"member\",\"(|(objectclass=ipaHostGroup)(objectclass=ipaHost))\",\"fqdn\")")'
|
||||
add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","+%deref_f(\"memberHost\",\"(&(objectclass=ipaHostGroup)(objectclass=mepOriginEntry))\",\"cn\")")'
|
||||
add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","+%deref_f(\"memberHost\",\"(objectclass=ipaNisNetgroup)\",\"cn\")")'
|
||||
add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%{hostMask}")'
|
||||
add:schema-compat-entry-attribute: 'sudoCommand=%ifeq("cmdCategory","all","ALL","%deref(\"memberAllowCmd\",\"sudoCmd\")")'
|
||||
add:schema-compat-entry-attribute: 'sudoCommand=%ifeq("cmdCategory","all","ALL","%deref_r(\"memberAllowCmd\",\"member\",\"sudoCmd\")")'
|
||||
add:schema-compat-entry-attribute: 'sudoCommand=!%deref("memberDenyCmd","sudoCmd")'
|
||||
|
||||
@@ -2,6 +2,9 @@ dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config
|
||||
only:schema-compat-entry-rdn:'%ifeq("ipaEnabledFlag", "FALSE", "DISABLED", "cn=%{cn}")'
|
||||
replace: schema-compat-entry-attribute:'sudoRunAsGroup=%deref("ipaSudoRunAs","cn")::sudoRunAsGroup=%deref_f("ipaSudoRunAsGroup","(objectclass=posixGroup)","cn")'
|
||||
|
||||
dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config
|
||||
add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%{hostMask}")'
|
||||
|
||||
# Change padding for host and userCategory so the pad returns the same value
|
||||
# as the original, '' or -.
|
||||
dn: cn=ng,cn=Schema Compatibility,cn=plugins,cn=config
|
||||
|
||||
@@ -17,6 +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/>.
|
||||
|
||||
import netaddr
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipalib import Str, StrEnum, Bool, Int
|
||||
from ipalib.plugable import Registry
|
||||
@@ -30,6 +32,7 @@ from ipalib.plugins.baseldap import (LDAPObject, LDAPCreate, LDAPDelete,
|
||||
external_host_param)
|
||||
from ipalib.plugins.hbacrule import is_all
|
||||
from ipalib import _, ngettext
|
||||
from ipalib.util import validate_hostmask
|
||||
from ipapython.dn import DN
|
||||
|
||||
__doc__ = _("""
|
||||
@@ -94,6 +97,12 @@ def deprecated(attribute):
|
||||
error=_('this option has been deprecated.'))
|
||||
|
||||
|
||||
hostmask_membership_param = Str('hostmask?', validate_hostmask,
|
||||
label=_('host masks of allowed hosts'),
|
||||
flags=['no_create', 'no_update', 'no_search'],
|
||||
multivalue=True,
|
||||
)
|
||||
|
||||
def validate_externaluser(ugettext, value):
|
||||
deprecated('externaluser')
|
||||
|
||||
@@ -123,7 +132,7 @@ class sudorule(LDAPObject):
|
||||
'memberallowcmd', 'memberdenycmd', 'ipasudoopt',
|
||||
'ipasudorunas', 'ipasudorunasgroup',
|
||||
'ipasudorunasusercategory', 'ipasudorunasgroupcategory',
|
||||
'sudoorder',
|
||||
'sudoorder', 'hostmask',
|
||||
]
|
||||
uuid_attribute = 'ipauniqueid'
|
||||
rdn_attribute = 'ipauniqueid'
|
||||
@@ -267,6 +276,12 @@ class sudorule(LDAPObject):
|
||||
label=_('Host Groups'),
|
||||
flags=['no_create', 'no_update', 'no_search'],
|
||||
),
|
||||
Str('hostmask', validate_hostmask,
|
||||
normalizer=lambda x: unicode(netaddr.IPNetwork(x).cidr),
|
||||
label=_('Host Masks'),
|
||||
flags=['no_create', 'no_update', 'no_search'],
|
||||
multivalue=True,
|
||||
),
|
||||
Str('memberallowcmd_sudocmd?',
|
||||
label=_('Sudo Allow Commands'),
|
||||
flags=['no_create', 'no_update', 'no_search'],
|
||||
@@ -577,6 +592,11 @@ class sudorule_add_host(LDAPAddMember):
|
||||
member_attributes = ['memberhost']
|
||||
member_count_out = ('%i object added.', '%i objects added.')
|
||||
|
||||
def get_options(self):
|
||||
for option in super(sudorule_add_host, self).get_options():
|
||||
yield option
|
||||
yield hostmask_membership_param
|
||||
|
||||
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
try:
|
||||
@@ -593,7 +613,30 @@ class sudorule_add_host(LDAPAddMember):
|
||||
def post_callback(self, ldap, completed, failed, dn, entry_attrs,
|
||||
*keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
return add_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options)
|
||||
try:
|
||||
_entry_attrs = ldap.get_entry(dn, self.obj.default_attributes)
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
|
||||
if 'hostmask' in options:
|
||||
norm = lambda x: unicode(netaddr.IPNetwork(x).cidr)
|
||||
|
||||
old_masks = set(map(norm, _entry_attrs.get('hostmask', [])))
|
||||
new_masks = set(map(norm, options['hostmask']))
|
||||
|
||||
num_added = len(new_masks - old_masks)
|
||||
|
||||
if num_added:
|
||||
entry_attrs['hostmask'] = list(old_masks | new_masks)
|
||||
try:
|
||||
ldap.update_entry(entry_attrs)
|
||||
except errors.EmptyModlist:
|
||||
pass
|
||||
completed = completed + num_added
|
||||
|
||||
return add_external_post_callback('memberhost', 'host', 'externalhost',
|
||||
ldap, completed, failed, dn,
|
||||
entry_attrs, keys, options)
|
||||
|
||||
|
||||
|
||||
@@ -604,9 +647,35 @@ class sudorule_remove_host(LDAPRemoveMember):
|
||||
member_attributes = ['memberhost']
|
||||
member_count_out = ('%i object removed.', '%i objects removed.')
|
||||
|
||||
def get_options(self):
|
||||
for option in super(sudorule_remove_host, self).get_options():
|
||||
yield option
|
||||
yield hostmask_membership_param
|
||||
def post_callback(self, ldap, completed, failed, dn, entry_attrs,
|
||||
*keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
|
||||
try:
|
||||
_entry_attrs = ldap.get_entry(dn, self.obj.default_attributes)
|
||||
except errors.NotFound:
|
||||
self.obj.handle_not_found(*keys)
|
||||
|
||||
if 'hostmask' in options:
|
||||
norm = lambda x: unicode(netaddr.IPNetwork(x).cidr)
|
||||
|
||||
old_masks = set(map(norm, _entry_attrs.get('hostmask', [])))
|
||||
removed_masks = set(map(norm, options['hostmask']))
|
||||
|
||||
num_added = len(removed_masks & old_masks)
|
||||
|
||||
if num_added:
|
||||
entry_attrs['hostmask'] = list(old_masks - removed_masks)
|
||||
try:
|
||||
ldap.update_entry(entry_attrs)
|
||||
except errors.EmptyModlist:
|
||||
pass
|
||||
completed = completed + num_added
|
||||
|
||||
return remove_external_post_callback('memberhost', 'host',
|
||||
'externalhost', ldap, completed,
|
||||
failed, dn, entry_attrs, keys,
|
||||
|
||||
@@ -32,6 +32,7 @@ from types import NoneType
|
||||
from weakref import WeakKeyDictionary
|
||||
from dns import resolver, rdatatype
|
||||
from dns.exception import DNSException
|
||||
from netaddr.core import AddrFormatError
|
||||
|
||||
from ipalib import errors
|
||||
from ipalib.text import _
|
||||
@@ -544,3 +545,9 @@ def validate_rdn_param(ugettext, value):
|
||||
except Exception, e:
|
||||
return str(e)
|
||||
return None
|
||||
|
||||
def validate_hostmask(ugettext, hostmask):
|
||||
try:
|
||||
netaddr.IPNetwork(hostmask)
|
||||
except (ValueError, AddrFormatError):
|
||||
return _('invalid hostmask')
|
||||
|
||||
Reference in New Issue
Block a user