def check_resource_owners(user1, user2, resource, grantor): """ check that each resource has the proper number of owners """ owners = User.objects.filter(u2urp__resource=resource, u2urp__privilege=PrivilegeCodes.OWNER) if user1 not in owners: # fix it NOW print(" SHARING {} {} with first owner {}".format( resource.short_id, resource.title.encode('ascii', 'ignore'), user1.username)) UserResourcePrivilege.share(user=user1, resource=resource, privilege=PrivilegeCodes.OWNER, grantor=grantor) # first argument is also quota holder. set_quota_holder(resource, user1) # for CZO national group, there's only one owner. if user1 != user2 and user2 not in owners: # fix it NOW print(" SHARING {} {} with second owner {}".format( resource.short_id, resource.title.encode('ascii', 'ignore'), user2.username)) UserResourcePrivilege.share(user=user2, resource=resource, privilege=PrivilegeCodes.OWNER, grantor=grantor) for o in owners: if o.username != user1.username and o.username != user2.username: # fix it NOW print(" UNSHARING {} {} with owner {}".format( resource.short_id, resource.title.encode('ascii', 'ignore'), o.username)) UserResourcePrivilege.unshare(user=o, resource=resource, grantor=grantor)
def check_resource_prefix(user, group, resource, prefix, mapper, grantor): if not resource.title.startswith(prefix): print(" UNSHARING {} {}: prefix not {} (UNSHARING)".format( resource.short_id, resource.title.encode('ascii', 'ignore'), prefix)) # not in the user's resources UserResourcePrivilege.unshare(resource=resource, user=user, grantor=grantor) # not in the group's resources GroupResourcePrivilege.unshare(resource=resource, group=group, grantor=grantor) # Where does it really go? new_prefix = resource.title.split(" ")[0] if new_prefix in mapper: new_username = mapper[new_prefix][0] new_groupname = mapper[new_prefix][1] new_user = User.objects.get(username=new_username) new_group = Group.objects.get(name=new_groupname) print(" SHARING {} {} with user={} group={}".format( resource.short_id, resource.title.encode('ascii', 'ignore'), new_username, new_groupname)) UserResourcePrivilege.share(resource=resource, user=new_user, privilege=PrivilegeCodes.OWNER, grantor=grantor) GroupResourcePrivilege.share(resource=resource, group=new_group, privilege=PrivilegeCodes.VIEW, grantor=grantor) else: print(" ERROR {} {} unknown prefix {}".format( resource.short_id, resource.title.encode('ascii', 'ignore'), new_prefix))
def create_resource( resource_type, owner, title, edit_users=None, view_users=None, edit_groups=None, view_groups=None, keywords=(), metadata=None, extra_metadata=None, files=(), create_metadata=True, create_bag=True, unpack_file=False, full_paths={}, auto_aggregate=True, **kwargs): """ Called by a client to add a new resource to HydroShare. The caller must have authorization to write content to HydroShare. The pid for the resource is assigned by HydroShare upon inserting the resource. The create method returns the newly-assigned pid. REST URL: POST /resource Parameters: Returns: The newly created resource Return Type: BaseResource resource object Note: The calling user will automatically be set as the owner of the created resource. Implementation notes: 1. pid is called short_id. This is because pid is a UNIX term for Process ID and could be confusing. 2. return type is an instance of hs_core.models.BaseResource class. This is for efficiency in the native API. The native API should return actual instance rather than IDs wherever possible to avoid repeated lookups in the database when they are unnecessary. 3. resource_type is a string: see parameter list :param resource_type: string. the type of the resource such as GenericResource :param owner: email address, username, or User instance. The owner of the resource :param title: string. the title of the resource :param edit_users: list of email addresses, usernames, or User instances who will be given edit permissions :param view_users: list of email addresses, usernames, or User instances who will be given view permissions :param edit_groups: list of group names or Group instances who will be given edit permissions :param view_groups: list of group names or Group instances who will be given view permissions :param keywords: string list. list of keywords to add to the resource :param metadata: list of dicts containing keys (element names) and corresponding values as dicts { 'creator': {'name':'John Smith'}}. :param extra_metadata: one dict containing keys and corresponding values { 'Outlet Point Latitude': '40', 'Outlet Point Longitude': '-110'}. :param files: list of Django File or UploadedFile objects to be attached to the resource :param create_bag: whether to create a bag for the newly created resource or not. By default, the bag is created. :param unpack_file: boolean. If files contains a single zip file, and unpack_file is True, the unpacked contents of the zip file will be added to the resource instead of the zip file. :param full_paths: Optional. A map of paths keyed by the correlating resource file. When this parameter is provided, a file will be placed at the path specified in the map. :param auto_aggregate: boolean, defaults to True. Find and create aggregations during resource creation. :param kwargs: extra arguments to fill in required values in AbstractResource subclasses :return: a new resource which is an instance of BaseResource with specificed resource_type. """ with transaction.atomic(): cls = check_resource_type(resource_type) owner = utils.user_from_id(owner) # get the metadata class specific to resource type to set resource # content_object (metadata) attribute metadata_class = cls.get_metadata_class() metadata_obj = metadata_class() metadata_obj.save() # create the resource resource = cls.objects.create( resource_type=resource_type, user=owner, creator=owner, title=title, last_changed_by=owner, in_menus=[], content_object=metadata_obj, **kwargs ) resource.resource_type = resource_type # by default make resource private resource.set_slug('resource{0}{1}'.format('/', resource.short_id)) resource.save() if not metadata: metadata = [] if extra_metadata is not None: resource.extra_metadata = extra_metadata resource.save() # by default resource is private resource_access = ResourceAccess(resource=resource) resource_access.save() # use the built-in share routine to set initial provenance. UserResourcePrivilege.share(resource=resource, grantor=owner, user=owner, privilege=PrivilegeCodes.OWNER) resource_labels = ResourceLabels(resource=resource) resource_labels.save() if edit_users: for user in edit_users: user = utils.user_from_id(user) owner.uaccess.share_resource_with_user(resource, user, PrivilegeCodes.CHANGE) if view_users: for user in view_users: user = utils.user_from_id(user) owner.uaccess.share_resource_with_user(resource, user, PrivilegeCodes.VIEW) if edit_groups: for group in edit_groups: group = utils.group_from_id(group) owner.uaccess.share_resource_with_group(resource, group, PrivilegeCodes.CHANGE) if view_groups: for group in view_groups: group = utils.group_from_id(group) owner.uaccess.share_resource_with_group(resource, group, PrivilegeCodes.VIEW) # set quota of this resource to this creator # quota holder has to be set before the files are added in order for real time iRODS # quota micro-services to work resource.set_quota_holder(owner, owner) if create_metadata: # prepare default metadata utils.prepare_resource_default_metadata(resource=resource, metadata=metadata, res_title=title) for element in metadata: # here k is the name of the element # v is a dict of all element attributes/field names and field values k, v = element.items()[0] resource.metadata.create_element(k, **v) for keyword in keywords: resource.metadata.create_element('subject', value=keyword) resource.title = resource.metadata.title.value resource.save() if len(files) == 1 and unpack_file and zipfile.is_zipfile(files[0]): # Add contents of zipfile as resource files asynchronously # Note: this is done asynchronously as unzipping may take # a long time (~15 seconds to many minutes). add_zip_file_contents_to_resource_async(resource, files[0]) else: # Add resource file(s) now # Note: this is done synchronously as it should only take a # few seconds. We may want to add the option to do this # asynchronously if the file size is large and would take # more than ~15 seconds to complete. add_resource_files(resource.short_id, *files, full_paths=full_paths, auto_aggregate=auto_aggregate) if create_bag: hs_bagit.create_bag(resource) # set the resource to private resource.setAVU('isPublic', resource.raccess.public) # set the resource type (which is immutable) resource.setAVU("resourceType", resource._meta.object_name) return resource
def handle(self, *args, **options): national_user = User.objects.get(username='******') czo_community = Community.objects.get(name='CZO National Community') czo_mapper = {} for czo in czo_setup: # index by prefix czo_mapper[czo[2]] = czo # check each group in turn for czo in czo_setup: czo_username = czo[0] czo_groupname = czo[1] czo_prefix = czo[2] # prefix for all titles for this group. print("CHECKING user {} against group {}".format( czo_username, czo_groupname)) czo_user = User.objects.get(username=czo_username) czo_group = Group.objects.get(name=czo_groupname) user_resources = set( BaseResource.objects.filter(r2urp__user=czo_user)) print(" There are {} user resources".format(len(user_resources))) # for r in user_resources: # print(" {} {}".format(r.short_id, r.title.encode('ascii', 'ignore'))) group_resources = set( BaseResource.objects.filter(r2grp__group=czo_group)) print(" There are {} group resources".format( len(group_resources))) # for r in group_resources: # print(" {} {}".format(r.short_id, r.title.encode('ascii', 'ignore'))) # check that group is in the community if not Community.objects.filter(c2gcp__community=czo_community, c2gcp__group=czo_group).exists(): print(" SHARING group {} with community {}".format( czo_group.name, czo_community.name)) # fix it NOW GroupCommunityPrivilege.share(group=czo_group, community=czo_community, privilege=PrivilegeCodes.VIEW, grantor=national_user) # check whether all resources are owned by czo national for r in user_resources | group_resources: if not UserResourcePrivilege.objects.filter( user=national_user, privilege=PrivilegeCodes.OWNER, resource=r).exists(): print(" SHARING {} {} with czo national user".format( r.short_id, r.title.encode('ascii', 'ignore'))) UserResourcePrivilege.share(user=national_user, resource=r, privilege=PrivilegeCodes.OWNER, grantor=national_user) # set quota holder to CZO national set_quota_holder(r, national_user) # Now everything is owned by CZO national so we can remove other owners safely. if czo_user != national_user: # Check that all resources have the appropriate prefix for r in user_resources | group_resources: # or r in user_resources for non-czo check_resource_prefix(czo_user, czo_group, r, czo_prefix, czo_mapper, national_user) # refresh for user and group changes from above user_resources = set( BaseResource.objects.filter(r2urp__user=czo_user)) group_resources = set( BaseResource.objects.filter(r2grp__group=czo_group)) # Now every resource is filed in the appropriate group, # and non-matching resources are owned by CZO National. # group owner should own all group resources and vice versa. # This will only pick up changes for resources that had the proper prefix. if len(user_resources - group_resources) != 0: print( " The following user resources are not group resources" ) for r in (user_resources - group_resources): check_resource_group(czo_group, r, national_user) # refresh group membership group_resources = set( BaseResource.objects.filter(r2grp__group=czo_group)) if len(group_resources - user_resources) != 0: print( " The following group resources are not user resources:" ) for r in (group_resources - user_resources): check_resource_owners(national_user, czo_user, r, national_user) # refresh ownership user_resources = set( BaseResource.objects.filter(r2urp__user=czo_user)) else: # czo national user and group only runs this clause # no assumption that user resources and group resources are the same. # * user resources are all resources. # * group resources are those that come from multiple sources. # Check that all resources have the appropriate prefix for r in group_resources: # no user_resources because that's everything check_resource_prefix(czo_user, czo_group, r, czo_prefix, czo_mapper, national_user) # pick up changes from above group_resources = set( BaseResource.objects.filter(r2grp__group=czo_group)) for r in group_resources: check_resource_group(czo_group, r, national_user) # pick up changes from above group_resources = set( BaseResource.objects.filter(r2grp__group=czo_group)) for r in group_resources: check_resource_owners(national_user, czo_user, r, national_user)
def create_resource(resource_type, owner, title, edit_users=None, view_users=None, edit_groups=None, view_groups=None, keywords=(), metadata=None, extra_metadata=None, files=(), source_names=[], source_sizes=[], fed_res_path='', move=False, is_file_reference=False, create_metadata=True, create_bag=True, unpack_file=False, **kwargs): """ Called by a client to add a new resource to CommonsShare. The caller must have authorization to write content to CommonsShare. The pid for the resource is assigned by CommonsShare upon inserting the resource. The create method returns the newly-assigned pid. REST URL: POST /resource Parameters: Returns: The newly created resource Return Type: BaseResource resource object Note: The calling user will automatically be set as the owner of the created resource. Implementation notes: 1. pid is called short_id. This is because pid is a UNIX term for Process ID and could be confusing. 2. return type is an instance of hs_core.models.BaseResource class. This is for efficiency in the native API. The native API should return actual instance rather than IDs wherever possible to avoid repeated lookups in the database when they are unnecessary. 3. resource_type is a string: see parameter list :param resource_type: string. the type of the resource such as GenericResource :param owner: email address, username, or User instance. The owner of the resource :param title: string. the title of the resource :param edit_users: list of email addresses, usernames, or User instances who will be given edit permissions :param view_users: list of email addresses, usernames, or User instances who will be given view permissions :param edit_groups: list of group names or Group instances who will be given edit permissions :param view_groups: list of group names or Group instances who will be given view permissions :param keywords: string list. list of keywords to add to the resource :param metadata: list of dicts containing keys (element names) and corresponding values as dicts { 'creator': {'name':'John Smith'}}. :param extra_metadata: one dict containing keys and corresponding values { 'Outlet Point Latitude': '40', 'Outlet Point Longitude': '-110'}. :param files: list of Django File or UploadedFile objects to be attached to the resource :param source_names: a list of file names from a federated zone to be used to create the resource in the federated zone, default is empty list :param source_sizes: a list of file sizes corresponding to source_names if if_file_reference is True; otherwise, it is not of any use and should be empty. :param fed_res_path: the federated zone path in the format of /federation_zone/home/localHydroProxy that indicate where the resource is stored, default is empty string :param move: a value of False or True indicating whether the content files should be erased from the source directory. default is False. :param is_file_reference: a value of False or True indicating whether the files stored in source_files are references to external files without being physically stored in HydroShare internally. default is False. :param create_bag: whether to create a bag for the newly created resource or not. By default, the bag is created. :param unpack_file: boolean. If files contains a single zip file, and unpack_file is True, the unpacked contents of the zip file will be added to the resource instead of the zip file. :param kwargs: extra arguments to fill in required values in AbstractResource subclasses :return: a new resource which is an instance of BaseResource with specificed resource_type. """ if __debug__: assert (isinstance(source_names, list)) with transaction.atomic(): cls = check_resource_type(resource_type) owner = utils.user_from_id(owner) # create the resource resource = cls.objects.create(resource_type=resource_type, user=owner, creator=owner, title=title, last_changed_by=owner, in_menus=[], **kwargs) resource.resource_type = resource_type # by default make resource private resource.set_slug('resource{0}{1}'.format('/', resource.short_id)) resource.save() if not metadata: metadata = [] if extra_metadata is not None: resource.extra_metadata = extra_metadata resource.save() fed_zone_home_path = '' if fed_res_path: resource.resource_federation_path = fed_res_path fed_zone_home_path = fed_res_path resource.save() # TODO: It would be safer to require an explicit zone path rather than harvesting file path elif len(source_names) > 0 and fed_res_path: fed_zone_home_path = utils.get_federated_zone_home_path( source_names[0]) resource.resource_federation_path = fed_zone_home_path resource.save() if len(files) == 1 and unpack_file and zipfile.is_zipfile(files[0]): # Add contents of zipfile as resource files asynchronously # Note: this is done asynchronously as unzipping may take # a long time (~15 seconds to many minutes). add_zip_file_contents_to_resource_async(resource, files[0]) else: # Add resource file(s) now # Note: this is done synchronously as it should only take a # few seconds. We may want to add the option to do this # asynchronously if the file size is large and would take # more than ~15 seconds to complete. add_resource_files(resource.short_id, *files, source_names=source_names, source_sizes=source_sizes, move=move, is_file_reference=is_file_reference) # by default resource is private resource_access = ResourceAccess(resource=resource) resource_access.save() # use the built-in share routine to set initial provenance. UserResourcePrivilege.share(resource=resource, grantor=owner, user=owner, privilege=PrivilegeCodes.OWNER) resource_labels = ResourceLabels(resource=resource) resource_labels.save() if edit_users: for user in edit_users: user = utils.user_from_id(user) owner.uaccess.share_resource_with_user(resource, user, PrivilegeCodes.CHANGE) if view_users: for user in view_users: user = utils.user_from_id(user) owner.uaccess.share_resource_with_user(resource, user, PrivilegeCodes.VIEW) if edit_groups: for group in edit_groups: group = utils.group_from_id(group) owner.uaccess.share_resource_with_group( resource, group, PrivilegeCodes.CHANGE) if view_groups: for group in view_groups: group = utils.group_from_id(group) owner.uaccess.share_resource_with_group( resource, group, PrivilegeCodes.VIEW) if create_metadata: # prepare default metadata utils.prepare_resource_default_metadata(resource=resource, metadata=metadata, res_title=title) for element in metadata: # here k is the name of the element # v is a dict of all element attributes/field names and field values k, v = element.items()[0] resource.metadata.create_element(k, **v) for keyword in keywords: resource.metadata.create_element('subject', value=keyword) resource.title = resource.metadata.title.value resource.save() if create_bag: hs_bagit.create_bag(resource) # set the resource to private resource.setAVU('isPublic', resource.raccess.public) # set the resource type (which is immutable) resource.setAVU("resourceType", resource._meta.object_name) # set quota of this resource to this creator resource.set_quota_holder(owner, owner) return resource