Esempio n. 1
0
    def _validate_set_ldap_request(self):
        if not self._only_system_reserved_users_in_manager():
            raise MethodNotAllowedError('LDAP Configuration may be set only on'
                                        ' a clean manager.')
        if not premium_enabled:
            raise MethodNotAllowedError('LDAP is only supported in the '
                                        'Cloudify premium edition.')
        ldap_config = rest_utils.get_json_and_verify_params({
            'ldap_server': {},
            'ldap_username': {
                'optional': True
            },
            'ldap_password': {
                'optional': True
            },
            'ldap_domain': {},
            'ldap_is_active_directory': {
                'optional': True
            },
            'ldap_dn_extra': {},
            'ldap_ca_cert': {
                'optional': True
            },
        })
        # Not allowing empty username or password
        ldap_config['ldap_username'] = ldap_config.get('ldap_username', '')
        ldap_config['ldap_password'] = ldap_config.get('ldap_password', '')
        ldap_config['ldap_is_active_directory'] = \
            rest_utils.verify_and_convert_bool(
                'ldap_is_active_directory',
                ldap_config.get('ldap_is_active_directory') or False
            )

        if ldap_config['ldap_server'].startswith('ldaps://'):
            if 'ldap_ca_cert' not in ldap_config:
                raise BadParametersError(
                    'A CA certificate must be provided to use ldaps.')
        elif ldap_config['ldap_server'].startswith('ldap://'):
            if 'ldap_ca_cert' in ldap_config:
                raise BadParametersError(
                    'CA certificate cannot be provided when not using ldaps.')
        else:
            raise BadParametersError(
                'ldap_server must specify protocol and should specify port, '
                'e.g. ldap://192.0.2.1:389 or ldaps://192.0.2.45:636')

        if ((ldap_config['ldap_username'] and not ldap_config['ldap_password'])
                or (ldap_config['ldap_password']
                    and not ldap_config['ldap_username'])):
            raise BadParametersError(
                'Must supply either both username and password or neither. '
                'Note that an empty username or password is invalid')

        return ldap_config
Esempio n. 2
0
    def post(self):
        ldap_config = self._validate_set_ldap_request()

        from cloudify_premium.multi_tenancy.ldap_authentication \
            import LdapAuthentication

        # update current configuration
        for key, value in ldap_config.iteritems():
            setattr(config.instance, key, value)

        # assert LDAP configuration is valid.
        authenticator = LdapAuthentication()
        authenticator.configure_ldap()
        try:
            authenticator.authenticate_user(ldap_config.get('ldap_username'),
                                            ldap_config.get('ldap_password'))
        except UnauthorizedError:
            # reload previous configuration.
            config.instance.load_configuration()
            raise BadParametersError(
                'Failed setting LDAP authenticator: Invalid parameters '
                'provided.')

        config.reset(config.instance, write=True)

        # Restart the rest service so that each the LDAP configuration
        # be loaded to all flask processes.
        rest_utils.set_restart_task()

        ldap_config.pop('ldap_password')
        return ldap_config
Esempio n. 3
0
 def post(self, username, multi_tenancy):
     """
     Set password/role for a certain user
     """
     request_dict = rest_utils.get_json_and_verify_params()
     password = request_dict.get('password')
     role_name = request_dict.get('role')
     if password:
         if role_name:
             raise BadParametersError('Both `password` and `role` provided')
         password = rest_utils.validate_and_decode_password(password)
         return multi_tenancy.set_user_password(username, password)
     elif role_name:
         return multi_tenancy.set_user_role(username, role_name)
     else:
         raise BadParametersError('Neither `password` nor `role` provided')
 def _validate_detach_site(self, site_name):
     detach_site = request.json.get('detach_site')
     if (site_name and detach_site) or (not site_name and not detach_site):
         raise BadParametersError(
             "Must provide either a `site_name` of a valid site or "
             "`detach_site` with true value for detaching the current "
             "site of the given deployment")
Esempio n. 5
0
    def post(self):
        ldap_config = self._validate_set_ldap_request()

        from cloudify_premium.authentication.ldap_authentication \
            import LdapAuthentication

        # update current configuration
        config.instance.update_db(ldap_config)

        # assert LDAP configuration is valid (if credential supplied).
        if ldap_config['ldap_username']:
            auth = LdapAuthentication()
            auth.configure(current_app.logger)
            try:
                auth.authenticate_user(ldap_config.get('ldap_username'),
                                       ldap_config.get('ldap_password'))
            except UnauthorizedError:
                # reload previous configuration.
                config.instance.load_configuration()
                raise BadParametersError(
                    'Failed setting LDAP authenticator: Invalid parameters '
                    'provided.')

        # Restart the rest service so that each the LDAP configuration
        # be loaded to all flask processes.
        rest_utils.set_restart_task()

        ldap_config.pop('ldap_password')
        return ldap_config
 def post(self, maintenance_action, **_):
     maintenance_file_path = get_maintenance_file_path()
     if maintenance_action == 'activate':
         if os.path.isfile(maintenance_file_path):
             state = utils.read_json_file(maintenance_file_path)
             return state, 304
         now = utils.get_formatted_timestamp()
         try:
             user = current_user.username
         except AttributeError:
             user = ''
         remaining_executions = get_running_executions()
         status = MAINTENANCE_MODE_ACTIVATING \
             if remaining_executions else MAINTENANCE_MODE_ACTIVATED
         activated_at = '' if remaining_executions else now
         utils.mkdirs(config.instance.maintenance_folder)
         new_state = prepare_maintenance_dict(
             status=status,
             activation_requested_at=now,
             activated_at=activated_at,
             remaining_executions=remaining_executions,
             requested_by=user)
         utils.write_dict_to_json_file(maintenance_file_path, new_state)
         return new_state
     if maintenance_action == 'deactivate':
         if not os.path.isfile(maintenance_file_path):
             return prepare_maintenance_dict(
                 MAINTENANCE_MODE_DEACTIVATED), 304
         os.remove(maintenance_file_path)
         return prepare_maintenance_dict(MAINTENANCE_MODE_DEACTIVATED)
     valid_actions = ['activate', 'deactivate']
     raise BadParametersError('Invalid action: {0}, Valid action '
                              'values are: {1}'.format(
                                  maintenance_action, valid_actions))
    def post(self, maintenance_action, **_):

        state = get_maintenance_state()
        if maintenance_action == 'activate':
            if state:
                return state, 304
            remaining_executions = get_running_executions()
            status = MAINTENANCE_MODE_ACTIVATING \
                if remaining_executions else MAINTENANCE_MODE_ACTIVATED

            now = datetime.utcnow()
            state = store_maintenance_state(
                status=status,
                activation_requested_at=now,
                activated_at=None if remaining_executions else now,
                requested_by=current_user)
            state['remaining_executions'] = remaining_executions,
            return state

        elif maintenance_action == 'deactivate':
            if not state:
                return {'status': MAINTENANCE_MODE_DEACTIVATED}, 304
            return remove_maintenance_state()

        else:
            valid_actions = ['activate', 'deactivate']
            raise BadParametersError('Invalid action: {0}, Valid action '
                                     'values are: {1}'.format(
                                         maintenance_action, valid_actions))
Esempio n. 8
0
 def post(self, group_name, multi_tenancy):
     """
     Set role for a certain group
     """
     request_dict = rest_utils.get_json_and_verify_params()
     role_name = request_dict.get('role')
     if not role_name:
         raise BadParametersError('`role` not provided')
     rest_utils.verify_role(role_name, is_system_role=True)
     return multi_tenancy.set_group_role(group_name, role_name)
Esempio n. 9
0
 def post(self, tenant_name, multi_tenancy):
     """
     Create a tenant
     """
     rest_utils.validate_inputs({'tenant_name': tenant_name})
     if tenant_name in ('users', 'user-groups'):
         raise BadParametersError(
             '{0!r} is not allowed as a tenant name '
             "because it wouldn't be possible to remove it later due to "
             'a conflict with the remove {0} from tenant endpoint'.format(
                 str(tenant_name)))
     return multi_tenancy.create_tenant(tenant_name)
Esempio n. 10
0
 def post(self, multi_tenancy):
     """
     Create a group
     """
     request_dict = rest_utils.get_json_and_verify_params()
     group_name = request_dict['group_name']
     ldap_group_dn = request_dict.get('ldap_group_dn')
     role = request_dict.get('role', constants.DEFAULT_SYSTEM_ROLE)
     rest_utils.verify_role(role, is_system_role=True)
     rest_utils.validate_inputs({'group_name': group_name})
     if group_name == 'users':
         raise BadParametersError(
             '{0!r} is not allowed as a user group name '
             "because it wouldn't be possible to remove it later due to "
             'a conflict with the remove {0} from user group endpoint'.
             format(str(group_name)))
     return multi_tenancy.create_group(group_name, ldap_group_dn, role)
Esempio n. 11
0
    def post(self, group_id, **kwargs):
        request_dict = get_json_and_verify_params({'action'})
        action = request_dict['action']

        valid_actions = ['cancel', 'force-cancel', 'kill',
                         'resume', 'force-resume']

        if action not in valid_actions:
            raise BadParametersError(
                'Invalid action: {0}, Valid action values are: {1}'.format(
                    action, valid_actions))

        sm = get_storage_manager()
        group = sm.get(models.ExecutionGroup, group_id)
        if action in ('cancel', 'force-cancel', 'kill'):
            self._cancel_group(sm, group, action)
        if action in ('resume', 'force-resume'):
            self._resume_group(sm, group, action)
        return group
Esempio n. 12
0
    def _validate_set_ldap_request(self):
        if not self._only_admin_in_manager():
            raise MethodNotAllowedError('LDAP Configuration may be set only on'
                                        ' a clean manager.')
        if not premium_enabled:
            raise MethodNotAllowedError('LDAP is only supported in the '
                                        'Cloudify premium edition.')
        ldap_config = rest_utils.get_json_and_verify_params({
            'ldap_server': {},
            'ldap_username': {
                'optional': True
            },
            'ldap_password': {
                'optional': True
            },
            'ldap_domain': {},
            'ldap_is_active_directory': {
                'optional': True
            },
            'ldap_dn_extra': {}
        })
        # Not allowing empty username or password
        ldap_config['ldap_username'] = ldap_config.get('ldap_username', '')
        ldap_config['ldap_password'] = ldap_config.get('ldap_password', '')
        ldap_config['ldap_is_active_directory'] = \
            rest_utils.verify_and_convert_bool(
                'ldap_is_active_directory',
                ldap_config.get('ldap_is_active_directory') or False
            )

        if ((ldap_config['ldap_username'] and not ldap_config['ldap_password'])
                or (ldap_config['ldap_password']
                    and not ldap_config['ldap_username'])):
            raise BadParametersError(
                'Must supply either both username and password or neither. '
                'Note that an empty username or password is invalid')

        return ldap_config
Esempio n. 13
0
def _verify_role(role):
    valid_roles = [r['name'] for r in config.instance.authorization_roles]
    if role not in valid_roles:
        raise BadParametersError(
            'Invalid role: `{0}`. Valid roles are: {1}'.format(
                role, valid_roles))
Esempio n. 14
0
    def delete(self, filters=None, pagination=None, all_tenants=None):
        request_dict = get_json_and_verify_params({
            'keep_last': {'optional': True, 'type': int},
            'to_datetime': {'optional': True}
        })
        if 'keep_last' in request_dict:
            if 'to_datetime' in request_dict:
                raise BadParametersError(
                    "Must provide either a `to_datetime` timestamp or a "
                    "`keep_last` number of executions to keep"
                )
            if request_dict['keep_last'] <= 0:
                raise BadParametersError(
                    "`keep_last` must be an integer greater than 0. got {} "
                    "instead.".format(request_dict['keep_last'])
                )
        requested_time = None
        if 'to_datetime' in request_dict:
            requested_time = parse_datetime_multiple_formats(
                request_dict['to_datetime'])
        if 'status' in filters:
            if filters['status'] not in ExecutionState.END_STATES:
                raise BadParametersError(
                    'Can\'t filter by execution status `{0}`. '
                    'Allowed statuses are: {1}'.format(
                        filters['status'], ExecutionState.END_STATES)
                )
        else:
            filters['status'] = ExecutionState.END_STATES

        sm = get_storage_manager()
        executions = sm.list(models.Execution,
                             filters=filters,
                             all_tenants=all_tenants,
                             get_all_results=True)
        dep_creation_execs = {}
        for execution in executions:
            if execution.workflow_id == 'create_deployment_environment' and \
                    execution.status == 'terminated':
                dep_creation_execs[execution.deployment_id] = \
                    dep_creation_execs.get(execution.deployment_id, 0) + 1

        deleted_count = 0
        if requested_time:
            for execution in executions:
                creation_time = datetime.strptime(execution.created_at,
                                                  '%Y-%m-%dT%H:%M:%S.%fZ')

                if creation_time < requested_time and \
                        self._can_delete_execution(execution,
                                                   dep_creation_execs):
                    sm.delete(execution)
                    deleted_count += 1
        else:
            if request_dict.get('keep_last'):
                max_to_delete = len(executions) - request_dict['keep_last']
            for execution in executions:
                if self._can_delete_execution(execution, dep_creation_execs):
                    sm.delete(execution)
                    deleted_count += 1
                    if request_dict.get('keep_last') and deleted_count >= \
                            max_to_delete:
                        break
        return ListResult([{'count': deleted_count}],
                          {'pagination': pagination})
Esempio n. 15
0
    def patch(self, blueprint_id, **kwargs):
        """
        Update a blueprint.

        Used for updating the blueprint's state (and error) while uploading,
        and updating the blueprint's other attributes upon a successful upload.
        This method is for internal use only.
        """
        if not request.json:
            raise IllegalActionError('Update a blueprint request must include '
                                     'at least one parameter to update')

        request_schema = {
            'plan': {
                'type': dict,
                'optional': True
            },
            'description': {
                'type': text_type,
                'optional': True
            },
            'main_file_name': {
                'type': text_type,
                'optional': True
            },
            'visibility': {
                'type': text_type,
                'optional': True
            },
            'state': {
                'type': text_type,
                'optional': True
            },
            'error': {
                'type': text_type,
                'optional': True
            },
            'error_traceback': {
                'type': text_type,
                'optional': True
            },
            'labels': {
                'type': list,
                'optional': True
            }
        }
        request_dict = rest_utils.get_json_and_verify_params(request_schema)

        invalid_params = set(request_dict.keys()) - set(request_schema.keys())
        if invalid_params:
            raise BadParametersError("Unknown parameters: {}".format(
                ','.join(invalid_params)))
        sm = get_storage_manager()
        rm = get_resource_manager()
        blueprint = sm.get(models.Blueprint, blueprint_id)

        # if finished blueprint validation - cleanup DB entry
        # and uploaded blueprints folder
        if blueprint.state == BlueprintUploadState.VALIDATING:
            uploaded_blueprint_path = join(
                config.instance.file_server_root,
                FILE_SERVER_UPLOADED_BLUEPRINTS_FOLDER, blueprint.tenant.name,
                blueprint.id)
            remove(uploaded_blueprint_path)
            sm.delete(blueprint)
            return blueprint

        # set blueprint visibility
        visibility = request_dict.get('visibility')
        if visibility:
            if visibility not in VisibilityState.STATES:
                raise BadParametersError(
                    "Invalid visibility: `{0}`. Valid visibility's values "
                    "are: {1}".format(visibility, VisibilityState.STATES))
            blueprint.visibility = visibility

        # set other blueprint attributes.
        if 'plan' in request_dict:
            blueprint.plan = request_dict['plan']
        if 'description' in request_dict:
            blueprint.description = request_dict['description']
        if 'main_file_name' in request_dict:
            blueprint.main_file_name = request_dict['main_file_name']
        provided_labels = request_dict.get('labels')

        if request_dict.get('plan'):
            dsl_labels = request_dict['plan'].get('labels', {})
            csys_obj_parents = dsl_labels.get('csys-obj-parent')
            if csys_obj_parents:
                dep_parents = csys_obj_parents['values']
                missing_parents = rm.get_missing_deployment_parents(
                    dep_parents)
                if missing_parents:
                    raise DeploymentParentNotFound(
                        'Blueprint {0}: is referencing deployments'
                        ' using label `csys-obj-parent` that does not exist, '
                        'make sure that deployment(s) {1} exist before '
                        'creating blueprint'.format(blueprint.id,
                                                    ','.join(missing_parents)))
        # set blueprint state
        state = request_dict.get('state')
        if state:
            if state not in BlueprintUploadState.STATES:
                raise BadParametersError(
                    "Invalid state: `{0}`. Valid blueprint state values are: "
                    "{1}".format(state, BlueprintUploadState.STATES))
            if (state != BlueprintUploadState.UPLOADED
                    and provided_labels is not None):
                raise ConflictError(
                    'Blueprint labels can be created only if the provided '
                    'blueprint state is {0}'.format(
                        BlueprintUploadState.UPLOADED))

            blueprint.state = state
            blueprint.error = request_dict.get('error')
            blueprint.error_traceback = request_dict.get('error_traceback')

            # On finalizing the blueprint upload, extract archive to file
            # server
            if state == BlueprintUploadState.UPLOADED:
                UploadedBlueprintsManager(). \
                    extract_blueprint_archive_to_file_server(
                        blueprint_id=blueprint_id,
                        tenant=blueprint.tenant.name)
                _create_blueprint_labels(blueprint, provided_labels)

            # If failed for any reason, cleanup the blueprint archive from
            # server
            elif state in BlueprintUploadState.FAILED_STATES:
                UploadedBlueprintsManager(). \
                    cleanup_blueprint_archive_from_file_server(
                        blueprint_id=blueprint_id,
                        tenant=blueprint.tenant.name)
        else:  # Updating the blueprint not as part of the upload process
            if provided_labels is not None:
                if blueprint.state != BlueprintUploadState.UPLOADED:
                    raise ConflictError(
                        'Blueprint labels can only be updated if the blueprint'
                        ' was uploaded successfully')

                rm = get_resource_manager()
                labels_list = rest_utils.get_labels_list(provided_labels)
                rm.update_resource_labels(models.BlueprintLabel, blueprint,
                                          labels_list)

        blueprint.updated_at = get_formatted_timestamp()
        return sm.update(blueprint)
Esempio n. 16
0
    def _validate_set_ldap_request(self):
        if not premium_enabled:
            raise MethodNotAllowedError('LDAP is only supported in the '
                                        'Cloudify premium edition.')
        base_substitutions = ['base_dn', 'domain_dn', 'group_dn']
        ldap_config = rest_utils.get_json_and_verify_params({
            'ldap_server': {},
            'ldap_domain': {},
            'ldap_username': {'optional': True},
            'ldap_password': {'optional': True},
            'ldap_is_active_directory': {'optional': True},
            'ldap_dn_extra': {'optional': True},
            'ldap_ca_cert': {'optional': True},
            'ldap_nested_levels': {'optional': True},
            'ldap_bind_format': {
                'optional': True,
                'allowed_substitutions': [
                    'username', 'domain'] + base_substitutions,
            },
            'ldap_group_dn': {
                'optional': True,
                'allowed_substitutions': ['base_dn', 'domain_dn'],
            },
            'ldap_base_dn': {'optional': True},
            'ldap_group_member_filter': {
                'optional': True,
                'allowed_substitutions': ['object_dn']
            },
            'ldap_user_filter': {
                'optional': True,
                'allowed_substitutions': ['username'] + base_substitutions,
            },
            'ldap_attribute_email': {'optional': True},
            'ldap_attribute_first_name': {'optional': True},
            'ldap_attribute_last_name': {'optional': True},
            'ldap_attribute_uid': {'optional': True},
            'ldap_attribute_group_membership': {'optional': True},
        })

        if ldap_config.get('ldap_nested_levels') is None:
            ldap_config['ldap_nested_levels'] = 1
        else:
            ldap_config['ldap_nested_levels'] = rest_utils.convert_to_int(
                ldap_config['ldap_nested_levels'])

        for attr in ldap_config:
            if ldap_config[attr] is None:
                # Otherwise we try to set None on the config entry, which is
                # not a string.
                ldap_config[attr] = ''

        ldap_config['ldap_is_active_directory'] = \
            rest_utils.verify_and_convert_bool(
                'ldap_is_active_directory',
                ldap_config.get('ldap_is_active_directory') or False
            )

        if ldap_config['ldap_server'].startswith('ldaps://'):
            if 'ldap_ca_cert' not in ldap_config:
                raise BadParametersError(
                    'A CA certificate must be provided to use ldaps.'
                )
        elif ldap_config['ldap_server'].startswith('ldap://'):
            if 'ldap_ca_cert' in ldap_config:
                raise BadParametersError(
                    'CA certificate cannot be provided when not using ldaps.'
                )
        else:
            raise BadParametersError(
                'ldap_server must specify protocol and should specify port, '
                'e.g. ldap://192.0.2.1:389 or ldaps://192.0.2.45:636'
            )

        user = ldap_config.get('ldap_username')
        password = ldap_config.get('ldap_password')
        if (user or password) and not (user and password):
            raise BadParametersError(
                'Must supply either both username and password or neither. '
                'Note that an empty username or password is invalid')

        return ldap_config