コード例 #1
0
ファイル: FastExecute.py プロジェクト: boxingbeetle/softfab
        async def process(self, req: Request['FastExecute_POST.Arguments'],
                          user: User) -> None:
            action = req.args.action

            if action is Actions.CANCEL:
                page = cast('FastExecute_POST', self.page)
                raise Redirect(req.args.refererURL
                               or page.getParentURL(req.args))

            if action is Actions.EXECUTE:
                checkPrivilege(user, 'j/c', 'create jobs')

                # Create jobs.
                jobIds = []
                userName = user.name
                for configId in sorted(req.args.confirmedId):
                    # TODO: Configs that have disappeared or become invalid are
                    #       silently ignored. Since this is a rare situation,
                    #       it is a minor problem, but still bad behaviour.
                    try:
                        config = self.configDB[configId]
                    except KeyError:
                        pass
                    else:
                        if config.hasValidInputs():
                            jobDB = self.jobDB
                            for job in config.createJobs(userName):
                                jobDB.add(job)
                                jobIds.append(job.getId())
                raise Redirect(createJobsURL(jobIds))

            assert False, action
コード例 #2
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
コード例 #3
0
ファイル: AbortTask.py プロジェクト: boxingbeetle/softfab
        async def process(self, req: Request['AbortTask_POST.Arguments'],
                          user: User) -> None:
            # pylint: disable=attribute-defined-outside-init
            jobId = req.args.jobId
            taskName = req.args.taskName
            action = req.args.action

            if action is Actions.CANCEL:
                page = cast(AbortTask_POST, self.page)
                raise Redirect(page.getParentURL(req.args))
            assert action is Actions.ABORT, action

            job = self.jobDB[jobId]
            checkPrivilegeForOwned(user, 't/d', job,
                                   ('abort tasks in this job', 'abort tasks'))
            message: XMLContent
            if taskName == '/all-waiting':
                aborted = job.abortAll(lambda task: task.isWaiting(),
                                       user.name)
                if aborted:
                    message = 'All waiting tasks have been aborted.'
                else:
                    message = 'There were no waiting tasks.'
            elif taskName == '/all':
                aborted = job.abortAll(user=user.name)
                if aborted:
                    message = 'All unfinished tasks have been aborted.'
                else:
                    message = 'There were no unfinished tasks.'
            else:
                result = job.abortTask(taskName, user.name)
                message = 'Task ', xhtml.b[taskName], ' ', result, '.'
            self.message = message
コード例 #4
0
ファイル: UserList.py プロジェクト: boxingbeetle/softfab
        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)))
コード例 #5
0
def _checkActive(
        page: FabResource[ArgsT, PageProcessor[ArgsT]],
        args: ArgsT
        ) -> None:
    '''If page is not active, redirect to parent.
    '''
    if isinstance(page, FabPage):
        if not page.isActive():
            raise Redirect(page.getParentURL(args))
コード例 #6
0
ファイル: selectview.py プロジェクト: boxingbeetle/softfab
    def processSelection(self) -> None:
        db = self.db
        args = self.args
        tagCache = self.tagCache
        action = args.action
        selected = {recordId for recordId in args.sel if recordId in db}

        if action == 'remove':
            selected -= args.bsk
        else:
            for value, label_, page in self.iterActions():
                if action == value:
                    raise Redirect(pageURL(page, SelectArgs(sel=selected)))

        # Determine tag key to filter by.
        # Empty string as key shows all records (all-pass filter).
        tagKey = args.tagkey
        tagKeys = tagCache.getKeys()
        if tagKey and tagKey not in tagKeys:
            # Drop non-existing key.
            tagKey = None
        if tagKey is None and tagKeys:
            # Pick default key.
            tagKey = tagKeys[0]

        # Determine tag value to filter by.
        # Empty string as value shows records that are not tagged
        # with the given tag key.
        if tagKey:
            tagValue = args.tagvalue
            if tagValue:
                if not tagCache.hasValue(tagKey, tagValue):
                    # Unknown tag; drop non-existing value.
                    tagValue = None
            if tagValue is None:
                # Pick default value.
                # If nothing is tagged with this key, show untagged.
                tagValue = min(tagCache.getValues(tagKey), default='')
        else:
            # A value only has meaning if we have a key.
            tagValue = None

        if (selected != args.sel or tagKey != args.tagkey
                or tagValue != args.tagvalue):
            raise ArgsCorrected(args,
                                sel=selected,
                                tagkey=tagKey,
                                tagvalue=tagValue)

        filteredRecords = self.__filterRecords(tagKey, tagValue)

        self.selected = selected
        self.selectedRecords = [db[recordId] for recordId in selected]
        self.filtered = {record.getId() for record in filteredRecords}
        self.filteredRecords = filteredRecords
コード例 #7
0
ファイル: LatestReport.py プロジェクト: boxingbeetle/softfab
 async def process(self, req: Request[TaskDefIdArgs],
                   user: User) -> None:
     taskId = req.args.id
     taskTimes = ((task.startTime, task)
                  for task in self.taskToJobs.iterTasksWithId(taskId)
                  if task.result in (ResultCode.OK, ResultCode.WARNING))
     try:
         starttime_, task = max(taskTimes)
     except ValueError:
         pass  # empty sequence; handled by presentContent()
     else:
         run = task.getLatestRun()
         raise Redirect(createRunURL(run, report=None))
コード例 #8
0
ファイル: RecordDelete.py プロジェクト: boxingbeetle/softfab
        async def process(self, req: Request, user: User) -> None:
            page = cast(RecordDelete_GET, getattr(self, 'page'))
            action = req.args.action
            if action is not Actions.DELETE:
                assert action is Actions.CANCEL, action
                raise Redirect(page.getCancelURL(req.args))

            assert isinstance(self, RecordDeleteProcessor), self
            record = fetchRecordForDeletion(self, req.args.id)
            checkPrivilegeForOwned(
                user, self.db.privilegeObject + '/d', record,
                ('delete this ' + self.recordName, 'delete ' + self.denyText))
            self.db.remove(record)
コード例 #9
0
ファイル: ResourceIndex.py プロジェクト: boxingbeetle/softfab
        async def process(self, req: Request['ResourceIndex_POST.Arguments'],
                          user: User) -> None:
            args = req.args

            # Get resource record.
            resource = self.getResource(args.resource)

            # Update suspend state.
            resource.setSuspend(args.action is Actions.SUSPEND, user.name
                                or 'anonymous')

            # Show new status, forget commands.
            raise Redirect(
                pageURL('ResourceIndex',
                        ResourceIndex_GET.Arguments.subset(args)))
コード例 #10
0
 async def process(self, req: Request['ScheduleIndex_POST.Arguments'],
                   user: User) -> None:
     actions = cast(Mapping[str, str], req.args.action)
     for scheduleId, action in actions.items():
         # Toggle suspend.
         scheduled = self.scheduleDB.get(scheduleId)
         # TODO: Report when action is not possible, instead of ignoring.
         if scheduled is not None:
             checkPrivilegeForOwned(user, 's/m', scheduled,
                                    ('suspend/resume this schedule',
                                     'suspend/resume schedules'))
             if not scheduled.isDone():
                 scheduled.setSuspend(action is Actions.SUSPEND)
     raise Redirect(
         pageURL('ScheduleIndex',
                 ScheduleIndex_GET.Arguments.subset(req.args)))
コード例 #11
0
 async def process(self,
                   req: Request['DelFinishedSchedules_POST.Arguments'],
                   user: User) -> None:
     action = req.args.action
     if action is Actions.CANCEL:
         page = cast(DelFinishedSchedules_POST, self.page)
         raise Redirect(page.getParentURL(req.args))
     elif action is Actions.DELETE:
         checkPrivilege(user, 's/d', 'delete all finished schedules')
         scheduleDB = self.scheduleDB
         finishedSchedules = [
             schedule for schedule in scheduleDB if schedule.isDone()
         ]
         for schedule in finishedSchedules:
             scheduleDB.remove(schedule)
     else:
         assert False, action
コード例 #12
0
        async def process(self, req: Request['Logout_GET.Arguments'],
                          user: User) -> None:
            url = req.args.url
            if url is not None:
                # Only accept relative URLs.
                url = req.relativeURL(url)
                if url is None:
                    raise ArgsCorrected(req.args, url=None)

            loggedOut = req.stopSession()
            # pylint: disable=attribute-defined-outside-init
            self.loggedOut = loggedOut

            # If the user still has privileges when logged out, redirect to
            # where they logged out from.
            # The privilege we check is semi-arbitrary: listing jobs is needed
            # to see the Home page, so even guests have this privilege.
            if self.project.defaultUser.hasPrivilege('j/l'):
                raise Redirect('Home' if url is None else url)
コード例 #13
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
コード例 #14
0
        async def process(self, req: Request['ConfigTags_POST.Arguments'],
                          user: User) -> None:
            args = req.args
            action = args.action
            if action is not Actions.APPLY:
                assert action is Actions.CANCEL, action
                raise Redirect(args.refererURL or parentPage)

            configDB = self.configDB
            self.notices = []
            self.findConfigs(configDB)
            if self.notices:
                return
            configs = self.configs

            checkPrivilegeForOwned(
                user, 'c/m', configs,
                ('change tags on configurations owned by other users',
                 'change configuration tags'))

            tagkeys = cast(Mapping[str, str], args.tagkeys)
            tagvalues = cast(Mapping[str, str], args.tagvalues)
            commontags = cast(Mapping[str, FrozenSet[str]], args.commontags)

            # Determine changes between the submitted tags and the stored
            # tags.
            additions: Dict[str, AbstractSet[str]] = {}
            removals: Dict[str, AbstractSet[str]] = {}
            for index, tagKey in tagkeys.items():
                storedValues = commontags.get(index, frozenset())
                values = textToValues(tagvalues[index])
                additions[tagKey] = values - storedValues
                removals[tagKey] = storedValues - values

            for config in configs:
                # TODO: Wrap update() call in context manager.
                for tagKey in tagkeys.values():
                    config.tags.updateTags(tagKey, additions[tagKey],
                                           removals[tagKey])
                configDB.update(config)
コード例 #15
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
コード例 #16
0
ファイル: BatchExecute.py プロジェクト: boxingbeetle/softfab
        async def process(self, req: Request[ParentArgs], user: User) -> None:
            args = cast(BatchExecute_POST.Arguments, req.args)
            action = args.action

            if action is not Actions.EXECUTE:
                assert action is Actions.CANCEL, action
                raise Redirect(args.refererURL or parentPage)

            notices: List[str] = []
            # pylint: disable=attribute-defined-outside-init
            self.notices = notices

            # Parse inputs.
            local = cast(Mapping[str, str], args.local)
            locations = dict(local)
            for inpName, lref in cast(Mapping[str, str], args.lref).items():
                location = local.get(lref)
                if location is not None:
                    locations[inpName] = location
            missingIds = []
            params: Dict[str, Mapping[str, Mapping[str, str]]] = {}
            configs = []
            for index, configId in cast(Mapping[str, str],
                                        args.config).items():
                try:
                    config = self.configDB[configId]
                except KeyError:
                    missingIds.append(configId)
                else:
                    configs.append(config)
                    taskParameters = cast(
                        Mapping[str, Mapping[str, Mapping[str, str]]],
                        args.param).get(index)
                    if taskParameters is not None:
                        params[configId] = taskParameters
            self.params = params
            self.configs = configs
            if missingIds:
                notices.append(presentMissingConfigs(missingIds))

            self.initTaskSet()
            taskSet = self.taskSet
            if taskSet is not None:
                for inpName, locator in cast(Mapping[str, str],
                                             args.prod).items():
                    inp = taskSet.getInput(inpName)
                    assert inp is not None
                    inp.setLocator(locator)
                for inpName, location in locations.items():
                    inp = taskSet.getInput(inpName)
                    assert inp is not None
                    inp.setLocalAt(location)

            if not notices:
                # Create jobs.
                inputs = cast(Mapping[str, str], args.prod)
                userName = user.name
                jobs: List[Job] = []
                empty: Mapping[str, Mapping[str, str]] = {}
                for config in configs:
                    try:
                        jobs += config.createJobs(userName,
                                                  locators=inputs,
                                                  localAt=locations,
                                                  taskParameters=params.get(
                                                      config.getId(), empty))
                    except ValueError as ex:
                        notices.append(f'{config.getId()}: {ex}')

                if not notices:
                    # Commit created jobs to database and show them to user.
                    jobDB = self.jobDB
                    jobIds = []
                    for job in jobs:
                        jobDB.add(job)
                        jobIds.append(job.getId())
                    raise Redirect(createJobsURL(jobIds))
コード例 #17
0
ファイル: AnonGuest.py プロジェクト: boxingbeetle/softfab
 async def process(self,
                   req: Request['AnonGuest_POST.Arguments'],
                   user: User
                   ) -> None:
     self.project.setAnonGuestAccess(req.args.anonguest)
     raise Redirect('AnonGuest')