Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
    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'])
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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])
Ejemplo n.º 17
0
    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()
Ejemplo n.º 18
0
    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
Ejemplo n.º 19
0
    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
Ejemplo n.º 20
0
    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
Ejemplo n.º 21
0
    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()
Ejemplo n.º 22
0
    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
Ejemplo n.º 23
0
    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
Ejemplo n.º 24
0
    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
Ejemplo n.º 25
0
    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()
Ejemplo n.º 26
0
    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
Ejemplo n.º 27
0
    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
Ejemplo n.º 28
0
    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)
Ejemplo n.º 29
0
    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
Ejemplo n.º 30
0
Archivo: vpn.py Proyecto: Qapf/freenas
    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
Ejemplo n.º 31
0
    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'])
Ejemplo n.º 32
0
    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'])
Ejemplo n.º 33
0
    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
Ejemplo n.º 34
0
    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
Ejemplo n.º 35
0
    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
Ejemplo n.º 36
0
    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
Ejemplo n.º 37
0
    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)
Ejemplo n.º 38
0
    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
Ejemplo n.º 39
0
    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)
Ejemplo n.º 40
0
    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