summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2014-08-18 16:49:40 +0200
committerMartin Kosek <mkosek@redhat.com>2014-08-21 14:07:01 +0200
commit8fabd6dde152fc394bd4f093d93c8a46e5b2851b (patch)
tree91e2dc7dfd6ffe7c09ec5b23b59a3a2e010786c9
parent27128bd8f50cebb8fc3b8a86b642ca0e272d2024 (diff)
downloadfreeipa-8fabd6dde152fc394bd4f093d93c8a46e5b2851b.tar.gz
freeipa-8fabd6dde152fc394bd4f093d93c8a46e5b2851b.tar.xz
freeipa-8fabd6dde152fc394bd4f093d93c8a46e5b2851b.zip
Support delegating RBAC roles to service principals
https://fedorahosted.org/freeipa/ticket/3164 Reviewed-By: Martin Kosek <mkosek@redhat.com>
-rw-r--r--API.txt6
-rw-r--r--VERSION4
-rw-r--r--ipalib/plugins/role.py2
-rw-r--r--ipalib/plugins/service.py3
-rw-r--r--ipatests/test_integration/test_service_permissions.py82
-rw-r--r--ipatests/test_xmlrpc/test_role_plugin.py2
-rw-r--r--ipatests/test_xmlrpc/test_service_plugin.py124
7 files changed, 217 insertions, 6 deletions
diff --git a/API.txt b/API.txt
index d731881ee..4fa275592 100644
--- a/API.txt
+++ b/API.txt
@@ -2908,7 +2908,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: role_add_member
-args: 1,8,3
+args: 1,9,3
arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
@@ -2916,6 +2916,7 @@ option: Str('host*', alwaysask=True, cli_name='hosts', csv=True)
option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups', csv=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('service*', alwaysask=True, cli_name='services', csv=True)
option: Str('user*', alwaysask=True, cli_name='users', csv=True)
option: Str('version?', exclude='webui')
output: Output('completed', <type 'int'>, None)
@@ -2973,7 +2974,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: role_remove_member
-args: 1,8,3
+args: 1,9,3
arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
@@ -2981,6 +2982,7 @@ option: Str('host*', alwaysask=True, cli_name='hosts', csv=True)
option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups', csv=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('service*', alwaysask=True, cli_name='services', csv=True)
option: Str('user*', alwaysask=True, cli_name='users', csv=True)
option: Str('version?', exclude='webui')
output: Output('completed', <type 'int'>, None)
diff --git a/VERSION b/VERSION
index 2a2789a8a..379ead756 100644
--- a/VERSION
+++ b/VERSION
@@ -89,5 +89,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=101
-# Last change: mbasti - Allow '/' in permission name
+IPA_API_VERSION_MINOR=102
+# Last change: pviktori - allow adding services to roles
diff --git a/ipalib/plugins/role.py b/ipalib/plugins/role.py
index b290ceeb1..f2021d3fa 100644
--- a/ipalib/plugins/role.py
+++ b/ipalib/plugins/role.py
@@ -75,7 +75,7 @@ class role(LDAPObject):
'memberindirect', 'memberofindirect',
]
attribute_members = {
- 'member': ['user', 'group', 'host', 'hostgroup'],
+ 'member': ['user', 'group', 'host', 'hostgroup', 'service'],
'memberof': ['privilege'],
}
reverse_members = {
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 9f3791aab..69b2cc6c3 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -306,10 +306,11 @@ class service(LDAPObject):
permission_filter_objectclasses = ['ipaservice']
search_attributes = ['krbprincipalname', 'managedby', 'ipakrbauthzdata']
default_attributes = ['krbprincipalname', 'usercertificate', 'managedby',
- 'ipakrbauthzdata',]
+ 'ipakrbauthzdata', 'memberof']
uuid_attribute = 'ipauniqueid'
attribute_members = {
'managedby': ['host'],
+ 'memberof': ['role'],
}
bindable = True
relationships = {
diff --git a/ipatests/test_integration/test_service_permissions.py b/ipatests/test_integration/test_service_permissions.py
new file mode 100644
index 000000000..3d4a50d32
--- /dev/null
+++ b/ipatests/test_integration/test_service_permissions.py
@@ -0,0 +1,82 @@
+# Authors:
+# Petr Viktorin <pviktori@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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 os
+
+from ipatests.test_integration.base import IntegrationTest
+from ipatests.test_integration import tasks
+
+
+class TestServicePermissions(IntegrationTest):
+ topology = 'star'
+
+ def test_service_as_user_admin(self):
+ """Test that a service in User Administrator role can manage users"""
+
+ service_name = 'testservice/%s@%s' % (self.master.hostname,
+ self.master.domain.realm)
+ keytab_file = os.path.join(self.master.config.test_dir,
+ 'testservice_keytab')
+
+ # Prepare a service
+
+ self.master.run_command(['ipa', 'service-add', service_name])
+
+ self.master.run_command(['ipa-getkeytab',
+ '-p', service_name,
+ '-k', keytab_file,
+ '-s', self.master.hostname])
+
+ # Check that the service cannot add a user
+
+ self.master.run_command(['kdestroy'])
+ self.master.run_command(['kinit', '-k', service_name,
+ '-t', keytab_file])
+
+ result = self.master.run_command(['ipa', 'role-add-member',
+ 'User Administrator',
+ '--service', service_name],
+ raiseonerr=False)
+ assert result.returncode > 0
+
+ # Add service to User Administrator role
+
+ self.master.run_command(['kdestroy'])
+ tasks.kinit_admin(self.master)
+
+ self.master.run_command(['ipa', 'role-add-member',
+ 'User Administrator',
+ '--service', service_name])
+
+ # Check that the service now can add a user
+
+ self.master.run_command(['kdestroy'])
+ self.master.run_command(['kinit', '-k', service_name,
+ '-t', keytab_file])
+
+ self.master.run_command(['ipa', 'user-add', 'tuser',
+ '--first', 'a', '--last', 'b', '--random'])
+
+ # Clean up
+
+ self.master.run_command(['kdestroy'])
+ tasks.kinit_admin(self.master)
+
+ self.master.run_command(['ipa', 'service-del', service_name])
+ self.master.run_command(['ipa', 'user-del', 'tuser'])
diff --git a/ipatests/test_xmlrpc/test_role_plugin.py b/ipatests/test_xmlrpc/test_role_plugin.py
index f400b0fa8..9a1c0748d 100644
--- a/ipatests/test_xmlrpc/test_role_plugin.py
+++ b/ipatests/test_xmlrpc/test_role_plugin.py
@@ -257,6 +257,7 @@ class test_role(Declarative):
group=[],
host=[],
hostgroup=[],
+ service=[],
),
),
result={
@@ -436,6 +437,7 @@ class test_role(Declarative):
group=[],
host=[],
hostgroup=[],
+ service=[],
),
),
result={
diff --git a/ipatests/test_xmlrpc/test_service_plugin.py b/ipatests/test_xmlrpc/test_service_plugin.py
index 532d9888c..5be27af9e 100644
--- a/ipatests/test_xmlrpc/test_service_plugin.py
+++ b/ipatests/test_xmlrpc/test_service_plugin.py
@@ -40,6 +40,8 @@ host1dn = DN(('fqdn',fqdn1),('cn','computers'),('cn','accounts'),api.env.basedn)
host2dn = DN(('fqdn',fqdn2),('cn','computers'),('cn','accounts'),api.env.basedn)
host3dn = DN(('fqdn',fqdn3),('cn','computers'),('cn','accounts'),api.env.basedn)
+role1 = u'Test Role'
+role1_dn = DN(('cn', role1), api.env.container_rolegroup, api.env.basedn)
badservercert = 'MIICbzCCAdigAwIBAgICA/4wDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBBIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDgwOTE1MDIyN1oXDTIwMDgwOTE1MDIyN1owKTEMMAoGA1UEChMDSVBBMRkwFwYDVQQDExBwdW1hLmdyZXlvYWsuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwYbfEOQPgGenPn9vt1JFKvWm/Je3y2tawGWA3LXDuqfFJyYtZ8ib3TcBUOnLk9WK5g2qCwHaNlei7bj8ggIfr5hegAVe10cun+wYErjnYo7hsHYd+57VZezeipWrXu+7NoNd4+c4A5lk4A/xJay9j3bYx2oOM8BEox4xWYoWge1ljPrc5JK46f0X7AGW4F2VhnKPnf8rwSuzI1U8VGjutyM9TWNy3m9KMWeScjyG/ggIpOjUDMV7HkJL0Di61lznR9jXubpiEC7gWGbTp84eGl/Nn9bgK1AwHfJ2lHwfoY4uiL7ge1gyP6EvuUlHoBzdb7pekiX28iePjW3iEG9IawIDAQABoyIwIDARBglghkgBhvhCAQEEBAMCBkAwCwYDVR0PBAQDAgUgMA0GCSqGSIb3DQEBBQUAA4GBACRESLemRV9BPxfEgbALuxH5oE8jQm8WZ3pm2pALbpDlAd9wQc3yVf6RtkfVthyDnM18bg7IhxKpd77/p3H8eCnS8w5MLVRda6ktUC6tGhFTS4QKAf0WyDGTcIgkXbeDw0OPAoNHivoXbIXIIRxlw/XgaSaMzJQDBG8iROsN4kCv'
@@ -626,3 +628,125 @@ class test_service(Declarative):
]
+
+
+class test_service_in_role(Declarative):
+ cleanup_commands = [
+ ('host_del', [fqdn1], {}),
+ ('service_del', [service1], {}),
+ ('role_del', [role1], {}),
+ ]
+
+ tests = [
+ dict(
+ desc='Create %r' % fqdn1,
+ command=('host_add', [fqdn1],
+ dict(
+ description=u'Test host 1',
+ l=u'Undisclosed location 1',
+ force=True,
+ ),
+ ),
+ expected=dict(
+ value=fqdn1,
+ summary=u'Added host "%s"' % fqdn1,
+ result=dict(
+ dn=host1dn,
+ fqdn=[fqdn1],
+ description=[u'Test host 1'],
+ l=[u'Undisclosed location 1'],
+ krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+ objectclass=objectclasses.host,
+ ipauniqueid=[fuzzy_uuid],
+ managedby_host=[u'%s' % fqdn1],
+ has_keytab=False,
+ has_password=False,
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Create %r' % service1,
+ command=('service_add', [service1], dict(force=True)),
+ expected=dict(
+ value=service1,
+ summary=u'Added service "%s"' % service1,
+ result=dict(
+ dn=service1dn,
+ krbprincipalname=[service1],
+ objectclass=objectclasses.service,
+ ipauniqueid=[fuzzy_uuid],
+ managedby_host=[fqdn1],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Create %r' % role1,
+ command=('role_add', [role1], dict(description=u'role desc 1')),
+ expected=dict(
+ value=role1,
+ summary=u'Added role "%s"' % role1,
+ result=dict(
+ dn=role1_dn,
+ cn=[role1],
+ description=[u'role desc 1'],
+ objectclass=objectclasses.role,
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Add %r to %r' % (service1, role1),
+ command=('role_add_member', [role1], dict(service=service1)),
+ expected=dict(
+ failed=dict(
+ member=dict(
+ host=[],
+ group=[],
+ hostgroup=[],
+ service=[],
+ user=[],
+ ),
+ ),
+ completed=1,
+ result=dict(
+ dn=role1_dn,
+ cn=[role1],
+ description=[u'role desc 1'],
+ member_service=[service1],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Verify %r is member of %r' % (service1, role1),
+ command=('service_show', [service1], {}),
+ expected=dict(
+ value=service1,
+ summary=None,
+ result=dict(
+ dn=service1dn,
+ krbprincipalname=[service1],
+ managedby_host=[fqdn1],
+ memberof_role=[role1.lower()],
+ has_keytab=False,
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Verify %r has member %r' % (role1, service1),
+ command=('role_show', [role1], {}),
+ expected=dict(
+ value=role1,
+ summary=None,
+ result=dict(
+ dn=role1_dn,
+ cn=[role1],
+ description=[u'role desc 1'],
+ member_service=[service1],
+ ),
+ ),
+ ),
+ ]