def datasets_with_no_organization_cannot_be_private(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: dataset_id = data.get(('id', )) owner_org = data.get(('owner_org', )) private = data[key] is True check_passed = True if not dataset_id and private and not owner_org: # When creating a dataset, enforce it directly check_passed = False elif dataset_id and private and not owner_org: # Check if the dataset actually has an owner_org, even if not provided try: dataset_dict = logic.get_action('package_show')({}, { 'id': dataset_id }) if not dataset_dict.get('owner_org'): check_passed = False except logic.NotFound: check_passed = False if not check_passed: errors[key].append( _("Datasets with no organization can't be private."))
def convert(converter: Callable[..., Any], key: FlattenKey, converted_data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: try: nargs = converter.__code__.co_argcount except AttributeError: raise TypeError(f"{converter.__name__} cannot be used as validator " "because it is not a user-defined function") if nargs == 1: params = (converted_data.get(key), ) elif nargs == 2: params = (converted_data.get(key), context) elif nargs == 4: params = (key, converted_data, errors, context) else: raise TypeError("Wrong number of arguments for " f"{converter.__name__}(expected 1, 2 or 4): {nargs}") try: value = converter(*params) # 4-args version sets value internally if nargs != 4: converted_data[key] = value return except Invalid as e: errors[key].append(e.error) return
def callable(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): value = data.get(key) other_value = data.get(key[:-1] + (other_key, )) if (not value or value is missing and not other_value or other_value is missing): errors[key].append(_('Missing value')) raise StopOnError
def owner_org_validator(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: value = data.get(key) if value is missing or value is None: if not authz.check_config_permission('create_unowned_dataset'): raise Invalid(_('An organization must be provided')) data.pop(key, None) raise df.StopOnError model = context['model'] user = model.User.get(context['user']) package = context.get('package') if value == '': if not authz.check_config_permission('create_unowned_dataset'): raise Invalid(_('An organization must be provided')) return if (authz.check_config_permission('allow_dataset_collaborators') and not authz.check_config_permission( 'allow_collaborators_to_change_owner_org')): if package and user and not user.sysadmin: is_collaborator = authz.user_is_collaborator_on_dataset( user.id, package.id, ['admin', 'editor']) if is_collaborator: # User is a collaborator, check if it's also a member with # edit rights of the current organization (redundant, but possible) user_orgs = logic.get_action('organization_list_for_user')( { 'ignore_auth': True }, { 'id': user.id, 'permission': 'update_dataset' }) user_is_org_member = package.owner_org in [ org['id'] for org in user_orgs ] if data.get( key) != package.owner_org and not user_is_org_member: raise Invalid( _('You cannot move this dataset to another organization' )) group = model.Group.get(value) if not group: raise Invalid(_('Organization does not exist')) group_id = group.id if not package or (package and package.owner_org != group_id): # This is a new dataset or we are changing the organization if not context.get(u'ignore_auth', False) and (not user or not ( user.sysadmin or authz.has_user_permission_for_group_or_org( group_id, user.name, 'create_dataset'))): raise Invalid(_('You cannot add a dataset to this organization')) data[key] = group_id
def user_password_not_empty(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: '''Only check if password is present if the user is created via action API. If not, user_both_passwords_entered will handle the validation''' # sysadmin may provide password_hash directly for importing users if (data.get(('password_hash', ), missing) is not missing and authz.is_sysadmin(context.get('user'))): return if not ('password1', ) in data and not ('password2', ) in data: password = data.get(('password', ), None) if not password: errors[key].append(_('Missing value'))
def package_name_validator(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: model = context['model'] session = context['session'] package = context.get('package') query = session.query(model.Package.id).filter( model.Package.name == data[key], model.Package.state != State.DELETED, ) if package: package_id: Union[Optional[str], Missing] = package.id else: package_id = data.get(key[:-1] + ('id', )) if package_id and package_id is not missing: query = query.filter(model.Package.id != package_id) if session.query(query.exists()).scalar(): errors[key].append(_('That URL is already in use.')) value = data[key] if len(value) < PACKAGE_NAME_MIN_LENGTH: raise Invalid( _('Name "%s" length is less than minimum %s') % (value, PACKAGE_NAME_MIN_LENGTH)) if len(value) > PACKAGE_NAME_MAX_LENGTH: raise Invalid( _('Name "%s" length is more than maximum %s') % (value, PACKAGE_NAME_MAX_LENGTH))
def not_empty(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: value = data.get(key) if not value or value is missing: errors[key].append(_('Missing value')) raise StopOnError
def func(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): new_tags = data.get(key) if not new_tags: return if isinstance(new_tags, str): new_tags = [new_tags] # get current number of tags n = 0 for k in data.keys(): if k[0] == 'tags': n = max(n, k[1] + 1) v = model.Vocabulary.get(vocab) if not v: raise df.Invalid(_('Tag vocabulary "%s" does not exist') % vocab) context['vocabulary'] = v for tag in new_tags: validators.tag_in_vocabulary_validator(tag, context) for num, tag in enumerate(new_tags): data[('tags', num + n, 'name')] = tag data[('tags', num + n, 'vocabulary_id')] = v.id
def ignore_empty(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: value = data.get(key) if value is missing or not value: data.pop(key, None) raise StopOnError
def list_of_strings_or_lists(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): value = data.get(key) if not isinstance(value, list): raise df.Invalid('Not a list') for x in value: if not isinstance(x, str) and not isinstance(x, list): raise df.Invalid('%s: %s' % ('Neither a string nor a list', x))
def list_of_strings(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: value = data.get(key) if not isinstance(value, list): raise Invalid(_('Not a list')) for x in value: if not isinstance(x, str): raise Invalid('%s: %s' % (_('Not a string'), x))
def free_tags_only(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: tag_number = key[1] if not data.get(('tags', tag_number, 'vocabulary_id')): return for k in list(data.keys()): if k[0] == 'tags' and k[1] == tag_number: del data[k]
def user_both_passwords_entered(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: password1 = data.get(('password1', ), None) password2 = data.get(('password2', ), None) if password1 is None or password1 == '' or \ password2 is None or password2 == '': errors[('password', )].append(_('Please enter both passwords'))
def user_both_passwords_entered(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: """Ensures that both password and password confirmation is not empty """ password1 = data.get(('password1', ), None) password2 = data.get(('password2', ), None) if password1 is None or password1 == '' or \ password2 is None or password2 == '': errors[('password', )].append(_('Please enter both passwords'))
def if_empty_guess_format(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: value = data[key] resource_id = data.get(key[:-1] + ('id', )) # if resource_id then an update if (not value or value is Missing) and not resource_id: url = data.get(key[:-1] + ('url', ), '') if not url: return # Uploaded files have only the filename as url, so check scheme to # determine if it's an actual url parsed = urlparse(url) if parsed.scheme and not parsed.path: return mimetype, _encoding = mimetypes.guess_type(url) if mimetype: data[key] = mimetype
def user_passwords_match(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: password1 = data.get(('password1', ), None) password2 = data.get(('password2', ), None) if not password1 == password2: errors[key].append(_('The passwords you entered do not match')) else: #Set correct password data[('password', )] = password1
def email_is_unique(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: '''Validate email is unique''' model = context['model'] session = context['session'] users = session.query(model.User) \ .filter(model.User.email == data[key]).all() # is there is no users with this email it's free if not users: return else: # allow user to update their own email for user in users: if (user.name in (data.get(("name",)), data.get(("id",))) or user.id == data.get(("id",))): return raise Invalid( _('The email address \'{email}\' belongs to a registered user.').format(email=data[key]))
def filter_fields_and_values_should_have_same_length(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: convert_to_list_if_string = converters.convert_to_list_if_string fields = convert_to_list_if_string(data.get(('filter_fields',), [])) values = convert_to_list_if_string(data.get(('filter_values',), [])) if len(fields) != len(values): msg = _('"filter_fields" and "filter_values" should have the same length') errors[('filter_fields',)].append(msg) errors[('filter_values',)].append(msg)
def rename_field(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): index = max([int(k[1]) for k in data.keys() if len(k) == 3 and k[0] == new] + [-1]) for field_name in list(data.keys()): if field_name[0] == old and data.get(field_name): new_field_name = list(field_name) new_field_name[0] = new if len(new_field_name) > 1: new_field_name[1] = int(new_field_name[1]) + index + 1 data[tuple(new_field_name)] = data[field_name] data.pop(field_name)
def prefix_validator(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): out = {} extras = data.get(('__extras', ), {}) # values passed as lists of dicts will have been flattened into __junk junk = df.unflatten(data.get(('__junk', ), {})) for field_name in junk: if not field_name.startswith(prefix): continue extras[field_name] = junk[field_name] for field_name in list(extras): if not field_name.startswith(prefix): continue data[(field_name, )] = extras.pop(field_name) for v in validator_fns: try: df.convert(v, (field_name, ), data, errors, context) except df.StopOnError: break out[field_name[len(prefix):]] = data.pop((field_name, )) data[(prefix, )] = out
def group_name_validator(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: model = context['model'] session = context['session'] group = context.get('group') query = session.query(model.Group.name).filter_by(name=data[key]) if group: group_id: Union[Optional[str], Missing] = group.id else: group_id = data.get(key[:-1] + ('id', )) if group_id and group_id is not missing: query = query.filter(model.Group.id != group_id) result = query.first() if result: errors[key].append(_('Group name already exists in database'))
def not_empty(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: """Ensure value is available in the input and is not empty. .. code-block:: data, errors = tk.navl_validate( {"hello": 0}, {"hello": [not_empty]} ) assert errors == {"hello": [error_message]} """ value = data.get(key) if not value or value is missing: errors[key].append(_('Missing value')) raise StopOnError
def not_missing(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: """Ensure value is not missing from the input, but may be empty. .. code-block:: data, errors = tk.navl_validate( {}, {"hello": [not_missing]} ) assert errors == {"hello": [error_message]} """ value = data.get(key) if value is missing: errors[key].append(_('Missing value')) raise StopOnError
def ignore_empty(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: """Skip the rest of validators if the value is empty or missing. .. code-block:: data, errors = tk.navl_validate( {"hello": ""}, {"hello": [ignore_empty, isodate]} ) assert data == {} assert not errors """ value = data.get(key) if value is missing or not value: data.pop(key, None) raise StopOnError
def url_validator(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: ''' Checks that the provided value (if it is present) is a valid URL ''' url = data.get(key, None) if not url: return try: pieces = urlparse(url) if all([pieces.scheme, pieces.netloc]) and \ set(pieces.netloc) <= set(string.ascii_letters + string.digits + '-.') and \ pieces.scheme in ['http', 'https']: return except ValueError: # url is invalid pass errors[key].append(_('Please provide a valid URL'))
def filter_fields_and_values_exist_and_are_valid(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> Any: convert_to_list_if_string = converters.convert_to_list_if_string fields = convert_to_list_if_string(data.get(('filter_fields',))) values = convert_to_list_if_string(data.get(('filter_values',))) if not fields: errors[('filter_fields',)].append(_('"filter_fields" is required when ' '"filter_values" is filled')) if not values: errors[('filter_values',)].append(_('"filter_values" is required when ' '"filter_fields" is filled')) filters = collections.defaultdict(list) for field, value in zip(fields, values): filters[field].append(value) data[('filters',)] = dict(filters)
def ignore_missing(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context) -> None: '''If the key is missing from the data, ignore the rest of the key's schema. By putting ignore_missing at the start of the schema list for a key, you can allow users to post a dict without the key and the dict will pass validation. But if they post a dict that does contain the key, then any validators after ignore_missing in the key's schema list will be applied. :raises ckan.lib.navl.dictization_functions.StopOnError: if ``data[key]`` is :py:data:`ckan.lib.navl.dictization_functions.missing` or ``None`` :returns: ``None`` ''' value = data.get(key) if value is missing or value is None: data.pop(key, None) raise StopOnError
def list_of_strings_or_string(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): value = data.get(key) if isinstance(value, str): return list_of_strings_or_lists(key, data, errors, context)
def callable(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): value = data.get(key) if not value or value is missing: data[key] = data[key[:-1] + (other_key, )]
def callable(key: FlattenKey, data: FlattenDataDict, errors: FlattenErrorDict, context: Context): value = data.get(key) if value is None or value == '' or value is missing: data[key] = default_value