def ticketGalleryFsView(ticketId, securityCode, fileName): """ View-file-within-a-ticket-generated-gallery-view route. Helper endpoint to return viewable (only viewables, there's no 'download') files in a ticket-gallery view. Must take care of punching the ticket. """ user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] # richTicket = dbGetEnrichAndCheckTicket( db, 'g', ticketId, securityCode, request.url_root, ) if richTicket is not None: issuer = dbGetUser(db, richTicket['ticket'].username) if richTicket['redeemable']: # valid ticket. Further checks are on the way. if (not g.settings['behaviour']['behaviour_tickets'] ['protect_banned_user_tickets']['value'] or issuer.banned == 0): # boxPath = richTicket['metadata']['box_path'] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath[1:], issuer) if parentBox is not None: # we retrieve the file and serve it file = getFileFromParent(db, parentBox, fileName, issuer) if file is not None: dbPunchRichTicket(db, richTicket) filePhysicalPath, filePhysicalName = fileIdToSplitPath( file.file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, attachment_filename=file.name, as_attachment=True, mimetype=file.mime_type, ) else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable')
def ticketFsDownloadView(ticketId, securityCode): """ Give-the-file-contents-based-on-ticket route. Helper endpoint to load-and-return a file upon a ticket; access to files based on a ticket. Used by both the direct-file-download or the view-file file-ticket modes. Note: punching occurs here. """ user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] # richTicket = dbGetEnrichAndCheckTicket( db, 'f', ticketId, securityCode, request.url_root, ) issuer = (dbGetUser(db, richTicket['ticket'].username) if richTicket is not None else None) noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][ 'protect_banned_user_tickets']['value'] if (issuer is not None and (not noBandUsrTickets or issuer.banned == 0)): boxPath, fileName = ( richTicket['metadata']['path'][:-1], richTicket['metadata']['path'][-1], ) parentBox = getBoxFromPath(db, boxPath, issuer) # if parentBox is not None: file = getFileFromParent(db, parentBox, fileName, issuer) if file is not None: # return it and contextually punch the ticket dbPunchRichTicket(db, richTicket) # then we return the file as a download # (this flow assumes download is desired as opposed to view) filePhysicalPath, filePhysicalName = fileIdToSplitPath( file.file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, attachment_filename=file.name, as_attachment=True, mimetype=file.mime_type, ) else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable')
def dbPunchTicket(db, mode, ticketId, securityCode, urlRoot, protectBannedUserTickets): """Retrieve/enrich a ticket and punch it with dbPunchRichTicket.""" richTicket = dbGetEnrichAndCheckTicket( db, mode, ticketId, securityCode, urlRoot, ) issuer = (dbGetUser(db, richTicket['ticket'].username) if richTicket is not None else None) if (issuer is not None and (not protectBannedUserTickets or issuer.banned == 0)): dbPunchRichTicket(db, richTicket) else: raise OstracionError('Not allowed to punch ticket')
def userThumbnailView(dummyId, username): """Route for access to thumbnail image files based on user name.""" user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] if user.username == username or userIsAdmin(db, user): targetUser = (user if user.username == username else dbGetUser( db, username)) if targetUser.icon_file_id != '': filePhysicalPath, filePhysicalName = fileIdToSplitPath( targetUser.icon_file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, mimetype=targetUser.icon_mime_type, ) else: return redirect(makeSettingImageUrl(g, 'user_images', 'user_icon')) else: return abort(400, 'User has no permission to access this resource.')
def _reduceBoxToPath(db, box): """ Given a box, calculate its full path and return it in a dict.""" return { 'path': _retraceBoxPath(db, box.box_id), } def describeFindResults(results): """ Given the find-results structure, prepare a textual nice description. """ foundCounts = [ (results['counts']['boxes'], 'box', 'boxes'), (results['counts']['files'], 'file', 'files'), (results['counts']['links'], 'link', 'links'), ] foundParts = pickSingularPluralSentences(foundCounts, keepZeroes=False) return '%s found.' % colloquialJoinClauses(foundParts) if __name__ == '__main__': from ostracion_app.utilities.database.dbTools import ( dbGetDatabase, ) from ostracion_app.utilities.database.userTools import ( dbGetUser, ) db = dbGetDatabase() # user = dbGetUser(db, 'sergio') results = fsFind(db, 'i', user) print(results)
def changePasswordUponTicket(db, user, issuer, richTicket, urlRoot): """Change-password-ticket route.""" ticketsByformerAdminsAreProtected = g.settings['behaviour'][ 'behaviour_tickets']['protect_nonadmin_user_password_tickets']['value'] applicationLongName = g.settings['behaviour']['behaviour_appearance'][ 'application_long_name']['value'] if not ticketsByformerAdminsAreProtected or userIsAdmin(db, issuer): form = generateTicketChangePasswordForm(g.settings) if form.validate_on_submit(): changeeUser = dbGetUser(db, richTicket['metadata']['username']) if form.username.data == changeeUser.username: # newPassword = form.newpassword.data minPasswordLength = g.settings['behaviour'][ 'behaviour_security']['password_min_length']['value'] if len(form.newpassword.data) < minPasswordLength: print('richTicket ', richTicket) flashMessage( 'Info', 'Please retry', 'New password too short (min. %i characters)' % (minPasswordLength, ), ) return redirect( url_for( 'redeemTicketView', mode='p', ticketId=richTicket['ticket'].ticket_id, securityCode=richTicket['ticket'].security_code, )) else: newUser = User( **({(k if k != 'passwordhash' else 'password'): ( v if k != 'passwordhash' else form.newpassword.data ) for k, v in changeeUser.asDict().items()})) try: dbPunchRichTicket(db, richTicket, skipCommit=True) dbUpdateUser(db, newUser, user, skipCommit=True) db.commit() # flash a message flashMessage( 'Success', 'Success', 'Password changed. You can now log in to %s' % (applicationLongName, ), ) # go to login return redirect(url_for('loginView')) except Exception as e: db.rollback() raise e else: raise OstracionError('Wrong username provided') else: pageSubtitle = ('Complete the process by entering your ' 'new password twice%s') % ( '' if richTicket['metadata'].get('message') is None else '. Message on ticket: "%s"' % (richTicket['metadata'].get('message'), )) return render_template( 'userchangepassword.html', user=user, form=form, showOldPasswordField=False, mode='ticket', breadCrumbs=None, pageTitle='Change password', pageSubtitle=pageSubtitle, iconUrl=makeSettingImageUrl(g, 'admin_images', 'change_password_ticket'), backToUrl=url_for('lsView'), ) else: raise OstracionError('Ticket not acessible or expired')
def ticketGalleryView(ticketId, securityCode, page=0): """Actual route to view a gallery with a ticket.""" user = g.user db = dbGetDatabase() # richTicket = dbGetEnrichAndCheckTicket( db, 'g', ticketId, securityCode, request.url_root, ) if richTicket is None: raise OstracionError('Invalid ticket magic link') else: issuer = dbGetUser(db, richTicket['ticket'].username) if richTicket['redeemable']: # valid ticket. Further checks are on the way. noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][ 'protect_banned_user_tickets']['value'] if (not noBandUsrTickets or issuer.banned == 0): # boxPath = richTicket['metadata']['box_path'] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath[1:], issuer) if parentBox is not None: if parentBox.box_id == richTicket['metadata']['box_id']: galleryMessage = richTicket['metadata'].get('message') fileStorageDirectory = g.settings['system'][ 'system_directories']['fs_directory']['value'] files = sorted( getFilesFromBox(db, parentBox), key=lambda f: (f.name.lower(), f.name), ) numGalleryFiles = len(files) if numGalleryFiles > 0: thisFileIndex = page % numGalleryFiles file = files[thisFileIndex] # gallery navigation calculations nextFileIndex = (thisFileIndex + 1) % numGalleryFiles prevFileIndex = (thisFileIndex - 1 + numGalleryFiles) % numGalleryFiles # fileContents = produceFileViewContents( db, file, mode='galleryview', viewParameters={ 'boxPath': boxPath, 'fileName': file.name, 'ticketId': ticketId, 'securityCode': securityCode, }, fileStorageDirectory=fileStorageDirectory, urlRoot=request.url_root, protectBannedUserTickets=noBandUsrTickets, ) fileActions = { 'gallery_prev': url_for( 'ticketGalleryView', ticketId=ticketId, securityCode=securityCode, page=prevFileIndex, ), 'gallery_next': url_for( 'ticketGalleryView', ticketId=ticketId, securityCode=securityCode, page=nextFileIndex, ), 'homepage': url_for('lsView', ) } return render_template( 'fileview.html', fileActions=fileActions, fileInfo=None, filecontents=fileContents, breadCrumbs=None, user=user, pageTitle='Gallery view, file "%s" (%i/%i)' % ( file.name, thisFileIndex + 1, numGalleryFiles, ), pageSubtitle=(None if galleryMessage is None else ('Message on ticket: "%s"' % (galleryMessage, ))), downloadUrl=None, hideBreadCrumbs=True, hideNavbar=True, ) # return fsView(fsPathString) else: flashMessage( 'Info', 'Empty box', 'Cannot view as gallery a box without files', ) return redirect(url_for('lsView')) else: raise OstracionError('Ticket cannot be redeemed') else: raise OstracionError('Invalid ticket magic link') else: raise OstracionError('Ticket cannot be redeemed') else: raise OstracionError('Invalid ticket magic link')
def redeemTicketView(mode, ticketId, securityCode): """ Redeem-a-ticket view. This is the route associated to the URL given in the 'ticket' as a magic link and internally performs one of several operations, depending on the ticket type. Depending on the type of ticket, different checks are in place. The actual handling of the ticket action (including upon POSTs) is in the hands of the subsequently-called function (not: endpoint), but: ** all checks except existing/redeemable are up to the individual functions. ** """ user = g.user db = dbGetDatabase() # richTicket = dbGetEnrichAndCheckTicket( db, mode, ticketId, securityCode, request.url_root, ) if richTicket is None: raise OstracionError('Invalid ticket magic link') else: issuer = (dbGetUser(db, richTicket['ticket'].username) if richTicket is not None else None) if richTicket is not None and richTicket['redeemable']: if mode == 'u': # new user creation with ticket registering # upon actual creation return createUserUponTicket( db=db, user=user, issuer=issuer, richTicket=richTicket, urlRoot=request.url_root, ) elif mode == 'p': # change password: change password form with ticket # registering upon successful change return changePasswordUponTicket( db=db, user=user, issuer=issuer, richTicket=richTicket, urlRoot=request.url_root, ) elif mode == 'f': return retrieveFileUponTicket( db=db, user=user, issuer=issuer, richTicket=richTicket, urlRoot=request.url_root, ) elif mode == 'c': return uploadFilesUponTicket( db=db, user=user, issuer=issuer, richTicket=richTicket, urlRoot=request.url_root, ) elif mode == 'g': return galleryViewUponTicket( db=db, user=user, issuer=issuer, richTicket=richTicket, urlRoot=request.url_root, ) else: raise OstracionError('Invalid ticket magic link') else: raise OstracionError('Invalid ticket magic link')
def isUsername(db, username, user): """Checks if the proposed username is not already existing.""" return dbGetUser(db, username) is not None
def setIconView(mode, itemPathString=''): """ Route to set/replace the thumbnail of various items.""" if (mode == 'au' and not g.settings['behaviour']['behaviour_admin_powers'] ['admin_is_god']['value']): request._onErrorUrl = url_for('adminHomeUsersView') raise OstracionError('This feature is turned off in the configuration') else: user = g.user form = UploadIconForm() db = dbGetDatabase() tempFileDirectory = g.settings['system']['system_directories'][ 'temp_directory']['value'] fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] if mode == 'b': boxPath = splitPathString(itemPathString) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = None thisItem = getBoxFromPath(db, boxPath, user) itemName = thisItem.getName() pageFeatures = { 'breadCrumbs': makeBreadCrumbs( splitPathString(itemPathString), g, appendedItems=[{ 'kind': 'link', 'target': None, 'name': 'Icon', }], ), } elif mode == 'f': fullPath = splitPathString(itemPathString) boxPath, fileName = fullPath[:-1], fullPath[-1] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath, user) thisItem = getFileFromParent(db, parentBox, fileName, user) itemName = thisItem.getName() pageFeatures = { 'breadCrumbs': makeBreadCrumbs( splitPathString(itemPathString)[:-1], g, appendedItems=[{ 'kind': 'file', 'target': thisItem, }, { 'kind': 'link', 'target': None, 'name': 'Icon', }], ), } elif mode == 'l': fullPath = splitPathString(itemPathString) boxPath, linkName = fullPath[:-1], fullPath[-1] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath, user) thisItem = getLinkFromParent(db, parentBox, linkName, user) itemName = thisItem.getName() pageFeatures = { 'breadCrumbs': makeBreadCrumbs( splitPathString(itemPathString)[:-1], g, appendedItems=[{ 'kind': 'external_link', 'target': thisItem, }, { 'kind': 'link', 'target': None, 'name': 'Icon', }], ), } elif mode == 'u': pageFeatures = prepareTaskPageFeatures( userProfilePageDescriptor, ['root', 'icon'], g, overrides={ 'pageTitle': None, 'pageSubtitle': None, 'iconUrl': None, }, ) thisItem = user itemName = thisItem.getName() parentBox = None elif mode == 'au': pageFeatures = prepareTaskPageFeatures( adminPageDescriptor, ['root', 'users'], g, appendedItems=[{ 'kind': 'link', 'link': False, 'target': None, 'name': 'Icon', }], overrides={ 'pageTitle': None, 'pageSubtitle': None, 'iconUrl': None, }, ) if userIsAdmin(db, user): thisItem = dbGetUser(db, itemPathString) itemName = thisItem.getName() parentBox = None else: raise OstracionError('Insufficient permissions') elif mode == 's': pageFeatures = prepareTaskPageFeatures( adminPageDescriptor, ['root', 'settings', 'images'], g, appendedItems=[{ 'kind': 'link', 'link': False, 'target': None, 'name': 'Set image', }], overrides={ 'pageTitle': None, 'pageSubtitle': None, 'iconUrl': None, }, ) if userIsAdmin(db, user): settingGroupId, settingId = itemPathString.split('/') thisItem = g.settings['image'][settingGroupId][settingId] itemName = thisItem['setting'].getName() parentBox = None else: raise OstracionError('Insufficient permissions') else: raise RuntimeError('Unknown mode encountered') # if form.validate_on_submit(): # storageSuccess = storeFileAsThumbnail( db, fileToSave=form.file.data, mode=mode, thumbnailFormat=determineThumbnailFormatByModeAndTarget( db, mode, thisItem, ), targetItem=thisItem, parentBox=parentBox, user=user, tempFileDirectory=tempFileDirectory, fileStorageDirectory=fileStorageDirectory, ) if not storageSuccess: raise OstracionError('Could not set the icon') # if mode in {'f', 'b', 'l'}: return redirect( url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1] if mode == 'b' else boxPath[1:]), )) elif mode == 'u': return redirect(url_for('userProfileView', )) elif mode == 'au': return redirect(url_for('adminHomeUsersView', )) elif mode == 's': return redirect(url_for('adminHomeSettingsImagesView', )) else: raise RuntimeError('Unknown mode encountered') else: # titleMap = { 'f': 'Set File Icon', 'l': 'Set Link Icon', 'b': 'Set Box Icon', 'u': 'Set User Icon', 'au': 'Set User Icon (as admin)', 's': 'Set Application Image', } modeNameMap = { 'f': 'for file', 'l': 'for link', 'b': 'for box', 'u': 'for user', 'au': '(as admin) for user', 's': 'for setting', } finalPageFeatures = recursivelyMergeDictionaries( { 'pageTitle': titleMap[mode], 'pageSubtitle': 'Upload an image file %s "%s"' % ( modeNameMap[mode], itemName, ), }, defaultMap=pageFeatures, ) if mode == 'u': finalPageFeatures['iconUrl'] = url_for( 'userThumbnailView', dummyId='%s_' % thisItem.icon_file_id, username=thisItem.username, ) elif mode == 'au': finalPageFeatures['iconUrl'] = url_for( 'userThumbnailView', dummyId='%s_' % thisItem.icon_file_id, username=thisItem.username, ) elif mode == 's': finalPageFeatures['iconUrl'] = makeSettingImageUrl( g, settingGroupId, settingId, ) elif mode == 'b': finalPageFeatures['iconUrl'] = url_for( 'boxThumbnailView', dummyId=thisItem.icon_file_id + '_', boxPathString='/'.join(boxPath[1:]), ) elif mode == 'f': finalPageFeatures['iconUrl'] = url_for( 'fileThumbnailView', dummyId=thisItem.icon_file_id + '_', fsPathString='/'.join(boxPath[1:] + [thisItem.name]), ) elif mode == 'l': finalPageFeatures['iconUrl'] = url_for( 'linkThumbnailView', dummyId=thisItem.icon_file_id + '_', fsPathString='/'.join(boxPath[1:] + [thisItem.name]), ) # return render_template( 'uploadicon.html', form=form, user=user, mode=mode, itemPathString=itemPathString, **finalPageFeatures, )
def unsetIconView(mode, itemPathString=''): """Route for explicit removal of thumbnail, if any, to various items.""" if (mode == 'au' and not g.settings['behaviour']['behaviour_admin_powers'] ['admin_is_god']['value']): request._onErrorUrl = url_for('adminHomeUsersView') raise OstracionError('This feature is turned off in the configuration') else: user = g.user form = UploadIconForm() db = dbGetDatabase() # tempFileDirectory = g.settings['system']['system_directories'][ 'temp_directory']['value'] fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] # if mode == 'b': modeName = 'box' boxPath = splitPathString(itemPathString) thisItem = getBoxFromPath(db, boxPath, user) parentBox = None elif mode == 'f': modeName = 'file' fullPath = splitPathString(itemPathString) boxPath, fileName = fullPath[:-1], fullPath[-1] parentBox = getBoxFromPath(db, boxPath, user) thisItem = getFileFromParent(db, parentBox, fileName, user) elif mode == 'l': modeName = 'link' fullPath = splitPathString(itemPathString) boxPath, linkName = fullPath[:-1], fullPath[-1] parentBox = getBoxFromPath(db, boxPath, user) thisItem = getLinkFromParent(db, parentBox, linkName, user) elif mode == 'u': modeName = 'user' parentBox = None thisItem = user elif mode == 'au': modeName = 'adminuser' if userIsAdmin(db, user): thisItem = dbGetUser(db, itemPathString) parentBox = None else: raise OstracionError('Insufficient permissions') elif mode == 's': modeName = 'settingIcon' if userIsAdmin(db, user): settingGroupId, settingId = itemPathString.split('/') thisItem = g.settings['image'][settingGroupId][settingId] parentBox = None else: raise OstracionError('Insufficient permissions') else: raise RuntimeError('Unknown mode encountered') # storageSuccess = storeFileAsThumbnail( db, fileToSave=None, mode=mode, thumbnailFormat=determineThumbnailFormatByModeAndTarget( db, mode, thisItem, ), targetItem=thisItem, parentBox=parentBox, user=user, tempFileDirectory=tempFileDirectory, fileStorageDirectory=fileStorageDirectory, ) if not storageSuccess: raise OstracionError('Could not unset the icon') # if mode in {'f', 'b', 'l'}: return redirect( url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1] if mode == 'b' else boxPath[1:]), )) elif mode == 'u': return redirect(url_for('userProfileView', )) elif mode == 'au': return redirect(url_for('adminHomeUsersView', )) elif mode == 's': return redirect(url_for('adminHomeSettingsImagesView', )) else: raise RuntimeError('Unknown mode encountered')
def loginView(): """Login view.""" user = g.user db = dbGetDatabase() g._onErrorUrl = url_for('loginView') if user is not None and user.is_authenticated: return redirect(url_for('indexView')) form = LoginForm() loginProtectionSeconds = int(g.settings['behaviour']['behaviour_security'] ['login_protection_seconds']['value']) if form.validate_on_submit(): # loginWaitSeconds = secondsToWaitBeforeLogin( db, request.remote_addr, doWrite=True, loginProtectionSeconds=loginProtectionSeconds, hashSalt=g.settings['behaviour']['behaviour_security'] ['ip_addr_hashing_salt']['value'], ) if loginWaitSeconds <= 0: # qUser = dbGetUser(db, form.username.data) if qUser and qUser.checkPassword(form.password.data): # newUser = User(**qUser.asDict()) newUser.last_login = datetime.datetime.now() dbUpdateUser(db, newUser, qUser) # login_user(qUser) # flashMessage( 'Success', 'Login successful', 'welcome, %s!' % (qUser.fullname, ), ) # return redirect(url_for('indexView')) else: g._onErrorUrl = url_for( 'loginView', username=form.username.data, ) raise OstracionError('invalid username or password') else: raise OstracionWarning( ('Automated repeated login protection. ' 'Please wait %i seconds ...') % int(loginWaitSeconds)) else: requestParams = request.args form.username.data = applyDefault( form.username.data, requestParams.get('username', ''), ) appShortName = g.settings['behaviour']['behaviour_appearance'][ 'application_short_name']['value'] iconUrl = makeSettingImageUrl(g, 'user_images', 'login') return render_template( 'login.html', form=form, user=user, pageTitle=loginTitle(g), pageSubtitle=loginSubtitle(g), iconUrl=iconUrl, )
def load_user(uid): """Given an ID, fetch the DB and return the corresponding user, if any.""" db = dbGetDatabase() return dbGetUser(db, uid)