Support delegating RBAC roles to service principals

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

Reviewed-By: Martin Kosek <mkosek@redhat.com>
This commit is contained in:
Petr Viktorin 2014-08-18 16:49:40 +02:00 committed by Martin Kosek
parent 27128bd8f5
commit 8fabd6dde1
7 changed files with 217 additions and 6 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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'])

View File

@ -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={

View File

@ -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],
),
),
),
]