def updateBoxThumbnail( db, box, tId, tMT, user, fileStorageDirectory, accountDeletionInProgress=False, skipCommit=False): """ Update the thumbnail info for a box. Return a list of zero or one full paths for deletion.""" prevId = box.icon_file_id if prevId != '': delQueue = [ fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory) ] else: delQueue = [] # box.icon_file_id = tId if tId is not None else '' box.icon_mime_type = tMT if tMT is not None else '' box.icon_file_id_username = (user.username if not accountDeletionInProgress else '') dbUpdateRecordOnTable( db, 'boxes', box.asDict(), dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() return delQueue
def updateUserThumbnail( db, targetUser, tId, tMT, user, fileStorageDirectory, skipCommit=False): """ Update the thumbnail info for a user. Return a list of zero or one full paths for deletion.""" prevId = targetUser.icon_file_id if prevId != '': delQueue = [ fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory) ] else: delQueue = [] # targetUser.icon_file_id = tId if tId is not None else '' targetUser.icon_mime_type = tMT if tMT is not None else '' targetUser.icon_file_id_username = user.username dbUpdateRecordOnTable( db, 'users', targetUser.asDict(), dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() return delQueue
def dbPunchRichTicket(db, richTicket, numPunches=1, skipCommit=False): """ Punch a ticket, i.e. mark it as been used once more. We assume the ticket has been read during this database session, i.e. it reflects current DB data. """ if richTicket['redeemable']: # newTicketDict = recursivelyMergeDictionaries( { 'times_redeemed': (richTicket['ticket'].times_redeemed + numPunches), 'last_redeemed': datetime.datetime.now(), }, defaultMap=richTicket['ticket'].asDict(), ) dbUpdateRecordOnTable( db, 'tickets', newTicketDict, dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() else: if richTicket['exhausted']: raise OstracionError('Ticket is exhausted') else: raise OstracionError('Cannot punch a non-redeemable ticket')
def dbToggleBoxRolePermissionBit(db, thisBox, roleClass, roleId, bit, user, skipCommit=False): """ Toggle (t<-->f) a bit from an existing permission set (a role) for a box. """ roleKey = (roleClass, roleId) if thisBox.box_id == '' and roleKey == ('system', 'admin'): raise OstracionError('Cannot edit root box permissions for admin') elif thisBox.box_id == '' and roleKey == ('system', 'anonymous'): raise OstracionError('Cannot edit root box permissions for anonymous') else: if userIsAdmin(db, user): foundRecords = list( dbRetrieveRecordsByKey( db, 'box_role_permissions', { 'box_id': thisBox.box_id, 'role_class': roleClass, 'role_id': roleId, }, dbTablesDesc=dbSchema, )) if len(foundRecords) > 0: foundBRP = BoxRolePermission(**foundRecords[0]) lBit = bit.lower() newBRPDict = recursivelyMergeDictionaries( {lBit: 1 if not foundBRP.booleanBit(lBit) else 0}, defaultMap={ k: v for k, v in foundBRP.asDict().items() if k in {'box_id', 'role_class', 'role_id'} }, ) try: dbUpdateRecordOnTable( db, 'box_role_permissions', newBRPDict, dbTablesDesc=dbSchema, allowPartial=True, ) if not skipCommit: db.commit() except Exception as e: db.rollback() raise e else: raise OstracionError('Box role permission does not exist') else: raise OstracionError('Insufficient permissions')
def secondsToWaitBeforeLogin(db, ipAddress, doWrite, loginProtectionSeconds, hashSalt, skipCommit=False): """ Check at once if the record exists and if the login is attemptable. Moreover update/insert the attempted-login entry in all cases and finally (optionally) commit. """ # atLogin = AttemptedLogin( sender_hash=hashOfIpAddress(ipAddress, hashSalt=hashSalt), datetime=datetime.datetime.now(), ) # prevLoginDict = dbRetrieveRecordByKey( db, 'attempted_logins', {'sender_hash': atLogin.sender_hash}, dbTablesDesc=dbSchema, ) if prevLoginDict is not None: prevLogin = AttemptedLogin(**prevLoginDict) else: prevLogin = None # if (prevLogin is None or (datetime.datetime.now() - prevLogin.datetime).seconds >= loginProtectionSeconds): secondsToWait = 0 else: secondsToWait = loginProtectionSeconds - (datetime.datetime.now() - prevLogin.datetime).seconds # if doWrite and secondsToWait <= 0: if prevLogin is None: dbAddRecordToTable( db, 'attempted_logins', atLogin.asDict(), dbTablesDesc=dbSchema, ) else: dbUpdateRecordOnTable( db, 'attempted_logins', atLogin.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() # return secondsToWait
def dbUpdateUser(db, newUser, user, skipCommit=False): """Update some columns of a user row.""" if newUser.username != '': dbUpdateRecordOnTable( db, 'users', newUser.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() else: raise OstracionError('Cannot alter system user')
def updateBox( db, path, newBox, user, accountDeletionInProgress=False, skipCommit=False): """ Update the fields of a box on DB.""" # # we check box_id is unchanged for updating prevBox = getBoxFromPath( db, path, user, accountDeletionInProgress=accountDeletionInProgress, ) parentBox = getBoxFromPath( db, path[:-1], user, accountDeletionInProgress=accountDeletionInProgress, ) if newBox.box_id != prevBox.box_id: raise RuntimeError('Box ID mismatch') else: if ( not accountDeletionInProgress and not userHasPermission(db, user, prevBox.permissions, 'w')): raise OstracionError('User is not allowed to edit box') else: if not isNameUnderParentBox( db, parentBox, newBox.box_name, excludedIds=[('box', newBox.box_id)]): newBoxItem = Box(**{ k: v for k, v in newBox.asDict().items() if k not in { 'dvector_box_name', 'dvector_title', 'dvector_description', } }) dbUpdateRecordOnTable( db, 'boxes', newBoxItem.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() else: raise OstracionError('Name already exists')
def updateLink( db, boxPath, prevLinkName, newLink, user, accountDeletionInProgress=False, skipCommit=False): """ Update the fields of a link object on DB.""" # # we check link_id is unchanged parentBox = getBoxFromPath( db, boxPath, user, accountDeletionInProgress=accountDeletionInProgress, ) prevLink = getLinkFromParent( db, parentBox, prevLinkName, user, accountDeletionInProgress=accountDeletionInProgress, ) if newLink.link_id != prevLink.link_id: raise RuntimeError('Link ID mismatch') else: if (not accountDeletionInProgress and not userHasPermission(db, user, parentBox.permissions, 'w')): raise OstracionError('User is not allowed to edit link') else: if not isNameUnderParentBox( db, parentBox, newLink.name, excludedIds=[('link', prevLink.link_id)]): newLinkItem = Link(**{ k: v for k, v in newLink.asDict().items() if k not in { 'dvector_name', 'dvector_description', 'dvector_title', } }) dbUpdateRecordOnTable( db, 'links', newLinkItem.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() else: raise OstracionError('Name already exists')
def dbUpdateRole(db, newRole, user): """Update role metadata on DB.""" if userIsAdmin(db, user): dbUpdateRecordOnTable( db, 'roles', { k: v for k, v in newRole.asDict().items() if k in {'description', 'role_class', 'role_id'} }, dbTablesDesc=dbSchema, allowPartial=True, ) db.commit() else: raise OstracionError('Insufficient permissions')
def updateSettingThumbnail(db, richSetting, tId, tMT, user, fileStorageDirectory, skipCommit=False): """ Update the image in a setting of type Image. This returns the (actual) filesystem deletion queue after taking care of the DB part. The new thumbnail ID is passed to this function ('' to reset). richSetting['setting'] is the DB object, at top-level we have enrichment info (such as the default/value resolved etc) """ prevId = richSetting['setting'].value if prevId != '': delQueue = [ fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory) ] else: delQueue = [] # newSettingDict = recursivelyMergeDictionaries( { 'value': tId if tId is not None else '', 'icon_mime_type': tMT if tMT is not None else '', }, defaultMap={ k: v for k, v in richSetting['setting'].asDict().items() if k in {'value', 'icon_mime_type', 'group_id', 'id'} }, ) dbUpdateRecordOnTable( db, 'settings', newSettingDict, dbTablesDesc=dbSchema, allowPartial=True, ) # if not skipCommit: db.commit() return delQueue
def moveFile( db, file, srcBox, dstBox, user, fileStorageDirectory, skipCommit=False): """ Move a file between boxes: also check for w permission on both boxes, check that no box in dstBox yields a name clash.""" if file.box_id != srcBox.box_id: raise OstracionError('Parent mismatch between file and source box') elif srcBox.box_id == dstBox.box_id: raise OstracionError('Source and destination are the same') else: if all([ userHasPermission(db, user, srcBox.permissions, 'w'), userHasPermission(db, user, dstBox.permissions, 'w'), ]): # fileName = file.name if isNameUnderParentBox(db, dstBox, fileName): raise OstracionError( 'Destination box contains an item with same name' ) else: # now can the actual MV be performed newFile = recursivelyMergeDictionaries( {'box_id': dstBox.box_id}, defaultMap=file.asDict(), ) dbUpdateRecordOnTable( db, 'files', newFile, dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() # messages = [] return messages else: raise OstracionError('User has no write permission')
db, tName, model.asDict(), dbTablesDesc=dbSchema, ) print('done ', end='') else: # careful override and update back mergedDict = recursivelyMergeDictionaries( {'value': itemDictFound['value']}, defaultMap=model.asDict(), ) print('updating... ', end='') dbUpdateRecordOnTable( db, tName, mergedDict, dbTablesDesc=dbSchema, ) print('done ', end='') print('#') print(' * done.') elif tName == 'links': # we may have to add a field columns = dbQueryColumns(db, tName) if 'title' not in columns: # must add 'title' and upgrade existing records print(' * Adding "title" column') db.execute('ALTER TABLE links ADD COLUMN title TEXT;') db.execute( 'ALTER TABLE links ADD COLUMN dvector_title TEXT;') print(' * Upgrading entries ... ', end='')
def dbUpdateStandardSettingDict(db, enrichedSetting, newFormValue, user, skipCommit=False): """ Update a non-image setting with a new value (taking care of deforming before the actual writing to DB). """ if userIsAdmin(db, user): deformedValue = _deformSettingValueByType( enrichedSetting['setting'].type, newFormValue, ) newSettingDict = recursivelyMergeDictionaries( { 'value': deformedValue, }, defaultMap={ k: v for k, v in enrichedSetting['setting'].asDict().items() if k in {'value', 'icon_mime_type', 'group_id', 'id'} }) # if (enrichedSetting['setting'].type == 'string' and enrichedSetting['metadata'].get('is_text_image', False)): # we handle the special case of deleting outdated # image-text that may have been produced # # this largerSettingDict is used only to determine the nextValue largerNextSettingDict = recursivelyMergeDictionaries( { 'value': deformedValue, }, defaultMap={ k: v for k, v in enrichedSetting['setting'].asDict().items() if k in {'value', 'type', 'default_value'} }) # prevValue = enrichedSetting['value'] nextValue = _getValueFromSetting(Setting(**largerNextSettingDict)) # if there is a picture-with-text to be superseded, delete it prevPath, prevTitle, prevTag = determineManagedTextImagePath( prevValue, prefix='%s/%s' % ( enrichedSetting['setting'].group_id, enrichedSetting['setting'].id, ), ) nextPath, nextTitle, nextTag = determineManagedTextImagePath( nextValue, prefix='%s/%s' % ( enrichedSetting['setting'].group_id, enrichedSetting['setting'].id, ), ) if prevTag != nextTag: prevFileName = os.path.join( prevPath, prevTitle, ) if os.path.isfile(prevFileName): # delete the old, unused file os.remove(prevFileName) # dbUpdateRecordOnTable(db, 'settings', newSettingDict, dbTablesDesc=dbSchema, allowPartial=True) if not skipCommit: db.commit() else: raise OstracionError('Insufficient permissions')
def moveBox(db, box, srcBox, dstBox, user, skipCommit=False): """ Move 'box' from being contained in 'srcBox' to being contained in 'dstBox' (with all involved permission checks). """ if box.box_id == '': # nobody can touch root raise OstracionError('Cannot act on this object') else: if box.parent_id != srcBox.box_id: # consistency in request raise RuntimeError( 'Parent mismatch between box to move and source box' ) else: if box.parent_id == dstBox.box_id: raise OstracionError( 'Source and destination box are the same' ) else: if not canDeleteBox(db, box, srcBox, user): # must be able to remove from source raise OstracionError( 'Insufficient permissions to move box away' ) else: if not all( userHasPermission( db, user, dstBox.permissions, prm ) for prm in {'w', 'c'}): # must be able to upload and create to dest raise OstracionError( 'Insufficient permissions to move boxes ' 'to the destination box' ) else: # name clash check for destination if isNameUnderParentBox(db, dstBox, box.box_name): raise OstracionError( 'An object with that name ' 'exists already' ) else: # check against box incest if isAncestorBoxOf(db, box, dstBox): raise OstracionError( 'Cannot move box to a sub-box of itself' ) else: # perform the move newBox = recursivelyMergeDictionaries( {'parent_id': dstBox.box_id}, defaultMap=box.asDict(), ) dbUpdateRecordOnTable( db, 'boxes', newBox, dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() return []