async def _validate(self, schema_name, data, id=None): verrors = ValidationErrors() await self._ensure_unique(verrors, schema_name, "name", data["name"], id) if data["type"] not in TYPES: verrors.add(f"{schema_name}.type", "Invalid type") raise verrors else: type = TYPES[data["type"]] attributes_verrors = validate_attributes(type.credentials_schema, data) verrors.add_child(f"{schema_name}.attributes", attributes_verrors) if verrors: raise verrors await type.validate_and_pre_save(self.middleware, verrors, f"{schema_name}.attributes", data["attributes"]) if verrors: raise verrors
async def validate(self, data, schema_name): verrors = ValidationErrors() if data.get('protocol') == 'httphttps' and data.get('tcpport') == data.get('tcpportssl'): verrors.add( f"{schema_name}.tcpportssl", 'The HTTP and HTTPS ports cannot be the same!' ) cert_ssl = data.get('certssl') or 0 if data.get('protocol') != 'http': if not cert_ssl: verrors.add( f"{schema_name}.certssl", 'WebDAV SSL protocol specified without choosing a certificate' ) else: verrors.extend((await self.middleware.call( 'certificate.cert_services_validation', cert_ssl, f'{schema_name}.certssl', False ))) if verrors: raise verrors return data
def validate_credentials(data): verrors = ValidationErrors() if data.get('api_token'): if data.get('cloudflare_email'): verrors.add('cloudflare_email', 'Should not be specified when using "api_token".') if data.get('api_key'): verrors.add('api_key', 'Should not be specified when using "api_token".') elif data.get('cloudflare_email') or data.get('api_key'): if not data.get('cloudflare_email'): verrors.add( 'cloudflare_email', 'Attribute is required when using a Global API Key (should be associated with Cloudflare account).' ) if not data.get('api_key'): verrors.add( 'api_key', 'Attribute is required when using a Global API Key.') else: verrors.add( 'api_token', 'Attribute must be specified when Global API Key is not specified.' ) verrors.check()
async def do_create(self, data): verrors = ValidationErrors() # We normalize the label data['label'] = data['label'].upper() if await self.query([['id', '=', data['label']]]): verrors.add('catalog_create.label', 'A catalog with specified label already exists', errno=errno.EEXIST) if await self.query([['repository', '=', data['repository']], ['branch', '=', data['branch']]]): for k in ('repository', 'branch'): verrors.add( f'catalog_create.{k}', 'A catalog with same repository/branch already exists', errno=errno.EEXIST ) verrors.check() if not data.pop('force'): # We will validate the catalog now to ensure it's valid wrt contents / format path = os.path.join( TMP_IX_APPS_DIR, 'validate_catalogs', convert_repository_to_path(data['repository'], data['branch']) ) try: await self.middleware.call('catalog.update_git_repository', {**data, 'location': path}, True) await self.middleware.call('catalog.validate_catalog_from_path', path) finally: await self.middleware.run_in_thread(shutil.rmtree, path, ignore_errors=True) await self.middleware.call('datastore.insert', self._config.datastore, data) asyncio.ensure_future(self.middleware.call('catalog.sync', data['label'])) return await self.get_instance(data['label'])
async def validate(self, tunable, schema_name): sysctl_re = \ re.compile('[a-z][a-z0-9_]+\.([a-z0-9_]+\.)*[a-z0-9_]+', re.I) loader_re = \ re.compile('[a-z][a-z0-9_]+\.*([a-z0-9_]+\.)*[a-z0-9_]+', re.I) verrors = ValidationErrors() tun_var = tunable['var'].lower() tun_type = tunable['type'].lower() if tun_type == 'loader' or tun_type == 'rc': err_msg = "Value can start with a letter and end with an alphanumeric. Aphanumeric and underscore" \ " characters are allowed" else: err_msg = 'Value can start with a letter and end with an alphanumeric. A period (.) once is a must.' \ ' Alphanumeric and underscore characters are allowed' if ( tun_type in ('loader', 'rc') and not loader_re.match(tun_var) ) or ( tun_type == 'sysctl' and not sysctl_re.match(tun_var) ): verrors.add(f"{schema_name}.var", err_msg) if verrors: raise verrors
async def clean(self, tunable, schema_name, old=None): verrors = ValidationErrors() skip_dupe = False tun_comment = tunable.get('comment') tun_value = tunable['value'] tun_var = tunable['var'] if tun_comment is not None: tunable['comment'] = tun_comment.strip() if '"' in tun_value or "'" in tun_value: verrors.add(f"{schema_name}.value", 'Quotes in value are not allowed') if schema_name == 'tunable_update' and old: old_tun_var = old['var'] if old_tun_var == tun_var: # They aren't trying to change to a new name, just updating skip_dupe = True if not skip_dupe: tun_vars = await self.middleware.call( 'datastore.query', self._config.datastore, [('tun_var', '=', tun_var)]) if tun_vars: verrors.add(f"{schema_name}.value", 'This variable already exists') if verrors: raise verrors return tunable
async def validate(self, tunable, schema_name): sysctl_re = \ re.compile('[a-z][a-z0-9_]+\.([a-z0-9_]+\.)*[a-z0-9_]+', re.I) loader_re = \ re.compile('[a-z][a-z0-9_]+\.*([a-z0-9_]+\.)*[a-z0-9_]+', re.I) verrors = ValidationErrors() tun_var = tunable['var'].lower() tun_type = tunable['type'].lower() if tun_type == 'loader' or tun_type == 'rc': err_msg = "Value can start with a letter and end with an alphanumeric. Aphanumeric and underscore" \ " characters are allowed" else: err_msg = 'Value can start with a letter and end with an alphanumeric. A period (.) once is a must.' \ ' Alphanumeric and underscore characters are allowed' if ( tun_type in ('loader', 'rc') and not loader_re.match(tun_var) ) or ( tun_type == 'sysctl' and not sysctl_re.match(tun_var) ): verrors.add(f"{schema_name}.var", err_msg) if verrors: raise verrors
async def clean(self, tunable, schema_name, old=None): verrors = ValidationErrors() skip_dupe = False tun_comment = tunable.get('comment') tun_value = tunable['value'] tun_var = tunable['var'] if tun_comment is not None: tunable['comment'] = tun_comment.strip() if '"' in tun_value or "'" in tun_value: verrors.add(f"{schema_name}.value", 'Quotes in value are not allowed') if schema_name == 'tunable_update' and old: old_tun_var = old['var'] if old_tun_var == tun_var: # They aren't trying to change to a new name, just updating skip_dupe = True if not skip_dupe: tun_vars = await self.middleware.call( 'datastore.query', self._config.datastore, [('tun_var', '=', tun_var)]) if tun_vars: verrors.add(f"{schema_name}.value", 'This variable already exists') if verrors: raise verrors return tunable
async def do_create(self, data): data.update({ 'kind': 'VolumeSnapshot', 'apiVersion': f'snapshot.storage.k8s.io/{self.VERSION}' }) namespace = data.pop('namespace') verrors = ValidationErrors() if not await self.middleware.call( 'k8s.zfs.snapshotclass.query', [['metadata.name', '=', data['spec']['volumeSnapshotClassName']]] ): verrors.add( 'zfs_snapshot_create.spec.volumeSnapshotClassName', 'Specified volumeSnapshotClassName does not exist.' ) if not await self.middleware.call( 'k8s.pvc.query', [ ['metadata.name', '=', data['spec']['source']['persistentVolumeClaimName']], ['metadata.namespace', '=', namespace] ] ): verrors.add( 'zfs_snapshot_create.spec.source.persistentVolumeClaimName', f'Specified persistentVolumeClaimName does not exist in {namespace}.' ) verrors.check() async with api_client() as (api, context): await context['custom_object_api'].create_namespaced_custom_object( group=self.GROUP, version=self.VERSION, plural=self.PLURAL, namespace=namespace, body=data ) return data
async def validate(self, data, schema_name): verrors = ValidationErrors() if data.get('protocol') == 'httphttps' and data.get('tcpport') == data.get('tcpportssl'): verrors.add( f"{schema_name}.tcpportssl", 'The HTTP and HTTPS ports cannot be the same!' ) cert_ssl = data.get('certssl') or 0 if data.get('protocol') != 'http': if not cert_ssl: verrors.add( f"{schema_name}.certssl", 'WebDAV SSL protocol specified without choosing a certificate' ) else: verrors.extend((await self.middleware.call( 'certificate.cert_services_validation', cert_ssl, f'{schema_name}.certssl', False ))) if verrors: raise verrors return data
async def do_create(self, data): """ Create a Tunable. """ verrors = ValidationErrors() if await self.middleware.call('tunable.query', [('var', '=', data['var'])]): verrors.add( 'tunable.create', f'Tunable {data["var"]!r} already exists in database.', errno.EEXIST) if data['var'] not in await self.middleware.call( 'tunable.get_system_tunables'): verrors.add('tunable.create', f'Tunable {data["var"]!r} does not exist in kernel.', errno.ENOENT) verrors.check() data['orig_value'] = await self.middleware.call( 'tunable.get_or_set', data['var']) if (comment := data.get('comment', '').strip()): data['comment'] = comment
async def do_update(self, data): """ Update settings of SSH daemon service. If `bindiface` is empty it will listen for all available addresses. .. examples(websocket):: Make sshd listen only to igb0 interface. :::javascript { "id": "6841f242-840a-11e6-a437-00e04d680384", "msg": "method", "method": "ssh.update", "params": [{ "bindiface": ["igb0"] }] } """ old = await self.config() new = old.copy() new.update(data) if new['bindiface']: verrors = ValidationErrors() iface_choices = await self.middleware.call('ssh.bindiface_choices') invalid_ifaces = list( filter(lambda x: x not in iface_choices, new['bindiface'])) if invalid_ifaces: verrors.add( 'ssh_update.bindiface', f'The following interfaces are not valid: {", ".join(invalid_ifaces)}', ) verrors.check() await self._update_service(old, new) keyfile = "/usr/local/etc/ssh/ssh_host_ecdsa_key.pub" if os.path.exists(keyfile): with open(keyfile, "rb") as f: pubkey = f.read().strip().split(None, 3)[1] decoded_key = base64.b64decode(pubkey) key_digest = hashlib.sha256(decoded_key).digest() ssh_fingerprint = (b"SHA256:" + base64.b64encode(key_digest).replace( b"=", b"")).decode("utf-8") syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_USER) syslog.syslog( syslog.LOG_ERR, 'ECDSA Fingerprint of the SSH KEY: ' + ssh_fingerprint) syslog.closelog() return new
def _validate(self, schema_name, data): verrors = ValidationErrors() if (':' in data['destination']) != (':' in data['gateway']): verrors.add(f'{schema_name}.destination', 'Destination and gateway address families must match') if verrors: raise verrors
async def common_validation(self, data, schema_name): verrors = ValidationErrors() if data['authenticator'] not in self.schemas: verrors.add( f'{schema_name}.authenticator', f'System does not support {data["authenticator"]} as an Authenticator' ) else: verrors = validate_attributes(self.schemas[data['authenticator']], data) if verrors: raise verrors
async def common_validation(self, data, schema_name): verrors = ValidationErrors() if data['authenticator'] not in self.schemas: verrors.add( f'{schema_name}.authenticator', f'System does not support {data["authenticator"]} as an Authenticator' ) else: verrors = validate_attributes(self.schemas[data['authenticator']], data) if verrors: raise verrors
async def validate_data(self, data, schema): verrors = ValidationErrors() await self.validate_path_field(data, schema, verrors) if not data['name'].isalnum(): verrors.add(f'{schema}.name', 'Only alphanumeric characters are allowed') verrors.check() if not os.path.exists(data[self.path_field]): os.makedirs(data[self.path_field])
async def common_validation(self, data, schema_name): verrors = ValidationErrors() if data['authenticator'] not in await self.middleware.call('acme.dns.authenticator.get_authenticator_schemas'): verrors.add( f'{schema_name}.authenticator', f'System does not support {data["authenticator"]} as an Authenticator' ) else: authenticator_obj = await self.middleware.call('acme.dns.authenticator.get_authenticator_internal', data) authenticator_obj.validate_credentials(data['attributes']) verrors.check()
async def do_update(self, data): """ Update settings of SSH daemon service. If `bindiface` is empty it will listen for all available addresses. .. examples(websocket):: Make sshd listen only to igb0 interface. :::javascript { "id": "6841f242-840a-11e6-a437-00e04d680384", "msg": "method", "method": "ssh.update", "params": [{ "bindiface": ["igb0"] }] } """ old = await self.config() new = old.copy() new.update(data) if new['bindiface']: verrors = ValidationErrors() iface_choices = await self.middleware.call('ssh.bindiface_choices') invalid_ifaces = list(filter(lambda x: x not in iface_choices, new['bindiface'])) if invalid_ifaces: verrors.add( 'ssh_update.bindiface', f'The following interfaces are not valid: {", ".join(invalid_ifaces)}', ) verrors.check() await self._update_service(old, new) keyfile = "/usr/local/etc/ssh/ssh_host_ecdsa_key.pub" if os.path.exists(keyfile): with open(keyfile, "rb") as f: pubkey = f.read().strip().split(None, 3)[1] decoded_key = base64.b64decode(pubkey) key_digest = hashlib.sha256(decoded_key).digest() ssh_fingerprint = (b"SHA256:" + base64.b64encode(key_digest).replace(b"=", b"")).decode("utf-8") syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_USER) syslog.syslog(syslog.LOG_ERR, 'ECDSA Fingerprint of the SSH KEY: ' + ssh_fingerprint) syslog.closelog() return new
async def validate(self, data, schema_name): verrors = ValidationErrors() if data['type'] == 'COMMAND': if not data.get('command'): verrors.add(f'{schema_name}.command', 'This field is required') if data['type'] == 'SCRIPT': if not data.get('script'): verrors.add(f'{schema_name}.script', 'This field is required') if verrors: raise verrors
async def validate(self, data, schema_name): verrors = ValidationErrors() if data['type'] == 'COMMAND': if not data.get('command'): verrors.add(f'{schema_name}.command', 'This field is required') if data['type'] == 'SCRIPT': if not data.get('script'): verrors.add(f'{schema_name}.script', 'This field is required') if verrors: raise verrors
async def common_validation(self, catalog, schema, data): found_trains = set(catalog['trains']) diff = set(data['preferred_trains']) - found_trains verrors = ValidationErrors() if diff: verrors.add( f'{schema}.preferred_trains', f'{", ".join(diff)} trains were not found in catalog.') if not data['preferred_trains']: verrors.add( f'{schema}.preferred_trains', 'At least 1 preferred train must be specified for a catalog.') verrors.check()
async def validate(self, init_shutdown_script, schema_name): verrors = ValidationErrors() if init_shutdown_script["type"] == "COMMAND": if not init_shutdown_script["command"]: verrors.add("%s.command" % schema_name, "This field is required") if init_shutdown_script["type"] == "SCRIPT": if not init_shutdown_script["script"]: verrors.add("%s.script" % schema_name, "This field is required") if verrors: raise verrors
async def validate(self, data, schema_name): verrors = ValidationErrors() if (data.get('protocol') == 'HTTPHTTPS' and data.get('tcpport') == data.get('tcpportssl')): verrors.add(f"{schema_name}.tcpportssl", 'The HTTP and HTTPS ports cannot be the same!') if (data.get('protocol') != 'HTTP' and data.get('certssl') is None): verrors.add( f"{schema_name}.certssl", 'Webdav SSL protocol specified without choosing a certificate') if verrors: raise verrors return data
async def validate(self, data, schema_name): verrors = ValidationErrors() if (data.get('protocol') == 'httphttps' and data.get( 'tcpport') == data.get('tcpportssl')): verrors.add(f"{schema_name}.tcpportssl", 'The HTTP and HTTPS ports cannot be the same!') if (data.get('protocol') != 'http' and data.get('certssl') is None): verrors.add( f"{schema_name}.certssl", 'WebDAV SSL protocol specified without choosing a certificate' ) if verrors: raise verrors return data
async def common_validation(self, data, schema_name, old=None): verrors = ValidationErrors() filters = [['name', '!=', old['name']]] if old else [] filters.append(['name', '=', data['name']]) if await self.query(filters): verrors.add(f'{schema_name}.name', 'Specified name is already in use') if data['authenticator'] not in await self.middleware.call( 'acme.dns.authenticator.get_authenticator_schemas'): verrors.add( f'{schema_name}.authenticator', f'System does not support {data["authenticator"]} as an Authenticator' ) else: authenticator_obj = await self.middleware.call( 'acme.dns.authenticator.get_authenticator_internal', data) authenticator_obj.validate_credentials(data['attributes']) verrors.check()
async def _validate(self, schema_name, data, id=None): verrors = ValidationErrors() await self._ensure_unique(verrors, schema_name, "name", data["name"], id) if data["type"] not in TYPES: verrors.add(f"{schema_name}.type", "Invalid type") raise verrors else: type = TYPES[data["type"]] attributes_verrors = validate_attributes(type.credentials_schema, data) verrors.add_child(f"{schema_name}.attributes", attributes_verrors) if verrors: raise verrors await type.validate_and_pre_save(self.middleware, verrors, f"{schema_name}.attributes", data["attributes"]) if verrors: raise verrors
async def validate_data(self, data, schema): verrors = ValidationErrors() path = data.get('path') if not path: verrors.add( f'{schema}.path', 'This field is required' ) else: await check_path_resides_within_volume(verrors, self.middleware, f'{schema}.path', data['path']) name = data.get('name') if not name: verrors.add( f'{schema}.name', 'This field is required' ) else: if not name.isalnum(): verrors.add( f'{schema}.name', 'Only AlphaNumeric characters are allowed' ) if verrors: raise verrors
async def validate_data(self, data, schema): verrors = ValidationErrors() path = data.get('path') if not path: verrors.add( f'{schema}.path', 'This field is required' ) else: await check_path_resides_within_volume(verrors, self.middleware, f'{schema}.path', data['path']) name = data.get('name') if not name: verrors.add( f'{schema}.name', 'This field is required' ) else: if not name.isalnum(): verrors.add( f'{schema}.name', 'Only AlphaNumeric characters are allowed' ) if verrors: raise verrors if not os.path.exists(path): os.makedirs(path)
async def validate(self, data, schema_name): verrors = ValidationErrors() if data['type'] == 'COMMAND': if not data.get('command'): verrors.add(f'{schema_name}.command', 'This field is required') else: data['script_text'] = '' data['script'] = '' if data['type'] == 'SCRIPT': if data.get('script') and data.get('script_text'): verrors.add(f'{schema_name}.script', 'Only one of two fields should be provided') elif not data.get('script') and not data.get('script_text'): # IDEA may be it's worth putting both fields validations errors to verrors # e.g. # verrors.add(f'{schema_name}.script', 'This field is required') # verrors.add(f'{schema_name}.script_text', 'This field is required') verrors.add(f'{schema_name}.script', "Either 'script' or 'script_text' field is required") elif data.get('script') and not data.get('script_text'): data['command'] = '' data['script_text'] = '' else: data['command'] = '' data['script'] = '' if verrors: raise verrors
async def common_validation(middleware, data, schema, mode): verrors = ValidationErrors() if data['cipher'] and data['cipher'] not in OpenVPN.ciphers(): verrors.add(f'{schema}.cipher', 'Please specify a valid cipher.') if data['authentication_algorithm'] and data[ 'authentication_algorithm'] not in OpenVPN.digests(): verrors.add(f'{schema}.authentication_algorithm', 'Please specify a valid authentication_algorithm.') if data['root_ca']: verrors = await OpenVPN.cert_validation(middleware, data, schema, mode, verrors) if data['tls_crypt_auth_enabled'] and not data['tls_crypt_auth']: verrors.add( f'{schema}.tls_crypt_auth', 'Please provide static key for authentication/encryption of all control ' 'channel packets when tls_crypt_auth_enabled is enabled.') data['tls_crypt_auth'] = None if not data.pop( 'tls_crypt_auth_enabled') else data['tls_crypt_auth'] return verrors, data
async def do_create(self, data): """ `catalog_create.preferred_trains` specifies trains which will be displayed in the UI directly for a user. """ verrors = ValidationErrors() # We normalize the label data['label'] = data['label'].upper() if await self.query([['id', '=', data['label']]]): verrors.add('catalog_create.label', 'A catalog with specified label already exists', errno=errno.EEXIST) if await self.query([['repository', '=', data['repository']], ['branch', '=', data['branch']]]): for k in ('repository', 'branch'): verrors.add( f'catalog_create.{k}', 'A catalog with same repository/branch already exists', errno=errno.EEXIST) verrors.check() if not data['preferred_trains']: data['preferred_trains'] = ['charts'] if not data.pop('force'): # We will validate the catalog now to ensure it's valid wrt contents / format path = os.path.join( TMP_IX_APPS_DIR, 'validate_catalogs', convert_repository_to_path(data['repository'], data['branch'])) try: await self.middleware.call('catalog.update_git_repository', { **data, 'location': path }, True) await self.middleware.call( 'catalog.validate_catalog_from_path', path) await self.common_validation( { 'trains': await self.middleware.call('catalog.get_trains', path) }, 'catalog_create', data) except CallError as e: verrors.add('catalog_create.label', f'Failed to validate catalog: {e}') finally: await self.middleware.run_in_thread(shutil.rmtree, path, ignore_errors=True) verrors.check() await self.middleware.call('datastore.insert', self._config.datastore, data) asyncio.ensure_future( self.middleware.call('catalog.sync', data['label'])) return await self.get_instance(data['label'])
async def do_create(self, job, data): """ `catalog_create.preferred_trains` specifies trains which will be displayed in the UI directly for a user. """ verrors = ValidationErrors() # We normalize the label data['label'] = data['label'].upper() if await self.query([['id', '=', data['label']]]): verrors.add('catalog_create.label', 'A catalog with specified label already exists', errno=errno.EEXIST) if await self.query([['repository', '=', data['repository']], ['branch', '=', data['branch']]]): for k in ('repository', 'branch'): verrors.add( f'catalog_create.{k}', 'A catalog with same repository/branch already exists', errno=errno.EEXIST ) verrors.check() if not data['preferred_trains']: data['preferred_trains'] = ['stable'] if not data.pop('force'): job.set_progress(40, f'Validating {data["label"]!r} catalog') # We will validate the catalog now to ensure it's valid wrt contents / format path = os.path.join( TMP_IX_APPS_DIR, 'validate_catalogs', convert_repository_to_path(data['repository'], data['branch']) ) try: await self.middleware.call('catalog.update_git_repository', {**data, 'location': path}) await self.middleware.call('catalog.validate_catalog_from_path', path) await self.common_validation( {'trains': await self.middleware.call('catalog.retrieve_train_names', path)}, 'catalog_create', data ) except ValidationErrors as ve: verrors.extend(ve) except CallError as e: verrors.add('catalog_create.label', f'Failed to validate catalog: {e}') finally: await self.middleware.run_in_thread(shutil.rmtree, path, ignore_errors=True) else: job.set_progress(50, 'Skipping validation of catalog') verrors.check() job.set_progress(60, 'Completed Validation') await self.middleware.call('datastore.insert', self._config.datastore, data) job.set_progress(70, f'Successfully added {data["label"]!r} catalog') job.set_progress(80, f'Syncing {data["label"]} catalog') sync_job = await self.middleware.call('catalog.sync', data['label']) await sync_job.wait() if sync_job.error: raise CallError(f'Catalog was added successfully but failed to sync: {sync_job.error}') job.set_progress(100, f'Successfully synced {data["label"]!r} catalog') return await self.get_instance(data['label'])
async def validate(self, tunable, schema_name): sysctl_re = \ re.compile('[a-z][a-z0-9_]+\.([a-z0-9_]+\.)*[a-z0-9_]+', re.I) loader_re = \ re.compile('[a-z][a-z0-9_]+\.*([a-z0-9_]+\.)*[a-z0-9_]+', re.I) verrors = ValidationErrors() tun_var = tunable['var'].lower() tun_type = tunable['type'].lower() if tun_type == 'loader' or tun_type == 'rc': err_msg = """Variable name must:<br /> 1. Start with a letter.<br /> 2. End with a letter or number.<br /> 3. Can contain a combination of alphanumeric characters, numbers and/or \ underscores. """ else: err_msg = """Variable name must:<br /> 1. Start with a letter.<br /> 2. Contain at least one period.<br /> 3. End with a letter or number.<br /> 4. Can contain a combination of alphanumeric characters, numbers and/or \ underscores. """ if ( tun_type in ('loader', 'rc') and not loader_re.match(tun_var) ) or ( tun_type == 'sysctl' and not sysctl_re.match(tun_var) ): verrors.add(f"{schema_name}.value", err_msg) if verrors: raise verrors
async def do_update(self, data): """ Update LLDP Service Configuration. `country` is a two letter ISO 3166 country code required for LLDP location support. `location` is an optional attribute specifying the physical location of the host. """ old = await self.config() new = old.copy() new.update(data) verrors = ValidationErrors() if new['country'] not in await self.country_choices(): verrors.add( 'lldp_update.country', f'{new["country"]} not in countries recognized by the system.') verrors.check() await self._update_service(old, new) return new
async def validate(self, data, schema_name): verrors = ValidationErrors() if data.get('protocol') == 'httphttps' and data.get( 'tcpport') == data.get('tcpportssl'): verrors.add(f"{schema_name}.tcpportssl", 'The HTTP and HTTPS ports cannot be the same!') cert_ssl = data.get('certssl') or 0 if data.get('protocol') != 'http': if not cert_ssl: verrors.add( f"{schema_name}.certssl", 'WebDAV SSL protocol specified without choosing a certificate' ) elif not (await self.middleware.call('certificate.query', [['id', '=', cert_ssl]])): verrors.add(f"{schema_name}.certssl", 'Please provide a valid certificate id') if verrors: raise verrors return data
async def common_validation(middleware, data, schema, mode): verrors = ValidationErrors() if data['cipher'] and data['cipher'] not in OpenVPN.ciphers(): verrors.add(f'{schema}.cipher', 'Please specify a valid cipher.') if data['authentication_algorithm'] and data[ 'authentication_algorithm'] not in OpenVPN.digests(): verrors.add(f'{schema}.authentication_algorithm', 'Please specify a valid authentication_algorithm.') root_ca = await middleware.call( 'certificateauthority.query', [['id', '=', data['root_ca']], ['revoked', '=', False]]) if not root_ca: verrors.add( f'{schema}.root_ca', 'Please provide a valid id for Root Certificate Authority which exists on the system ' 'and hasn\'t been revoked.') else: # Validate root ca root_ca = root_ca[0] extensions = root_ca['extensions'] for ext in ('BasicConstraints', 'KeyUsage', 'SubjectKeyIdentifier'): if not extensions.get(ext): verrors.add(f'{schema}.root_ca', f'Root CA must have {ext} extension set.') if 'CA:TRUE' not in (extensions.get('BasicConstraints') or ''): verrors.add( f'{schema}.root_ca', 'Root CA must have CA=TRUE set for BasicConstraints extension.' ) for k in ('Certificate Sign', 'CRL Sign'): if k not in (extensions.get('KeyUsage') or ''): verrors.add( f'{schema}.root_ca', f'Root CA must have {k} set for KeyUsage extension.') cert = await middleware.call('certificate.query', [['id', '=', data[f'{mode}_certificate']], ['revoked', '=', False]]) if not cert: verrors.add( f'{schema}.{mode}_certificate', f'Please provide a valid id for {mode.capitalize()} certificate which exists on ' 'the system and hasn\'t been revoked.') else: # Validate server/client cert cert = cert[0] if root_ca and not await middleware.call( 'cryptokey.validate_cert_with_chain', cert['certificate'], [root_ca['certificate']]): verrors.add( f'{schema}.{mode}_certificate', f'{mode} certificate chain could not be verified with specified root CA.' ) extensions = cert['extensions'] for ext in ('KeyUsage', 'SubjectKeyIdentifier', 'ExtendedKeyUsage'): if not extensions.get(ext): verrors.add( f'{schema}.{mode}_certificate', f'{mode.capitalize()} certificate must have {ext} extension set.' ) if mode == 'client': if not any(k in (extensions.get('KeyUsage') or '') for k in ('Digital Signature', 'Key Agreement')): verrors.add( f'{schema}.client_certificate', 'Client certificate must have "Digital Signature" and/or ' '"Key Agreement" set for KeyUsage extension.') if 'TLS Web Client Authentication' not in ( extensions.get('ExtendedKeyUsage') or ''): verrors.add( f'{schema}.client_certificate', 'Client certificate must have "TLS Web Client Authentication" ' 'set in ExtendedKeyUsage extension.') else: if not any(k in (extensions.get('KeyUsage') or '') for k in ('Key Encipherment', 'Key Agreement' )) or 'Digital Signature' not in ( extensions.get('KeyUsage') or ''): verrors.add( f'{schema}.server_certificate', 'Server certificate must have "Digital Signature" and either ' '"Key Agreement" or "Key Encipherment" set for KeyUsage extension.' ) if 'TLS Web Server Authentication' not in ( extensions.get('ExtendedKeyUsage') or ''): verrors.add( f'{schema}.server_certificate', 'Server certificate must have "TLS Web Server Authentication" ' 'set in ExtendedKeyUsage extension.') if data['tls_crypt_auth_enabled'] and not data['tls_crypt_auth']: verrors.add( f'{schema}.tls_crypt_auth', 'Please provide static key for authentication/encryption of all control ' 'channel packets when tls_crypt_auth_enabled is enabled.') data['tls_crypt_auth'] = None if not data.pop( 'tls_crypt_auth_enabled') else data['tls_crypt_auth'] return verrors, data
def do_create(self, data): """ Register with ACME Server Create a regisration for a specific ACME Server registering root user with it `acme_directory_uri` is a directory endpoint for any ACME Server .. examples(websocket):: Register with ACME Server :::javascript { "id": "6841f242-840a-11e6-a437-00e04d680384", "msg": "method", "method": "acme.registration.create", "params": [{ "tos": true, "acme_directory_uri": "https://acme-staging-v02.api.letsencrypt.org/directory" "JWK_create": { "key_size": 2048, "public_exponent": 65537 } }] } """ # STEPS FOR CREATION # 1) CREATE KEY # 2) REGISTER CLIENT # 3) SAVE REGISTRATION OBJECT # 4) SAVE REGISTRATION BODY verrors = ValidationErrors() directory = self.get_directory(data['acme_directory_uri']) if not isinstance(directory, messages.Directory): verrors.add( 'acme_registration_create.acme_directory_uri', f'System was unable to retrieve the directory with the specified acme_directory_uri: {directory}' ) # Normalizing uri after directory call as let's encrypt staging api # does not accept a trailing slash right now data['acme_directory_uri'] += '/' if data['acme_directory_uri'][-1] != '/' else '' if not data['tos']: verrors.add( 'acme_registration_create.tos', 'Please agree to the terms of service' ) # For now we assume that only root is responsible for certs issued under ACME protocol email = (self.middleware.call_sync('user.query', [['uid', '=', 0]]))[0]['email'] if not email: raise CallError( 'Please specify root email address which will be used with the ACME server' ) if self.middleware.call_sync( 'acme.registration.query', [['directory', '=', data['acme_directory_uri']]] ): verrors.add( 'acme_registration_create.acme_directory_uri', 'A registration with the specified directory uri already exists' ) if verrors: raise verrors key = jose.JWKRSA(key=rsa.generate_private_key( public_exponent=data['JWK_create']['public_exponent'], key_size=data['JWK_create']['key_size'], backend=default_backend() )) acme_client = client.ClientV2(directory, client.ClientNetwork(key)) register = acme_client.new_account( messages.NewRegistration.from_data( email=email, terms_of_service_agreed=True ) ) # We have registered with the acme server # Save registration object registration_id = self.middleware.call_sync( 'datastore.insert', self._config.datastore, { 'uri': register.uri, 'tos': register.terms_of_service, 'new_account_uri': directory.newAccount, 'new_nonce_uri': directory.newNonce, 'new_order_uri': directory.newOrder, 'revoke_cert_uri': directory.revokeCert, 'directory': data['acme_directory_uri'] } ) # Save registration body self.middleware.call_sync( 'datastore.insert', 'system.acmeregistrationbody', { 'contact': register.body.contact[0], 'status': register.body.status, 'key': key.json_dumps(), 'acme': registration_id } ) return self.middleware.call_sync(f'{self._config.namespace}._get_instance', registration_id)
async def validate_general_settings(self, data, schema): verrors = ValidationErrors() for key in [key for key in data.keys() if 'nameserver' in key]: nameserver_value = data.get(key) if nameserver_value: try: nameserver_ip = ipaddress.ip_address(nameserver_value) except ValueError as e: verrors.add( f'{schema}.{key}', str(e) ) else: if nameserver_ip.is_loopback: verrors.add( f'{schema}.{key}', 'Loopback is not a valid nameserver' ) elif nameserver_ip.is_unspecified: verrors.add( f'{schema}.{key}', 'Unspecified addresses are not valid as nameservers' ) elif nameserver_ip.version == 4: if nameserver_value == '255.255.255.255': verrors.add( f'{schema}.{key}', 'This is not a valid nameserver address' ) elif nameserver_value.startswith('169.254'): verrors.add( f'{schema}.{key}', '169.254/16 subnet is not valid for nameserver' ) nameserver_number = int(key[-1]) for i in range(nameserver_number - 1, 0, -1): if f'nameserver{i}' in data.keys() and not data[f'nameserver{i}']: verrors.add( f'{schema}.{key}', f'Must fill out namserver{i} before filling out {key}' ) ipv4_gateway_value = data.get('ipv4gateway') if ipv4_gateway_value: if not await self.middleware.call( 'routes.ipv4gw_reachable', ipaddress.ip_address(ipv4_gateway_value).exploded ): verrors.add( f'{schema}.ipv4gateway', f'Gateway {ipv4_gateway_value} is unreachable' ) netwait_ip = data.get('netwait_ip') if netwait_ip: for ip in netwait_ip: try: ipaddress.ip_address(ip) except ValueError as e: verrors.add( f'{schema}.netwait_ip', f'{e.__str__()}' ) if data.get('domains'): if len(data.get('domains')) > 5: verrors.add( f'{schema}.domains', 'No more than 5 additional domains are allowed' ) return verrors
def do_create(self, data): """ Register with ACME Server Create a regisration for a specific ACME Server registering root user with it `acme_directory_uri` is a directory endpoint for any ACME Server .. examples(websocket):: Register with ACME Server :::javascript { "id": "6841f242-840a-11e6-a437-00e04d680384", "msg": "method", "method": "acme.registration.create", "params": [{ "tos": true, "acme_directory_uri": "https://acme-staging-v02.api.letsencrypt.org/directory" "JWK_create": { "key_size": 2048, "public_exponent": 65537 } }] } """ # STEPS FOR CREATION # 1) CREATE KEY # 2) REGISTER CLIENT # 3) SAVE REGISTRATION OBJECT # 4) SAVE REGISTRATION BODY verrors = ValidationErrors() directory = self.get_directory(data['acme_directory_uri']) if not isinstance(directory, messages.Directory): verrors.add( 'acme_registration_create.acme_directory_uri', f'System was unable to retrieve the directory with the specified acme_directory_uri: {directory}' ) # Normalizing uri after directory call as let's encrypt staging api # does not accept a trailing slash right now data['acme_directory_uri'] += '/' if data['acme_directory_uri'][ -1] != '/' else '' if not data['tos']: verrors.add('acme_registration_create.tos', 'Please agree to the terms of service') # For now we assume that only root is responsible for certs issued under ACME protocol email = (self.middleware.call_sync('user.query', [['uid', '=', 0]]))[0]['email'] if not email: raise CallError( 'Please specify root email address which will be used with the ACME server' ) if self.middleware.call_sync( 'acme.registration.query', [['directory', '=', data['acme_directory_uri']]]): verrors.add( 'acme_registration_create.acme_directory_uri', 'A registration with the specified directory uri already exists' ) if verrors: raise verrors key = jose.JWKRSA(key=rsa.generate_private_key( public_exponent=data['JWK_create']['public_exponent'], key_size=data['JWK_create']['key_size'], backend=default_backend())) acme_client = client.ClientV2(directory, client.ClientNetwork(key)) register = acme_client.new_account( messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)) # We have registered with the acme server # Save registration object registration_id = self.middleware.call_sync( 'datastore.insert', self._config.datastore, { 'uri': register.uri, 'tos': register.terms_of_service, 'new_account_uri': directory.newAccount, 'new_nonce_uri': directory.newNonce, 'new_order_uri': directory.newOrder, 'revoke_cert_uri': directory.revokeCert, 'directory': data['acme_directory_uri'] }) # Save registration body self.middleware.call_sync( 'datastore.insert', 'system.acmeregistrationbody', { 'contact': register.body.contact[0], 'status': register.body.status, 'key': key.json_dumps(), 'acme': registration_id }) return self.middleware.call_sync( f'{self._config.namespace}._get_instance', registration_id)
async def validate_general_settings(self, data, schema): verrors = ValidationErrors() for key in [key for key in data.keys() if 'nameserver' in key]: nameserver_value = data.get(key) if nameserver_value: try: nameserver_ip = ipaddress.ip_address(nameserver_value) except ValueError as e: verrors.add(f'{schema}.{key}', str(e)) else: if nameserver_ip.is_loopback: verrors.add(f'{schema}.{key}', 'Loopback is not a valid nameserver') elif nameserver_ip.is_unspecified: verrors.add( f'{schema}.{key}', 'Unspecified addresses are not valid as nameservers' ) elif nameserver_ip.version == 4: if nameserver_value == '255.255.255.255': verrors.add( f'{schema}.{key}', 'This is not a valid nameserver address') elif nameserver_value.startswith('169.254'): verrors.add( f'{schema}.{key}', '169.254/16 subnet is not valid for nameserver' ) nameserver_number = int(key[-1]) for i in range(nameserver_number - 1, 0, -1): if f'nameserver{i}' in data.keys( ) and not data[f'nameserver{i}']: verrors.add( f'{schema}.{key}', f'Must fill out namserver{i} before filling out {key}' ) ipv4_gateway_value = data.get('ipv4gateway') if ipv4_gateway_value: if not await self.middleware.call( 'route.ipv4gw_reachable', ipaddress.ip_address(ipv4_gateway_value).exploded): verrors.add(f'{schema}.ipv4gateway', f'Gateway {ipv4_gateway_value} is unreachable') netwait_ip = data.get('netwait_ip') if netwait_ip: for ip in netwait_ip: try: ipaddress.ip_address(ip) except ValueError as e: verrors.add(f'{schema}.netwait_ip', f'{e.__str__()}') if data.get('domains'): if len(data.get('domains')) > 5: verrors.add(f'{schema}.domains', 'No more than 5 additional domains are allowed') return verrors