Skip to content

Commit

Permalink
Changes to the models that are expected to be 'members' of groups to …
Browse files Browse the repository at this point in the history
…have get_groups() and first pass at publisher auth profile.

Checks in auth/publisher/update.py use _groups_intersect() to determine whether the user's groups intersect with those of the object being edited, and if so then it allows the flow to continue
  • Loading branch information
rossjones committed Jan 23, 2012
1 parent ba417cd commit 69cba2d
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 5 deletions.
2 changes: 1 addition & 1 deletion ckan/controllers/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _db_to_form_schema(self):

def _setup_template_variables(self, context):
c.is_sysadmin = Authorizer().is_sysadmin(c.user)

## This is messy as auths take domain object not data_dict
context_group = context.get('group',None)
group = context_group or c.group
Expand Down
7 changes: 7 additions & 0 deletions ckan/logic/auth/publisher/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


def _groups_intersect( groups_A, groups_B ):
""" Return true if any of the groups in A are also in B (or size
of intersection > 0)"""
return len( set( groups_A ).intersection( set(groups_B) ) ) > 0

5 changes: 5 additions & 0 deletions ckan/logic/auth/publisher/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from ckan.logic.auth import get_package_object, get_group_object, get_authorization_group_object, \
get_user_object, get_resource_object
from ckan.logic.auth.publisher import _groups_intersect
from ckan.logic import check_access_old, NotFound
from ckan.authz import Authorizer
from ckan.lib.base import _
Expand Down Expand Up @@ -48,6 +49,8 @@ def group_create(context, data_dict=None):
model = context['model']
user = context['user']

# TODO: We need to check whether this group is being created within another group

authorized = check_access_old(model.System(), model.Action.GROUP_CREATE, context)
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to create groups') % str(user)}
Expand All @@ -57,6 +60,8 @@ def group_create(context, data_dict=None):
def authorization_group_create(context, data_dict=None):
model = context['model']
user = context['user']

# TODO: We need to check whether this group is being created within another group

authorized = check_access_old(model.System(), model.Action.AUTHZ_GROUP_CREATE, context)
if not authorized:
Expand Down
42 changes: 40 additions & 2 deletions ckan/logic/auth/publisher/update.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Updated: False

from ckan.logic import check_access_old, NotFound
from ckan.logic.auth import get_package_object, get_group_object, get_authorization_group_object, \
get_user_object, get_resource_object
from ckan.logic.auth.publisher import _groups_intersect
from ckan.logic.auth.create import check_group_auth, package_relationship_create
from ckan.authz import Authorizer
from ckan.lib.base import _
Expand All @@ -15,6 +14,12 @@ def package_update(context, data_dict):
user = context.get('user')
package = get_package_object(context, data_dict)

userobj = model.User.get( user )

# Only allow package update if the user and package groups intersect
if not _groups_intersect( userobj.get_groups(), package.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to edit packages in these groups') % str(user)}

check1 = check_access_old(package, model.Action.EDIT, context)
if not check1:
return {'success': False, 'msg': _('User %s not authorized to edit package %s') % (str(user), package.id)}
Expand All @@ -30,6 +35,11 @@ def resource_update(context, data_dict):
user = context.get('user')
resource = get_resource_object(context, data_dict)

# Only allow resource update if the user and resource packages groups intersect
userobj = model.User.get( user )
if not _groups_intersect( userobj.get_groups(), resource.package.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to edit resources in this package') % str(user)}

# check authentication against package
query = model.Session.query(model.Package)\
.join(model.ResourceGroup)\
Expand All @@ -55,6 +65,10 @@ def package_change_state(context, data_dict):
user = context['user']
package = get_package_object(context, data_dict)

userobj = model.User.get( user )
if not _groups_intersect( userobj.get_groups(), package.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to change this package state') % str(user)}

authorized = check_access_old(package, model.Action.CHANGE_STATE, context)
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to change state of package %s') % (str(user),package.id)}
Expand All @@ -66,6 +80,11 @@ def package_edit_permissions(context, data_dict):
user = context['user']
package = get_package_object(context, data_dict)

# Only allow package update if the user and package groups intersect
userobj = model.User.get( user )
if not _groups_intersect( userobj.get_groups(), package.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to edit permissions of this package') % str(user)}

authorized = check_access_old(package, model.Action.EDIT_PERMISSIONS, context)
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to edit permissions of package %s') % (str(user),package.id)}
Expand All @@ -77,6 +96,11 @@ def group_update(context, data_dict):
user = context['user']
group = get_group_object(context, data_dict)

# Only allow package update if the user and package groups intersect
userobj = model.User.get( user )
if not _groups_intersect( userobj.get_groups(), group.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to edit this group') % str(user)}

authorized = check_access_old(group, model.Action.EDIT, context)
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to edit group %s') % (str(user),group.id)}
Expand All @@ -88,6 +112,10 @@ def group_change_state(context, data_dict):
user = context['user']
group = get_group_object(context, data_dict)

userobj = model.User.get( user )
if not _groups_intersect( userobj.get_groups(), group.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to change state of group') % str(user)}

authorized = check_access_old(group, model.Action.CHANGE_STATE, context)
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to change state of group %s') % (str(user),group.id)}
Expand All @@ -99,6 +127,11 @@ def group_edit_permissions(context, data_dict):
user = context['user']
group = get_group_object(context, data_dict)

# Only allow package update if the user and package groups intersect
userobj = model.User.get( user )
if not _groups_intersect( userobj.get_groups(), group.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to edit permissions of group') % str(user)}

authorized = check_access_old(group, model.Action.EDIT_PERMISSIONS, context)
if not authorized:
return {'success': False, 'msg': _('User %s not authorized to edit permissions of group %s') % (str(user),group.id)}
Expand Down Expand Up @@ -136,6 +169,11 @@ def user_update(context, data_dict):
not ('reset_key' in data_dict and data_dict['reset_key'] == user_obj.reset_key):
return {'success': False, 'msg': _('User %s not authorized to edit user %s') % (str(user), user_obj.id)}

# Only allow package update if the user and package groups intersect or user is editing self
if (user != user_obj.name) and
not _groups_intersect( current_user.get_groups(), user_obj.get_groups() ):
return {'success': False, 'msg': _('User %s not authorized to edit user') % str(user)}

return {'success': True}

def revision_change_state(context, data_dict):
Expand Down
33 changes: 31 additions & 2 deletions ckan/model/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ def get(cls, reference):
return group
# Todo: Make sure group names can't be changed to look like group IDs?

def members_of_type(self, object_type):
object_type_string = object_type.__name__.lower()
query = Session.query(object_type).\
filter_by(state=vdm.sqlalchemy.State.ACTIVE).\
filter(group_table.c.id == self.id).\
filter(member_table.c.state == 'active').\
filter(member_table.c.table_name == object_type_string).\
join(member_table, member_table.c.table_id == getattr(object_type,'id') ).\
join(group_table, group_table.c.id == member_table.c.group_id)
return query

def add_child(self, object_instance):
object_type_string = object_instance.__class__.__name__.lower()
if not object_instance in self.members_of_type(object_instance.__class__).all():
member = Member(group=self, table_id=getattr(object_instance,'id'), table_name=object_type_string)
Session.add(member)


def active_packages(self, load_eager=True):
query = Session.query(Package).\
filter_by(state=vdm.sqlalchemy.State.ACTIVE).\
Expand All @@ -114,10 +132,22 @@ def add_package_by_name(self, package_name):
return
package = Package.by_name(package_name)
assert package
if not package in self.active_packages().all():
if not package in self.members_of_type( package.__class__ ).all():
member = Member(group=self, table_id=package.id, table_name='package')
Session.add(member)

def get_groups(self):
""" Get all groups that this group is within """
import ckan.model as model
if '_groups' not in self.__dict__:
self._groups = model.Session.query(model.Group).\
join(model.Member, model.Member.group_id == model.Group.id).\
join(model.Group, model.Group.id == model.Member.table_id).\
join(model.Package, model.Member.table_name == 'group' ).\
filter(model.Member.state == 'active').\
filter(model.Group.id == self.id).all()
return self._groups


@property
def all_related_revisions(self):
Expand Down Expand Up @@ -170,4 +200,3 @@ def __repr__(self):
#TODO
MemberRevision.related_packages = lambda self: [self.continuity.package]


4 changes: 4 additions & 0 deletions ckan/model/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,12 +498,16 @@ def metadata_modified(self):
timestamp_float = timegm(timestamp_without_usecs) + usecs
return datetime.datetime.utcfromtimestamp(timestamp_float)

def is_in_group(self, group):
return group in self.get_groups()

def get_groups(self):
import ckan.model as model
if '_groups' not in self.__dict__:
self._groups = model.Session.query(model.Group).\
join(model.Member, model.Member.group_id == model.Group.id).\
join(model.Package, model.Package.id == model.Member.table_id).\
join(model.Package, model.Member.table_name == 'package' ).\
filter(model.Member.state == 'active').\
filter(model.Package.id == self.id).all()
return self._groups
Expand Down
14 changes: 14 additions & 0 deletions ckan/model/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ def number_administered_packages(self):
q = q.filter_by(user=self, role=model.Role.ADMIN)
return q.count()

def is_in_group(self, group):
return group in self.get_groups()

def get_groups(self):
import ckan.model as model
if '_groups' not in self.__dict__:
self._groups = model.Session.query(model.Group).\
join(model.Member, model.Member.group_id == model.Group.id).\
join(model.User, model.User.id == model.Member.table_id).\
join(model.Package, model.Member.table_name == 'user' ).\
filter(model.Member.state == 'active').\
filter(model.User.id == self.id).all()
return self._groups

@classmethod
def search(cls, querystr, sqlalchemy_query=None):
'''Search name, fullname, email and openid.
Expand Down

0 comments on commit 69cba2d

Please sign in to comment.