Esempio n. 1
0
        async def process(self, req: Request['FastExecute_GET.Arguments'],
                          user: User) -> None:
            configId = req.args.configId
            tagkey = req.args.tagkey
            tagvalue = req.args.tagvalue
            # pylint: disable=attribute-defined-outside-init

            # Tag key and value must be provided both or neither.
            if tagkey is None and tagvalue is not None:
                raise PresentableError(
                    xhtml.p['Got "tagkey" without "tagvalue".'])
            if tagkey is not None and tagvalue is None:
                raise PresentableError(
                    xhtml.p['Got "tagvalue" without "tagkey".'])

            # Either configId or tag key+value must be provided.
            if configId is None:
                if tagkey is None:
                    raise PresentableError(
                        xhtml.p['Either "configId" or "tagkey" + "tagvalue" '
                                'is required.'])
                assert tagvalue is not None
                # Look up tag key+value.
                self.configs = sorted(
                    self.configDB.iterConfigsByTag(tagkey, tagvalue))
            else:
                if tagkey is not None:
                    raise PresentableError(xhtml.p[
                        'Providing both "configId" and "tagkey" + "tagvalue" '
                        'is not allowed.'])
                # Look up configId.
                try:
                    self.configs = [self.configDB[configId]]
                except KeyError:
                    self.configs = []
Esempio n. 2
0
def verifyToken(tokenDB: TokenDB, args: PasswordSetArgs) -> Token:
    """Verify a password reset token.

    @return: A valid token matching the given arguments.
    @raise PresentableError: If there is no valid token corresponding to
        the given arguments.
    """
    tokenId = args.token
    credentials = Credentials(tokenId, args.secret)
    try:
        token = authenticateToken(tokenDB, credentials)
    except KeyError as ex:
        raise PresentableError(xhtml[f'Token {tokenId} does not exist. '
                                     f'Perhaps it was already used?']) from ex
    except UnauthorizedLogin as ex:
        raise PresentableError(xhtml[
            f'The provided secret was not correct for token {tokenId}: {ex}']
                               ) from ex
    if token.role is not TokenRole.PASSWORD_RESET:
        raise PresentableError(
            xhtml[f'Token {tokenId} is not a password reset token.'])
    if token.expired:
        raise PresentableError(
            xhtml[f'Token {tokenId} has expired. '
                  f'Please ask the factory operator to reset the password.'])
    return token
Esempio n. 3
0
        async def process(self, req: Request['UserList_POST.Arguments'],
                          user: User) -> None:
            # Find user record.
            userName = req.args.user
            try:
                subject = self.userDB[userName]
            except KeyError:
                raise PresentableError(
                    xhtml.p(class_='notice')
                    [f'There is no user named "{userName}"'])

            # Parse and check all changes.
            requestUserName = user.name
            newRoles = uiRoleToSet(req.args.role)
            if (userName == requestUserName
                    and not rolesGrantPrivilege(newRoles, 'u/m')):
                # Prevent user from revoking their own 'u/m' privilege.
                raise PresentableError(xhtml[xhtml.p(
                    class_='notice'
                )['Revoking your own privileges could lead to '
                  'a situation from which recovery is impossible.'], xhtml.p[
                      f'If you want to change the role of user "{userName}", '
                      f'please log in as another user with operator '
                      f'privileges.'], ])

            # Changes are OK, commit them.
            subject.roles = newRoles

            raise Redirect(
                pageURL('UserList', UserList_GET.Arguments.subset(req.args)))
Esempio n. 4
0
        def _checkState(self) -> None:
            """Check against making parameters final which are overridden in
            existing task defs.
            """
            args = self.args

            # TODO: Generalize this when we support a variable number of levels.
            for index, name in cast(DictArgInstance[str], args.params).items():
                if name == '':
                    continue
                final = index in cast(DictArgInstance[bool], args.final)
                if final:
                    for taskDef in self.taskDefDB:
                        if taskDef.frameworkId == args.id and \
                                name in taskDef.getParametersSelf():
                            raise PresentableError(xhtml.p[
                                f'Cannot make parameter "{name}" final, '
                                f'because it is overridden '
                                f'by task "{taskDef.getId()}".'])

            if args.wrapper == '':
                raise PresentableError(
                    xhtml.p['Value of the wrapper field cannot be empty. '
                            'If you have no special wishes, '
                            'use the framework name as the wrapper name.'])

            checkParamState(args, paramTop)
            checkResourceRequirementsState(self.resTypeDB, args)
Esempio n. 5
0
        async def process(self, req: Request['SetPassword_POST.Arguments'],
                          user: User) -> None:
            # pylint: disable=attribute-defined-outside-init

            try:
                token = verifyToken(self.tokenDB, req.args)
            except PresentableError:
                self.userName = None
                raise
            else:
                self.userName = userName = token.getParam('name')

            password = req.args.password
            credentials = Credentials(userName, password)
            if password == req.args.password2:
                quality = passwordQuality(credentials)
            else:
                quality = PasswordMessage.MISMATCH
            if quality is not PasswordMessage.SUCCESS:
                raise PresentableError(xhtml[passwordStr[quality]])

            try:
                setPassword(self.userDB, credentials)
            except ValueError as ex:
                raise PresentableError(xhtml[ex.args[0]])
            else:
                self.tokenDB.remove(token)
Esempio n. 6
0
def fetchRecordForDeletion(proc: RecordDeleteProcessor[DeleteArgsT, DBRecord],
                           recordId: str) -> DBRecord:
    """Tries to fetch the record with the given ID.
    Raises PresentableError if the record does not exist or can currently
    not be deleted.
    """

    try:
        record = proc.db[recordId]
    except KeyError:
        raise PresentableError(
            xhtml.p['Cannot delete ', proc.recordName, ' ', xhtml.b[recordId],
                    ' because it does not exist (anymore).'])

    try:
        proc.checkState(record)
    except RecordInUseError as ex:
        raise PresentableError(
            xhtml.p['Cannot delete ', proc.recordName, ' ', xhtml.b[recordId],
                    ' because it is used in the following ',
                    pluralize(ex.refererName, ex.referers), ':'] +
            unorderedList[(ex.presenter(referer)
                           for referer in sorted(ex.referers))].present())

    return record
Esempio n. 7
0
        async def process(self, req: Request['ChangePassword_POST.Arguments'],
                          user: User) -> None:
            # pylint: disable=attribute-defined-outside-init

            if req.args.action is Actions.CANCEL:
                page = cast(ChangePassword_POST, self.page)
                raise Redirect(page.getCancelURL(req.args))
            elif req.args.action is Actions.CHANGE:
                userDB = self.userDB

                userName = user.name  # get current logged-in user
                if userName is None:
                    self.retry = False
                    raise PresentableError(
                        xhtml["Anonymous user has no password."])

                # Perform sanity checks on new password.
                password = req.args.password
                newCredentials = Credentials(userName, password)
                if password == req.args.password2:
                    quality = passwordQuality(newCredentials)
                else:
                    quality = PasswordMessage.MISMATCH
                if quality is not PasswordMessage.SUCCESS:
                    self.retry = True
                    raise PresentableError(xhtml[passwordStr[quality]])

                oldCredentials = Credentials(userName, req.args.loginpass)
                try:
                    user_ = await authenticateUser(userDB, oldCredentials)
                except LoginFailed as ex:
                    self.retry = True
                    raise PresentableError(
                        xhtml["Verification of old password failed",
                              f": {ex.args[0]}" if ex.args else None, "."])

                # Apply changes.
                try:
                    setPassword(userDB, newCredentials)
                except ValueError as ex:
                    self.retry = True
                    raise PresentableError(xhtml[ex.args[0]])
                else:
                    # Successfully changed password
                    raise Redirect(
                        pageURL(
                            'ChangePassword',
                            ChangePassword_GET.Arguments(
                                user=userName, msg=PasswordMessage.SUCCESS)))
            else:
                assert False, req.args.action
Esempio n. 8
0
 def _checkState(self) -> None:
     if not self.args.restype:
         raise PresentableError(xhtml.p[
             'No resource type was selected.'
             ])
     resTypeName = self.args.restype
     if resTypeName not in self.resTypeDB:
         raise PresentableError(xhtml.p[
             'Resource type ', xhtml.b[resTypeName],
             ' does not exist (anymore).'
             ])
     if resTypeName == taskRunnerResourceTypeName:
         raise PresentableError(xhtml.p[
             'This page cannot be used to create Task Runners.'
             ])
Esempio n. 9
0
        async def process(self, req: Request['ChangePassword_GET.Arguments'],
                          user: User) -> None:
            # pylint: disable=attribute-defined-outside-init

            # This page will not be linked for anonymous users, but they can
            # of course still navigate here manually.
            if user.name is None:
                self.retry = False
                raise PresentableError(
                    xhtml["Anonymous user has no password."])

            # Check if msg has been set and act upon accordingly
            msg = req.args.msg
            if msg is not None:
                self.retry = msg is not PasswordMessage.SUCCESS
                raise PresentableError(xhtml[passwordStr[msg]])
Esempio n. 10
0
 def getResource(self, resourceId: str) -> ResourceBase:
     try:
         return self.resourceDB[resourceId]
     except KeyError:
         raise PresentableError(xhtml.p['Resource ',
                                        xhtml.b[resourceId],
                                        ' does not exist (anymore).'])
Esempio n. 11
0
        async def process(self,
                          req: Request[ProductDefIdArgs],
                          user: User
                          ) -> None:
            productDefId = req.args.id
            frameworkDB = self.frameworkDB
            productDefDB = self.productDefDB

            try:
                productDef = productDefDB[productDefId]
            except KeyError:
                raise PresentableError(xhtml[
                    'Product ', xhtml.b[ productDefId ], ' does not exist.'
                    ])

            producers = []
            consumers = []
            for frameworkId, framework in frameworkDB.items():
                if productDefId in framework.getInputs():
                    consumers.append(frameworkId)
                if productDefId in framework.getOutputs():
                    producers.append(frameworkId)

            graphBuilder = ExecutionGraphBuilder(
                'graph',
                products=[productDef],
                frameworks=(frameworkDB[fid] for fid in producers + consumers),
                )

            # pylint: disable=attribute-defined-outside-init
            self.productDef = productDef
            self.producers = producers
            self.consumers = consumers
            self.graph = graphBuilder
Esempio n. 12
0
        async def process(self,
                          req: Request[FrameworkIdArgs],
                          user: User
                          ) -> None:
            frameworkId = req.args.id
            frameworkDB = self.frameworkDB
            productDefDB = self.productDefDB
            taskDefDB = self.taskDefDB

            try:
                framework = frameworkDB[frameworkId]
            except KeyError:
                raise PresentableError(xhtml[
                    'Framework ', xhtml.b[ frameworkId ], ' does not exist.'
                    ])
            taskDefs = list(taskDefsUsingFramework(taskDefDB, frameworkId))

            productIds = framework.getInputs() | framework.getOutputs()
            graphBuilder = ExecutionGraphBuilder(
                'graph',
                products=(productDefDB[pid] for pid in productIds),
                frameworks=[framework]
                )

            # pylint: disable=attribute-defined-outside-init
            self.taskDef = framework
            self.children = taskDefs
            self.graph = graphBuilder
Esempio n. 13
0
        def _checkState(self) -> None:
            args = self.args

            framework = args.framework
            if framework == '':
                raise PresentableError(xhtml.p[
                    'Please select a framework.'
                    ])
            else:
                try:
                    parent = self.frameworkDB[framework]
                except KeyError:
                    raise PresentableError(xhtml.p[
                        f'Framework "{framework}" does not exist (anymore).'
                        ])

            checkParamState(args, parent)
            checkResourceRequirementsState(self.resTypeDB, args)
Esempio n. 14
0
        async def process(self, req: Request['Notifications_POST.Arguments'],
                          user: User) -> None:
            args = req.args
            action = args.action
            smtpRelay = args.smtpRelay
            mailSender = args.mailSender

            if action is Actions.CANCEL:
                page = cast(Notifications_POST, self.page)
                raise Redirect(page.getParentURL(req.args))
            elif action is Actions.TEST:
                # pylint: disable=attribute-defined-outside-init
                recipient = args.mailRecipient
                if not recipient:
                    raise PresentableError(
                        xhtml.p(class_='notice')[
                            'Please enter a recipient address '
                            'to send the test-email to'])
                self.mailTestTime = time.localtime()
                try:
                    addresses: Iterable[Tuple[bytes, int, bytes]]
                    numOk_, addresses = await sendTestMail(
                        smtpRelay, mailSender, args.mailRecipient)
                except Exception as ex:
                    raise PresentableError(
                        xhtml.p(class_='notice')
                        [f'Sending test mail failed: {ex}'])
                self.mailTestResult = tuple(
                    (address.decode(errors='replace'),
                     f"{resp.decode(errors='replace')} ({code:d})")
                    for address, code, resp in addresses)
            elif action is Actions.SAVE:
                if mailSender and not reMailAddress.match(mailSender):
                    raise PresentableError(
                        xhtml.p(class_='notice')
                        ['Mail sender ', xhtml.code[mailSender],
                         ' does not look like an e-mail address.'])
                self.project.setMailConfig(args.mailNotification, smtpRelay,
                                           mailSender)
            else:
                assert False, action
Esempio n. 15
0
 async def process(self,
                   req: Request[ScheduleIdArgs],
                   user: User
                   ) -> None:
     scheduleId = req.args.id
     try:
         # pylint: disable=attribute-defined-outside-init
         self.scheduled = self.scheduleDB[scheduleId]
     except KeyError:
         raise PresentableError(xhtml[
             'Schedule ', xhtml.b[ scheduleId ], ' does not exist.'
             ])
Esempio n. 16
0
        async def process(self,
                          req: Request['AddUser_POST.Arguments'],
                          user: User
                          ) -> None:
            if req.args.action is Actions.CANCEL:
                page = cast(AddUser_POST, self.page)
                raise Redirect(page.getCancelURL(req.args))
            elif req.args.action is Actions.ADD:
                userDB = self.userDB

                # Validate input.
                userName = req.args.user
                if not userName:
                    raise PresentableError(xhtml['User name cannot be empty.'])

                # Authenticate currently logged-in operator.
                reqUserName = user.name
                if reqUserName is not None:
                    operatorCred = Credentials(reqUserName, req.args.loginpass)
                    try:
                        user_ = await authenticateUser(userDB, operatorCred)
                    except LoginFailed as ex:
                        raise PresentableError(xhtml[
                            'Operator authentication failed%s.' % (
                                ': ' + str(ex) if str(ex) else ''
                                )
                            ])

                # Create new user account.
                try:
                    roles = uiRoleToSet(req.args.role)
                    addUserAccount(userDB, userName, roles)
                except ValueError as ex:
                    raise PresentableError(xhtml[f'{ex}.'])

                # Create a password reset token for the new account.
                # pylint: disable=attribute-defined-outside-init
                self.token = resetPassword(userDB, userName, self.tokenDB)
            else:
                assert False, req.args.action
Esempio n. 17
0
        async def process(self, req: Request[TaskDefIdArgs],
                          user: User) -> None:
            taskDefId = req.args.id

            try:
                taskDef = self.taskDefDB[taskDefId]
            except KeyError:
                raise PresentableError(xhtml['Task Definition ',
                                             xhtml.b[taskDefId],
                                             ' does not exist.'])

            # pylint: disable=attribute-defined-outside-init
            self.taskDef = taskDef
            self.configs = list(configsUsingTaskDef(self.configDB, taskDefId))
Esempio n. 18
0
def checkResourceRequirementsState(
        resTypeDB: ResTypeDB, args: ResourceRequirementsArgsMixin) -> None:
    args = cast(_ResourceRequirementsArgs, args)
    if args.type.count(taskRunnerResourceTypeName) != 1:
        # Even though the UI can only produce one Task Runner entry,
        # we should never trust the client to enforce that.
        raise PresentableError(
            xhtml.p[f'There must be exactly one Task Runner '
                    f'(resource type "{taskRunnerResourceTypeName}").'])

    if not len(args.ref) == len(args.type) == len(args.caps):
        raise PresentableError(xhtml.p['Unequal number of ref/type/caps args'])
    usedRefs: Set[str] = set()
    for ref, resType in zip(args.ref, args.type):
        if resType == '':
            continue

        # Check whether reference name is valid.
        if not ref:
            raise PresentableError(
                xhtml.p['Empty resource reference name is not allowed.'])
        if resType == taskRunnerResourceTypeName:
            if ref != taskRunnerResourceRefName:
                raise PresentableError(
                    xhtml.p[f'The Task Runner resource reference must be '
                            f'named "{taskRunnerResourceRefName}", '
                            f'got "{ref}" instead.'])
        else:
            try:
                checkWrapperVarName(ref)
            except KeyError as ex:
                raise PresentableError(
                    xhtml.p[f'Invalid resource reference name "{ref}": ',
                            xhtml.b[str(ex.args[0])], '.'])

        # Check whether reference name is unique.
        if ref in usedRefs:
            raise PresentableError(
                xhtml.p[f'Duplicate resource reference name "{ref}".'])
        usedRefs.add(ref)

        if resType not in resTypeDB:
            raise PresentableError(
                xhtml.p[f'Resource type "{resType}" does not exist (anymore).']
            )
Esempio n. 19
0
        async def process(self,
                          req: Request['ConfigDetails_GET.Arguments'],
                          user: User
                          ) -> None:
            configId = req.args.configId
            configDB = self.configDB
            frameworkDB = self.frameworkDB
            productDefDB = self.productDefDB
            scheduleDB = self.scheduleDB

            try:
                config = configDB[configId]
            except KeyError:
                raise PresentableError(xhtml[
                    'Configuration ', xhtml.b[ configId ], ' does not exist.'
                    ])

            frameworkIds: List[str] = []
            productIds: Set[str] = set()
            for task in config.getTasks():
                framework = task.getFramework()
                if not framework.getId() in frameworkIds:
                    frameworkIds.append(framework.getId())
                productIds |= framework.getInputs()
                productIds |= framework.getOutputs()
            graphBuilder = ExecutionGraphBuilder(
                'graph',
                products=(productDefDB[pid] for pid in productIds),
                frameworks=(frameworkDB[fid] for fid in frameworkIds),
                )
            scheduleIds = tuple(
                scheduleId
                for scheduleId, schedule in scheduleDB.items()
                if configId in schedule.getMatchingConfigIds(configDB)
                )

            # pylint: disable=attribute-defined-outside-init
            self.config = config
            self.graph = graphBuilder
            self.scheduleIds = scheduleIds
Esempio n. 20
0
        async def process(self,
                          req: Request['UserDetails_GET.Arguments'],
                          user: User
                          ) -> None:
            infoUserName = req.args.user

            try:
                infoUser = self.userDB[infoUserName]
            except KeyError:
                raise PresentableError(xhtml[
                    'User ', xhtml.b[ infoUserName ], ' does not exist.'
                    ])

            jobDB = self.jobDB
            jobs = runQuery(
                [ ValueFilter('owner', infoUserName, jobDB),
                  KeySorter.forDB(['recent'], jobDB)
                  ],
                jobDB
                )[ : self.visibleJobs]

            # pylint: disable=attribute-defined-outside-init
            self.infoUser = infoUser
            self.jobs = jobs
Esempio n. 21
0
        def _checkState(self) -> None:
            args = self.args

            # Check max job count.
            if args.maxjobs <= 0:
                raise PresentableError(xhtml.p[
                    f'The value for multiple jobs limit ({args.maxjobs:d}) '
                    f'is invalid; it must be a positive integer.'])

            # Check timezone.
            # Since timezone is selected from a drop-down list, under normal
            # circumstances it will always be valid.
            timezone = decodeTimezone(args.timezone)
            knownZones = getKnownTimezones()
            if knownZones and timezone not in knownZones:
                raise PresentableError(
                    xhtml.p[f'Unknown timezone "{timezone}".'])

            # Check site filters.
            siteFilters = []
            for siteFilter in args.embedcustom.split():
                if ';' in siteFilter:
                    # Semicolon is used as a separator in the CSP header.
                    raise PresentableError(xhtml.p[
                        f'Illegal character ";" in site filter "{siteFilter}"']
                                           )
                try:
                    url = urlparse(siteFilter)
                    if not url.scheme:
                        # Force scheme-less location to be parsed as a netloc;
                        # otherwise urlparse() treats it as a relative path.
                        url = urlparse('//' + siteFilter)
                    # Force sanity check of port number.
                    _ = url.port
                except ValueError as ex:
                    raise PresentableError(
                        xhtml.p[f'Invalid site filter "{siteFilter}": {ex}'])
                for name in ('path', 'params', 'query', 'fragment', 'username',
                             'password'):
                    if getattr(url, name):
                        raise PresentableError(xhtml.p[
                            f'Site filter "{siteFilter}" contains {name}, '
                            f'which is not supported'])
                scheme = url.scheme
                netloc = url.netloc
                if scheme and netloc:
                    siteFilters.append(f'{scheme}://{netloc}')
                elif scheme:
                    siteFilters.append(f'{scheme}:')
                elif netloc:
                    siteFilters.append(netloc)
                else:
                    # Note: I don't know what filter would parse like this,
                    #       but handle it just in case.
                    raise PresentableError(xhtml.p[
                        f'Site filter "{siteFilter}" contains no information'])
            if not siteFilters and args.embed is EmbeddingPolicy.CUSTOM:
                raise PresentableError(
                    xhtml.p['Custom embedding policy cannot be empty'])
            siteFilterStr = ' '.join(siteFilters)
            if siteFilterStr != args.embedcustom:
                raise ArgsCorrected(args, embedcustom=siteFilterStr)