def create_password_reset_request(db_session, username): try: user = db_session.query(User). \ filter_by(username=username).one() except orm_exceptions.NoResultFound: raise HTTPRequestError(404, 'User not found') # verify if this user have an active password reset request old_request = db_session.query(PasswordRequest). \ filter_by(user_id=user.id).one_or_none() if old_request and check_request_validity(db_session, old_request): raise HTTPRequestError(409, 'You have a password reset' ' request in progress') request_dict = { 'user_id': user.id, 'link': str(binascii.hexlify(os.urandom(16)), 'ascii') } password_request = PasswordRequest(**request_dict) db_session.add(password_request) with open('templates/passwordReset.html', 'r') as f: html = f.read() reset_link = conf.resetPwdView + request_dict['link'] html = html.format(name=user.name, link=reset_link) send_mail(user.email, 'Password Reset', html)
def update_perm(db_session, permission: str, perm_data, requester): """ Updates all information about a permission (excluding name and ID, of course). :param db_session: The postgres session to be used. :param permission: String The permission name or ID. :param perm_data: New information for this permission. :param requester: Who is creating this user. This is a dictionary with two keys: "userid" and "username". :return: :raises HTTPRequestError: Can't edit a system permission. """ perm_data = { k: perm_data[k] for k in perm_data if k in Permission.fillable } check_perm(perm_data) try: perm = Permission.get_by_name_or_id(permission) if perm.type == PermissionTypeEnum.api: if 'name' in perm_data.keys() and perm.name != perm_data['name']: raise HTTPRequestError(400, "permission name can't be changed") for key, value in perm_data.items(): setattr(perm, key, value) db_session.add(perm) LOGGER.info( f"permission {perm.name} updated by {requester['username']}") LOGGER.info(perm_data) db_session.commit() else: raise HTTPRequestError(405, "Can't edit a system permission ") except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No permission found with this ID")
def delete_perm(db_session, permission: str, requester): """ Removes a permission from the system :param db_session: The postgres session to be used. :param permission: String The permission to be removed (name or ID). :param requester: Who is creating this user. This is a dictionary with two keys: "userid" and "username". :return: :raises HTTPRequestError: Can't delete a system permission. """ try: perm = Permission.get_by_name_or_id(permission) if perm.type == PermissionTypeEnum.api: db_session.execute( UserPermission.__table__.delete( UserPermission.permission_id == perm.id)) db_session.execute( GroupPermission.__table__.delete( GroupPermission.permission_id == perm.id)) cache.delete_key(action=perm.method, resource=perm.path) LOGGER.info( f"permission {perm.name} deleted by {requester['username']}") LOGGER.info(perm.safe_dict()) db_session.delete(perm) db_session.commit() MVUserPermission.refresh() MVGroupPermission.refresh() else: raise HTTPRequestError(405, "Can't delete a system permission") except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No permission found with this ID or name")
def deleteUser(dbSession, user, requester): try: user = User.getByNameOrID(user) if user.id == requester['userid']: raise HTTPRequestError(400, "a user can't remove himself") dbSession.execute( UserPermission.__table__.delete(UserPermission.user_id == user.id) ) dbSession.execute( UserGroup.__table__.delete(UserGroup.user_id == user.id) ) cache.deleteKey(userid=user.id) # The user is not hardDeleted. # it should be copied to inactiveUser table inactiveTables.PasswdInactive.createInactiveFromUser(dbSession, user,) inactiveTables.UserInactive.createInactiveFromUser(dbSession, user, requester['userid']) passwd.expirePasswordResetRequests(dbSession, user.id) dbSession.delete(user) log().info('user ' + user.username + ' deleted by ' + requester['username'], user.safeDict()) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No user found with this ID")
def createUser(dbSession, user, requester): # drop invalid fields user = {k: user[k] for k in user if k in User.fillable} checkUser(user) anotherUser = dbSession.query(User.id) \ .filter_by(username=user['username']).one_or_none() if anotherUser: raise HTTPRequestError(400, "username '" + user['username'] + "' is in use.") anotherUser = dbSession.query(User.id) \ .filter_by(email=user['email']).one_or_none() if anotherUser: raise HTTPRequestError(400, "Email '" + user['email'] + "' is in use.") if conf.emailHost == 'NOEMAIL': user['salt'], user['hash'] = passwd.createPwd(conf.temporaryPassword) user['created_by'] = requester['userid'] newUser = User(**user) log().info('user ' + user['username'] + ' created by ' + requester['username'], newUser.safeDict()) return newUser
def getJwtPayload(rawJWT): if not rawJWT: raise HTTPRequestError(401, "not authorized") # remove the bearer of the token splittedToken = rawJWT.split(' ') if len(splittedToken) > 1: rawJWT = splittedToken[1] try: jwtPayload = jwt.decode(rawJWT, verify=False) except jwt.exceptions.DecodeError: raise HTTPRequestError(401, "Corrupted JWT") try: user_id = jwtPayload['userid'] except KeyError: raise HTTPRequestError(401, "Invalid JWT payload") # now that we know the user, we know the secret # and can check the jwt signature if conf.checkJWTSign: try: user = dbSession.query(User). \ filter_by(user_id=jwtPayload['userid']).one() options = { 'verify_exp': False, } jwt.decode(rawJWT, user.secret, algorithm='HS256', options=options) except (jwt.exceptions.DecodeError, sqlalchemy.orm.exc.NoResultFound): raise HTTPRequestError(401, "Invalid JWT signaure") except sqlalchemy.exc.DBAPIError: raise HTTPRequestError(500, 'Problem connecting to database') return jwtPayload
def updateUser(dbSession, user, updatedInfo, requester): # Drop invalid fields updatedInfo = { k: updatedInfo[k] for k in updatedInfo if k in User.fillable } oldUser = User.getByNameOrID(user) if 'username' in updatedInfo.keys() \ and updatedInfo['username'] != oldUser.username: raise HTTPRequestError(400, "usernames can't be updated") checkUser(updatedInfo) # Verify if the email is in use by another user if 'email' in updatedInfo.keys() and updatedInfo['email'] != oldUser.email: anotherUser = dbSession.query(User) \ .filter_by(email=updatedInfo['email']) \ .one_or_none() if anotherUser: raise HTTPRequestError(400, "email already in use") log().info('user ' + oldUser.username + ' updated by ' + requester['username'], {'oldUser': oldUser.safeDict(), 'newUser': updatedInfo}) if 'name' in updatedInfo.keys(): oldUser.name = updatedInfo['name'] if 'service' in updatedInfo.keys(): oldUser.service = updatedInfo['service'] if 'email' in updatedInfo.keys(): oldUser.email = updatedInfo['email'] return oldUser
def update(db_session, user, new_password): check_password_format(user, new_password) # check actual password if user.hash and (user.hash == crypt(new_password, user.salt, 1000).split('$').pop()): raise HTTPRequestError( 400, "Please, choose a password" " that was not used before.") # check all old password from database if conf.passwdHistoryLen > 0: oldpwds = (db_session.query(PasswdInactive).filter_by( user_id=user.id).order_by( PasswdInactive.deletion_date.desc()).limit( conf.passwdHistoryLen)) for pwd in oldpwds: if pwd.hash == crypt(new_password, pwd.salt, 1000).split('$').pop(): raise HTTPRequestError( 400, "Please, choose a password" " that was not used before") PasswdInactive.createInactiveFromUser(db_session, user) return create_pwd(new_password)
def add_user_group(db_session, user, group, requester): try: user = User.get_by_name_or_id(user) except orm_exceptions.NoResultFound: raise HTTPRequestError(404, f"No user found with this ID or name: {user}") try: group = Group.get_by_name_or_id(group) except orm_exceptions.NoResultFound: raise HTTPRequestError( 404, f"No group found with this ID or name: {group}") if db_session.query(UserGroup).filter_by(user_id=user.id, group_id=group.id).one_or_none(): raise HTTPRequestError(409, "User is already a member of the group") r = UserGroup(user_id=user.id, group_id=group.id) db_session.add(r) cache.delete_key(userid=user.id) user.reset_token() db_session.add(user) log().info( f"user {user.username} added to group {group.name} by {requester['username']}" ) db_session.commit()
def search_perm(db_session, path=None, method=None, permission=None): """ Retrieves a set of permissions from database. :param db_session: The postgres session to be used. :param path: Permission path, if any. :param method: Permission allowed methods, if any. :param permission: Permission verb (permit or deny), if any. :return: """ perm_query = db_session.query(Permission) if path: perm_query = perm_query.filter(Permission.path.like(f"%{path}%")) if method: perm_query = perm_query.filter(Permission.method.like(f"%{method}%")) if permission: if permission not in [p.value for p in PermissionEnum]: raise HTTPRequestError( 400, f"Invalid filter. Permission can't be {permission}") perm_query = perm_query.filter_by(permission=permission) perms = perm_query.all() if not perms: raise HTTPRequestError(404, "No results found with these filters") return perms
def check_password_format(user, password): password_len = len(password) if password_len < conf.passwdMinLen: raise HTTPRequestError(400, 'password must have at least ' + str(conf.passwdMinLen) + ' characters') if password_len > 512: raise HTTPRequestError(400, 'Calm down! 512 characters is the ' ' maximum password length') lower_pwd = password.lower() # check if the password can be guessed with user info if (SequenceMatcher(None, lower_pwd, user.username) .find_longest_match(0, password_len, 0, len(user.username)) .size > 4 or SequenceMatcher(None, lower_pwd, user.email) .find_longest_match(0, password_len, 0, len(user.email)) .size > 4 or SequenceMatcher(None, lower_pwd, user.name.lower()) .find_longest_match(0, password_len, 0, len(user.name.lower())) .size > 4): raise HTTPRequestError(400, 'Please, choose a password that is' ' harder to guess. Your user info may' ' give hints on this password') # check for dull sequences # like 'aaa' '123' 'abc' last_char = '\0' count_equals = 1 count_up = 1 count_down = 1 for c in password: if ord(c) == ord(last_char) + 1: count_up += 1 else: count_up = 1 if ord(c) == ord(last_char) - 1: count_down += 1 else: count_down = 1 if c == last_char: count_equals += 1 else: count_equals = 1 if count_equals == 3 or count_up == 3 or count_down == 3: raise HTTPRequestError(400, 'do not use passwords with ' ' easy to guess' ' character sequences') last_char = c # check vs a blacklist if password in password_blackList: raise HTTPRequestError(400, "This password can't be used, as it is " " in our blacklist of bad passwords")
def update_user(db_session, user: str, updated_info, requester) -> (dict, str): """ Updates all the information about a particular user. :param db_session: The postgres session to be used. :param user: The user ID to be updated. :param updated_info: The new data. :param requester: Who is requiring this update. :return: The old information (a dictionary containing the old information about the user and the old service. :raises HTTPRequestError: If the username is different from the original (this field cannot be updated). """ # Drop invalid fields updated_info = { k: updated_info[k] for k in updated_info if k in User.fillable } user = User.get_by_name_or_id(user) old_user = user.safe_dict() old_service = user.service if 'username' in updated_info.keys() \ and updated_info['username'] != user.username: raise HTTPRequestError(400, "usernames can't be updated") # check_user function needs username. updated_info['username'] = user.username check_user(updated_info) # Verify if the email is in use by another user if 'email' in updated_info.keys() and updated_info['email'] != user.email: if db_session.query(User).filter_by( email=updated_info['email']).one_or_none(): raise HTTPRequestError(400, "email already in use") log().info(f"user {user.username} updated by {requester['username']}") log().info({'oldUser': user.safe_dict(), 'newUser': updated_info}) # Update all new data. if 'name' in updated_info.keys(): user.name = updated_info['name'] if 'service' in updated_info.keys(): user.service = updated_info['service'] if 'email' in updated_info.keys(): user.email = updated_info['email'] db_session.add(user) db_session.commit() # Publish messages related to service creation/deletion if count_tenant_users(db_session, old_service) == 0: log().info(f"will emit tenant lifecycle event {old_service} - DELETE") Publisher.send_notification({"type": 'DELETE', 'tenant': old_service}) if count_tenant_users(db_session, user.service) == 1: log().info(f"will emit tenant lifecycle event {user.service} - CREATE") Publisher.send_notification({"type": 'CREATE', 'tenant': user.service}) return old_user, old_service
def checkRequest(pdpRequest): if 'action' not in pdpRequest.keys() or len(pdpRequest['action']) == 0: raise HTTPRequestError(400, "Missing action") if 'jwt' not in pdpRequest.keys() or len(pdpRequest['jwt']) == 0: raise HTTPRequestError(400, "Missing JWT") if 'resource' not in pdpRequest.keys() or len(pdpRequest['resource']) == 0: raise HTTPRequestError(400, "Missing resource")
def delete_user(db_session, username: str, requester): """ Deletes an user from the system :param db_session: The postgres session to be used :param username: String The user to be removed :param requester: Who is creating this user. This is a dictionary with two keys: "userid" and "username" :return: The removed user :raises HTTPRequestError: If the user tries to remove itself. :raises HTTPRequestError: Can't delete the admin user. :raises HTTPRequestError: If the user is not in the database. """ try: user = User.get_by_name_or_id(username) if user.id == requester['userid']: raise HTTPRequestError(400, "a user can't remove himself") elif user.username == 'admin': raise HTTPRequestError(405, "Can't delete the admin user") db_session.execute( UserPermission.__table__.delete(UserPermission.user_id == user.id)) db_session.execute( UserGroup.__table__.delete(UserGroup.user_id == user.id)) cache.delete_key(userid=user.id) # The user is not hardDeleted. # it should be copied to inactiveUser table inactiveTables.PasswdInactive.createInactiveFromUser( db_session, user, ) inactiveTables.UserInactive.createInactiveFromUser( db_session, user, requester['userid']) password.expire_password_reset_requests(db_session, user.id) db_session.delete(user) LOGGER.info(f"user {user.username} deleted by {requester['username']}") LOGGER.info(user.safe_dict()) kongUtils.remove_from_kong(user.username) MVUserPermission.refresh() MVGroupPermission.refresh() db_session.commit() if count_tenant_users(db_session, user.service) == 0: LOGGER.info( f"will emit tenant lifecycle event {user.service} - DELETE") Publisher.send_notification({ "type": 'DELETE', 'tenant': user.service }) return user except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No user found with this ID")
def check_group(group): if not group.get('name', ""): raise HTTPRequestError(400, 'Missing group name') if len(group['name']) > GroupLimits.name: raise HTTPRequestError(400, "Group name too long") if re.match(r'^[a-zA-Z0-9]+$', group['name']) is None: raise HTTPRequestError( 400, 'Invalid group name, only alphanumeric allowed') if 'desc' in group.keys() and len(group['desc']) > GroupLimits.description: raise HTTPRequestError(400, "Group description is too long")
def updatePerm(dbSession, permission, permData, requester): permData = {k: permData[k] for k in permData if k in Permission.fillable} checkPerm(permData) try: perm = Permission.getByNameOrID(permission) if 'name' in permData.keys() and perm.name != permData['name']: raise HTTPRequestError(400, "permission name can't be changed") for key, value in permData.items(): setattr(perm, key, value) dbSession.add(perm) log().info('permission ' + perm.name + ' updated by ' + requester['username'], permData) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No permission found with this ID")
def updateGroup(dbSession, group, groupData, requester): groupData = {k: groupData[k] for k in groupData if k in Group.fillable} checkGroup(groupData) try: group = Group.getByNameOrID(group) if 'name' in groupData.keys() and group.name != groupData['name']: raise HTTPRequestError(400, "groups name can't be changed") for key, value in groupData.items(): setattr(group, key, value) dbSession.add(group) log().info('group ' + group.name + ' updated by ' + requester['username'], groupData) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No group found with this ID")
def getUserDirectPermissions(dbSession, user): try: user = User.getByNameOrID(user) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No user found with this username or ID") return user.permissions
def update_group(db_session, group, group_data, requester): group_data = {k: group_data[k] for k in group_data if k in Group.fillable} check_group(group_data) try: group = Group.get_by_name_or_id(group) if 'name' in group_data.keys() and group.name != group_data['name']: raise HTTPRequestError(400, "groups name can't be changed") for key, value in group_data.items(): setattr(group, key, value) db_session.add(group) log().info( 'group ' + group.name + ' updated by ' + requester['username'], group_data) db_session.commit() except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No group found with this ID")
def get_group_permissions(db_session, group): try: group = Group.get_by_name_or_id(group) except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No group found with this name or ID") else: return group.permissions
def getGroupUsers(dbSession, group): try: group = Group.getByNameOrID(group) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No group found with this name or ID") else: return group.users
def getUserGrups(dbSession, user): try: user = User.getByNameOrID(user) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No user found with this username or ID") else: return user.groups
def reset_kong_secret(username, token_id): if conf.kongURL == 'DISABLED': return try: delete_response = requests.delete("%s/consumers/%s/jwt/%s" % (conf.kongURL, username, token_id)) if not (200 <= delete_response.status_code < 300): LOGGER.error("failed to delete key: %d %s" % (delete_response.status_code, delete_response.reason)) LOGGER.error(delete_response.json()) return None headers = {"content-type": "application/x-www-form-urlencoded"} response = requests.post('%s/consumers/%s/jwt' % (conf.kongURL, username), headers=headers) if not (200 <= response.status_code < 300): LOGGER.error("failed to create key: %d %s" % (response.status_code, response.reason)) LOGGER.error(response.json()) return None reply = response.json() return { 'key': reply['key'], 'secret': reply['secret'], 'kongid': reply['id'] } except ConnectionError: LOGGER.error("Failed to connect to kong") raise HTTPRequestError(500, "Failed to connect to kong")
def get_group_users(db_session, group): try: group = Group.getByNameOrID(group) except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No group found with this name or ID") else: return group.users
def get_user_groups(db_session, user): try: user = User.getByNameOrID(user) except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No user found with this username or ID") else: return user.groups
def create_group(db_session, group_data, requester): """ Create a new group :param db_session: The postgres session to be used. :param group_data: The group data. This is a simple dictionary with "name" and "description" keys. :param requester: Who is creating this user. This is a dictionary with two keys: "userid" and "username". :return: The new group. """ group_data = {k: group_data[k] for k in group_data if k in Group.fillable} check_group(group_data) if db_session.query( Group.name).filter_by(name=group_data['name']).one_or_none(): raise HTTPRequestError(400, f"Group name {group_data['name']} is in use.") group_data['created_by'] = requester['userid'] group = Group(**group_data) LOGGER.info(f"group {group.name} created by {requester['username']}") LOGGER.info(group.safe_dict()) db_session.add(group) db_session.commit() return group
def list_tenants(db_session): try: tenants = [] for tenant in db_session.query(User.service).distinct(): tenants.append(tenant[0]) return tenants except orm_exceptions.NoResultFound: raise HTTPRequestError(404, "No registered tenants found")
def removeFromKong(user): if conf.kongURL == 'DISABLED': return try: requests.delete("%s/consumers/%s" % (conf.kongURL, user)) except ConnectionError: LOGGER.error("Failed to connect to kong") raise HTTPRequestError(500, "Failed to connect to kong")
def removeGroupPermission(dbSession, group, permission, requester): try: group = Group.getByNameOrID(group) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No group found with this ID or name") try: perm = Permission.getByNameOrID(permission) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No permission found with this ID") try: relation = dbSession.query(GroupPermission) \ .filter_by(group_id=group.id, permission_id=perm.id).one() dbSession.delete(relation) cache.deleteKey(action=perm.method, resource=perm.path) log().info('permission ' + perm.name + ' removed from ' ' group ' + group.name + ' by ' + requester['username']) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "Group does not have this permission")
def removeUserGroup(dbSession, user, group, requester): try: user = User.getByNameOrID(user) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No user found with this ID or name") try: group = Group.getByNameOrID(group) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "No group found with this ID or name") try: relation = dbSession.query(UserGroup) \ .filter_by(user_id=user.id, group_id=group.id).one() dbSession.delete(relation) cache.deleteKey(userid=user.id) log().info('user ' + user.username + ' removed from ' + group.name + ' by ' + requester['username']) except sqlalchemy.orm.exc.NoResultFound: raise HTTPRequestError(404, "User is not a member of the group")