def dbGrantRoleToUser(db, role, targetUser, user): """Assign a role to a user, i.e. grant it.""" if userIsAdmin(db, user): grantedRoleKeys = {r.roleKey() for r in targetUser.roles} if role.can_user == 0: raise OstracionError('Role cannot be granted to users') elif role.roleKey() in grantedRoleKeys: raise OstracionError('Role is already granted to user') else: # newUserRole = UserRole( username=targetUser.username, role_class=role.role_class, role_id=role.role_id, ) dbAddRecordToTable( db, 'user_roles', newUserRole.asDict(), dbTablesDesc=dbSchema, ) # db.commit() 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 dbMakeUserInvitationTicket(db, ticketName, validityHours, userName, userFullName, userEmail, ticketMessage, user, urlRoot, settings): """ Generate a ticket to be used to create a user (with/out username pre-specified. This is an admin-only operation. """ if isTicketIssuablePerSettings(validityHours, 1, settings): ''' only admins can do this by design ''' if userIsAdmin(db, user): ticketId, securityCode = randomTicketNumbers(user) issueDate = datetime.datetime.now() metadata = { k: v for k, v in { 'username': userName, 'fullname': userFullName, 'email': userEmail, 'message': ticketMessage, }.items() if v is not None } expirationDate = None if validityHours is None else ( issueDate + datetime.timedelta(hours=validityHours)) newTicket = Ticket( ticket_id=ticketId, name=ticketName, security_code=securityCode, username=user.username, issue_date=issueDate, expiration_date=expirationDate, multiplicity=1, target_type='user', metadata=json.dumps(metadata), last_redeemed=None, times_redeemed=0, ) # dbAddRecordToTable( db, 'tickets', newTicket.asDict(), dbTablesDesc=dbSchema, ) db.commit() return makeTicketMagicLink(newTicket, urlRoot) else: raise OstracionError('Insufficient permissions') else: raise OstracionError( 'Ticket parameters not allowed under the current settings')
def makeFileInParent(db, parentBox, newFile): """ Create a file object in a box.""" if newFile.box_id != parentBox.box_id: raise RuntimeError('wrong parent box id in makeFileInParent') else: if not isBoxNameUnderParentBox(db, parentBox, newFile.name): dbAddRecordToTable( db, 'files', newFile.asDict(), dbTablesDesc=dbSchema, ) db.commit() else: raise OstracionError('Name already exists')
def dbMakeGalleryTicket(db, ticketName, validityHours, multiplicity, ticketMessage, box, boxPath, user, urlRoot, settings): """ Generate a gallery-view ticket on a box (with the specified ticket settings). """ if isTicketIssuablePerSettings(validityHours, multiplicity, settings): ticketId, securityCode = randomTicketNumbers(user) issueDate = datetime.datetime.now() metadata = { k: v for k, v in { 'box_id': box.box_id, 'box_path': boxPath, 'box_name': box.box_name, 'box_title': box.title, 'message': ticketMessage, }.items() if v is not None } expirationDate = None if validityHours is None else ( issueDate + datetime.timedelta(hours=validityHours)) newTicket = Ticket( ticket_id=ticketId, name=ticketName, security_code=securityCode, username=user.username, issue_date=issueDate, expiration_date=expirationDate, multiplicity=multiplicity, target_type='gallery', metadata=json.dumps(metadata), last_redeemed=None, times_redeemed=0, ) # dbAddRecordToTable( db, 'tickets', newTicket.asDict(), dbTablesDesc=dbSchema, ) db.commit() return makeTicketMagicLink(newTicket, urlRoot) else: raise OstracionError( 'Ticket parameters not allowed under the current settings')
def dbCreateUser(db, newUser, user): """Create a new user row.""" dbAddRecordToTable( db, 'users', newUser.asDict(), dbTablesDesc=dbSchema, ) # we read the config flag about new users having or not the 'ticketer' role # new_users_are_ticketer addTicketerRole = dbGetSetting( db, 'behaviour', 'behaviour_tickets', 'new_users_are_ticketer', user )['value'] if addTicketerRole: dbAddRecordToTable( db, 'user_roles', UserRole( username=newUser.username, role_class='system', role_id='ticketer', ).asDict(), dbTablesDesc=dbSchema, ) # user-tied role handling dbAddRecordToTable( db, 'roles', Role( role_id=newUser.username, description='%s user-role' % newUser.username, role_class='user', can_box=1, can_user=0, can_delete=0, ).asDict(), dbTablesDesc=dbSchema, ) dbAddRecordToTable( db, 'user_roles', UserRole( username=newUser.username, role_class='user', role_id=newUser.username, ).asDict(), dbTablesDesc=dbSchema, ) # db.commit()
def dbCreateRole(db, newRole, user): """Create a new role in DB.""" if userIsAdmin(db, user): if dbGetRole(db, newRole.role_class, newRole.role_id, user) is None: if newRole.can_delete == 0: raise OstracionError('Manual roles must be deletable') else: dbAddRecordToTable( db, 'roles', newRole.asDict(), dbTablesDesc=dbSchema, ) db.commit() else: raise OstracionWarning('Role "%s/%s" exists already' % newRole.roleKey()) else: raise OstracionError('Insufficient permissions')
def dbInsertBoxRolePermission(db, newBoxRolePermission, user, skipCommit=False): """Add a new permission-set (i.e. tied to a role) to a box.""" if newBoxRolePermission.box_id == '': raise OstracionError('Cannot add a permission to root box') else: # system/admin cannot be added/removed if newBoxRolePermission.roleKey() == ('system', 'admin'): raise OstracionError('Cannot add permissions for admin role') else: role = dbGetRole( db, newBoxRolePermission.role_class, newBoxRolePermission.role_id, user, ) if role is not None: # the role must have can_box attribute if role.can_box == 0: raise OstracionError( 'Cannot add permissions for this role') else: if userIsAdmin(db, user): try: dbAddRecordToTable( db, 'box_role_permissions', newBoxRolePermission.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() except Exception as e: db.rollback() raise e else: raise OstracionError('Insufficient permissions') else: raise OstracionError('Could not find role')
def makeBoxInParent(db, parentBox, newBox, user, skipCommit=False): """ Create a box, with name newBox, in parentBox on behalf of 'user'. Does all permission/name availability checks. """ # first we check user has permission to create boxes here if not userHasPermission(db, user, parentBox.permissions, 'c'): raise OstracionError('User is not allowed to create boxes') else: # then we check there are no children with same name in the parent if not isNameUnderParentBox(db, parentBox, newBox.box_name): # now we create dbAddRecordToTable( db, 'boxes', newBox.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() else: raise OstracionError('Name already exists')
def makeLinkInParent( db, user, parentBox, date, linkName, linkTitle, linkDescription, linkTarget, linkOptions={}): """ Create a new external link object in the specified box. Return a dummy value (True upon success, but it is the errors that are raised.) """ if userHasPermission(db, user, parentBox.permissions, 'w'): if not isNameUnderParentBox(db, parentBox, linkName): userName = user.username newLink = Link( box_id=parentBox.box_id, name=linkName, title=linkTitle, description=linkDescription, icon_file_id='', date=date, creator_username=userName, icon_file_id_username=userName, icon_mime_type='', metadata_username=userName, target=linkTarget, metadata_dict=linkOptions, ) dbAddRecordToTable( db, 'links', newLink.asDict(), dbTablesDesc=dbSchema, ) db.commit() else: raise OstracionError('Name already exists') else: raise OstracionError('User has no write permission')
def fixRoleTablesAddingRoleClass(db): """ Deal with the schema changes to bring 'roles' from primary key = role_id to primary_key = (role_class, role_id) by acting on all involved tables while preserving the contents thereof. Returns whether it did something or not as a bool """ rolesTableExists = dbTableExists(db, 'roles') roleColumns = dbQueryColumns(db, 'roles') if rolesTableExists else {} mustAct = all([ rolesTableExists, 'role_class' not in roleColumns, 'system' in roleColumns, ]) if not mustAct: return False else: print(' * Applying patch to roles') # get ready to dance targetSchema = { tempTableName(tName): tDesc for tName, tDesc in dbSchema.items() if tName in legacySchema } targetTableCreationOrder = { tempTableName(k): v for k, v in tableCreationOrder.items() } # create the transitional tables (new schema) for ntName, ntContents in sorted( targetSchema.items(), key=lambda tnc: targetTableCreationOrder[tnc[0]]): print(' * creating "%s" ' % ntName, end='') dbCreateTable( db, ntName, {k: v for k, v in ntContents.items() if k != 'foreign_keys'}, ) print('done.') # copy items table by table print(' * populating ...') for srcTName, srcSchema in sorted( legacySchema.items(), key=lambda tnc: tableCreationOrder[tnc[0]]): print(' - "%s" ...' % srcTName) # read all items from this srcTName and # copy it - with changes - onto tempTableName(srcTName) dstTName = tempTableName(srcTName) dstSchema = targetSchema[dstTName] for inRecordIndex, inRecord in enumerate(dbRetrieveAllRecords( db, srcTName, dbTablesDesc=legacySchema, )): print(' record[%i] ... ' % inRecordIndex, end='') outRecord = convertRoleRelatedRecord(db, legacySchema, srcTName, inRecord) dbAddRecordToTable( db, dstTName, outRecord, dbTablesDesc=targetSchema, ) print('inserted.') print(' - done "%s"' % srcTName) print(' * done populating.') # we drop the original tables and recreate them with the new schema print(' * dropping legacy tables ...') for srcTName, srcSchema in sorted( legacySchema.items(), key=lambda tnc: tableCreationOrder[tnc[0]], reverse=True): print(' - "%s" ... ' % srcTName, end='') dbDeleteTable(db, srcTName) print('dropped.') print(' * done dropping legacy tables.') # we recreate the tables with the new schema print(' * re-creating tables ...') for srcTName, _ in sorted( legacySchema.items(), key=lambda tnc: tableCreationOrder[tnc[0]]): print(' - "%s" ... ' % srcTName, end='') dbCreateTable( db, srcTName, dbSchema[srcTName], ) print('re-created.') print(' * done re-creating tables.') # we populate the recreated tables with the temporary tables' contents print(' * repopulating tables ...') for srcTName, srcSchema in sorted( legacySchema.items(), key=lambda tnc: tableCreationOrder[tnc[0]]): print(' - "%s" ... ' % srcTName) for recordIndex, record in enumerate(dbRetrieveAllRecords( db, tempTableName(srcTName), dbTablesDesc=targetSchema, )): print(' record [%i] ... ' % recordIndex, end='') dbAddRecordToTable( db, srcTName, record, dbTablesDesc=dbSchema, ) print('inserted.') print(' - done "%s"' % srcTName) print(' * done repopulating tables.') # we drop the temporary tables print(' * dropping temporary tables ...') for srcTName, srcSchema in sorted( legacySchema.items(), key=lambda tnc: tableCreationOrder[tnc[0]], reverse=True): print(' - "%s" ... ' % srcTName, end='') deleteeTName = tempTableName(srcTName) dbDeleteTable(db, deleteeTName) print('dropped.') print(' * done dropping temporary tables.') # return True
dbCreateTable(db, tName, tContents) print(' done', end='') else: print('already_there', end='') print('.') # record insertion if not tableFound: tcontents = initialDbValues.get(tName) if tcontents is not None: # ordinary new-table filling print(' * Populating') for item in tcontents['values']: model = tcontents['model'](**item) dbAddRecordToTable( db, tName, model.asDict(), dbTablesDesc=dbSchema, ) print(' - %s' % model) print(' * done.') else: # special handling of some tables if tName == 'settings': # here we add new settings and refresh some fields of the # existing ones (namely all but 'value') tcontents = initialDbValues.get(tName) print(' * Refreshing') for item in tcontents['values']: model = tcontents['model'](**item) print(' - %s : ' % model, end='') itemDictFound = dbRetrieveRecordByKey(