Example #1
0
    def register_user(self, username, password, verify=False):
        """Create a new user for the given username. Raises an error if a user
        with that name already is registered. Returns the internal unique
        identifier for the created user.

        The verify flag allows to create active or inactive users. An inactive
        user cannot login until they have been activated. This option is
        intended for scenarios where the user receives an email after they
        register that contains a verification/activation link to ensure that
        the provided email address is valid.

        Parameters
        ----------
        username: string
            User email address that is used as the username
        password: string
            Password used to authenticate the user
        verify: bool, optional
            Determines whether the created user is active or inactive

        Returns
        -------
        flowserv.model.base.User

        Raises
        ------
        flowserv.error.ConstraintViolationError
        flowserv.error.DuplicateUserError
        """
        # Ensure that the password has at least one (non-space) character
        validate_password(password)
        # Ensure that the username is not empty and does not contain more than
        # 512 characters.
        if username is None:
            raise err.ConstraintViolationError('missing user name')
        username = username.strip()
        if username == '' or len(username) > 512:
            raise err.ConstraintViolationError('username too long')
        # If a user with the given username already exists raise an error
        # Get the unique user identifier and encrypted password. Raise error
        # if user is unknown
        query = self.session.query(User).filter(User.name == username)
        user = query.one_or_none()
        if user is not None:
            raise err.DuplicateUserError(username)
        # Insert new user into database after creating an unique user
        # identifier and the password hash.
        user = User(user_id=util.get_unique_identifier(),
                    name=username,
                    secret=pbkdf2_sha256.hash(password.strip()),
                    active=False if verify else True)
        self.session.add(user)
        return user
Example #2
0
def read_run_results(run: RunObject, schema: ResultSchema, rundir: str):
    """Read the run results from the result file that is specified in the workflow
    result schema. If the file is not found we currently do not raise an error.

    Parameters
    ----------
    run: flowserv.model.base.RunObject
        Handle for a workflow run.
    schema: flowserv.model.template.schema.ResultSchema
        Workflow result schema specification that contains the reference to the
        result file key.
    rundir: string
        Directory containing run result files.
    """
    filename = os.path.join(rundir, schema.result_file)
    if os.path.exists(filename):
        results = util.read_object(filename)
        # Create a dictionary of result values.
        values = dict()
        for col in schema.columns:
            val = util.jquery(doc=results, path=col.jpath())
            col_id = col.column_id
            if val is None and col.required:
                msg = "missing value for '{}'".format(col_id)
                raise err.ConstraintViolationError(msg)
            elif val is not None:
                values[col_id] = col.cast(val)
        run.result = values
Example #3
0
def read_run_results(run: RunObject, schema: ResultSchema,
                     runstore: StorageVolume):
    """Read the run results from the result file that is specified in the workflow
    result schema. If the file is not found we currently do not raise an error.

    Parameters
    ----------
    run: flowserv.model.base.RunObject
        Handle for a workflow run.
    schema: flowserv.model.template.schema.ResultSchema
        Workflow result schema specification that contains the reference to the
        result file key.
    runstore: flowserv.volume.base.StorageVolume
        Storage volume containing the run (result) files for a successful
        workflow run.
    """
    with runstore.load(schema.result_file).open() as f:
        results = util.read_object(f)
    # Create a dictionary of result values.
    values = dict()
    for col in schema.columns:
        val = util.jquery(doc=results, path=col.jpath())
        col_id = col.column_id
        if val is None and col.required:
            msg = "missing value for '{}'".format(col_id)
            raise err.ConstraintViolationError(msg)
        elif val is not None:
            values[col_id] = col.cast(val)
    run.result = values
Example #4
0
def validate_name(name):
    """Validate the given name. Raises an error if the given name violates the
    current constraints for names. The constraints are:

    - no empty or missing names
    - names can be at most 512 characters long

    Parameters
    ----------
    name: string
        Name that is being validated

    Raises
    ------
    flowserv.error.ConstraintViolationError
    """
    if name is None:
        raise err.ConstraintViolationError('missing name')
    name = name.strip()
    if name == '' or len(name) > 512:
        raise err.ConstraintViolationError('invalid name')
Example #5
0
    def update_workflow(self,
                        workflow_id,
                        name=None,
                        description=None,
                        instructions=None):
        """Update name, description, and instructions for a given workflow.

        Raises an error if the given workflow does not exist or if the name is
        not unique.

        Parameters
        ----------
        workflow_id: string
            Unique workflow identifier
        name: string, optional
            Unique workflow name
        description: string, optional
            Optional short description for display in workflow listings
        instructions: string, optional
            Text containing detailed instructions for workflow execution

        Returns
        -------
        flowserv.model.base.WorkflowObject

        Raises
        ------
        flowserv.error.ConstraintViolationError
        flowserv.error.UnknownWorkflowError
        """
        # Get the workflow from the database. This will raise an error if the
        # workflow does not exist.
        workflow = self.get_workflow(workflow_id)
        # Update workflow properties.
        if name is not None:
            # Ensure that the name is a valid name.
            constraint.validate_name(name)
            # Ensure that the name is unique.
            wf = self.session\
                .query(WorkflowObject)\
                .filter(WorkflowObject.name == name)\
                .one_or_none()
            if wf is not None and wf.workflow_id != workflow_id:
                msg = "workflow '{}' exists".format(name)
                raise err.ConstraintViolationError(msg)
            workflow.name = name
        if description is not None:
            workflow.description = description
        if instructions is not None:
            workflow.instructions = instructions
        return workflow
Example #6
0
def validate_state_transition(current_state: str, target_state: str, valid_states: List[str]):
    """Validate that a transition from current state to target state is
    permitted. The list of valid state identifier determines the current states
    that are permitted to transition to the target state. If an invalid
    transition is detected an error is raised.

    Parameters
    ----------
    current_state: str
        Identifier for the current run state.
    target_state: str
        Identifier for the target workflow state.
    valid_states: list of string
        List of valid source states for the anticipated target state.
    """
    if current_state not in valid_states:
        msg = 'cannot change run in state {} to state {}'
        raise err.ConstraintViolationError(msg.format(current_state, target_state))
Example #7
0
def validate_password(password):
    """Validate a given password. Raises constraint violation error if an
    invalid password is given.

    Currently, the only constraint for passwords is that they are not empty

    Parameters
    ----------
    password: string
        User password for authentication

    Raises
    ------
    flowserv.error.ConstraintViolationError
    """
    # Raise error if password is invalid
    if password is None or password.strip() == '':
        raise err.ConstraintViolationError('empty password')
Example #8
0
    def create_group(self,
                     workflow_id: str,
                     name: str,
                     parameters: List[Parameter],
                     workflow_spec: Dict,
                     user_id: Optional[str] = None,
                     members: List[str] = None,
                     engine_config: Optional[Dict] = None,
                     identifier: Optional[str] = None):
        """Create a new group for a given workflow. Within each workflow,
        the names of groups are expected to be unique.

        The workflow group may define additional parameters for the template.
        The full (modifued or original) parameter list is stored with the group
        together with the workflow specification.

        A group may have a list of users that are members. Membership can be
        used to control which users are allowed to execute the associated
        workflow and to upload/view files. The user that creates the group,
        identified by user_id parameter, is always part of the initial list of
        group members.

        If a list of members is given it is ensured that each identifier in the
        list references an existing user.

        Parameters
        ----------
        workflow_id: string
            Unique workflow identifier
        name: string
            Group name
        user_id: string
            Unique identifier of the user that created the group
        parameters: list(flowserv.model.parameter.base.Parameter)
            List of workflow template parameter declarations that may be
            specific to the group
        workflow_spec: dict
            Workflow specification
        members: list(string), optional
            Optional list of user identifiers for other group members
        engine_config: dict, default=None
            Optional configuration settings that will be used as the default
            when running a workflow.
        identifier: string, default=None
            Optional user-provided group identifier.

        Returns
        -------
        flowserv.model.base.GroupObject

        Raises
        ------
        flowserv.error.ConstraintViolationError
        flowserv.error.UnknownUserError
        """
        # Validate the given group identifier. This will raise a ValueError
        # if the identifier is invalid.
        validate_identifier(identifier)
        # Ensure that the given name is valid and unique for the workflow
        constraint.validate_name(name)
        # Ensure that the user identifier is not None.
        if user_id is None:
            raise err.UnknownUserError('none')
        group = self.session.query(GroupObject)\
            .filter(GroupObject.name == name)\
            .filter(GroupObject.workflow_id == workflow_id)\
            .one_or_none()
        if group is not None:
            msg = "group '{}' exists".format(name)
            raise err.ConstraintViolationError(msg)
        # Create the group object
        identifier = identifier if identifier else unique_identifier()
        group = GroupObject(group_id=identifier,
                            name=name,
                            workflow_id=workflow_id,
                            owner_id=user_id,
                            parameters=parameters,
                            workflow_spec=workflow_spec,
                            engine_config=engine_config)
        # Create a set of member identifier that contains the identifier of
        # the group owner. Ensure that all group members exist. This will also
        # ensure that the group owner exists.
        member_set = set() if members is None else set(members)
        if user_id is not None and user_id not in member_set:
            member_set.add(user_id)
        for member_id in member_set:
            group.members.append(self.users.get_user(member_id, active=True))
        # Enter group information into the database.
        self.session.add(group)
        return group