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
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
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
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)))
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))
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
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))
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)
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)))
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)))
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
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)
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
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)
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
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))
async def process(self, req: Request['AnonGuest_POST.Arguments'], user: User ) -> None: self.project.setAnonGuestAccess(req.args.anonguest) raise Redirect('AnonGuest')