def pull_character(self, info): """This always updates all information on the character, so that we do not end up with inconsistencies. There is some weirdness that, if a user already has a key with full permissions, and adds a limited one, we'll erase information on that character. We should probably check for and refresh info from the most-permissioned key instead of this.""" from brave.core.character.model import EVEAlliance, EVECorporation, EVECharacter try: char = EVECharacter(identifier=info.characterID).save() new = True except NotUniqueError: char = EVECharacter.objects(identifier=info.characterID)[0] new = False if char.owner and self.owner != char.owner: log.warning("Security violation detected. Multiple accounts trying to register character %s, ID %d. " "Actual owner is %s. User adding this character is %s.", char.name, info.characterID, EVECharacter.objects(identifier=info.characterID).first().owner, self.owner) self.violation = "Character" # Mark both accounts as duplicates of each other. User.add_duplicate(self.owner, char.owner) return try: if self.mask.has_access(api.char.CharacterSheet.mask): info = api.char.CharacterSheet(self, characterID=info.characterID) elif self.mask.has_access(api.char.CharacterInfoPublic.mask): info = api.char.CharacterInfoPublic(self, characterID=info.characterID) except Exception: log.warning("An error occured while querying data for key %s.", self.key) if new: char.delete() raise char.corporation, char.alliance = self.get_membership(info) char.name = info.name if 'name' in info else info.characterName char.owner = self.owner if self not in char.credentials: char.credentials.append(self) char.race = info.race if 'race' in info else None char.bloodline = (info.bloodLine if 'bloodLine' in info else info.bloodline if 'bloodline' in info else None) char.ancestry = info.ancestry if 'ancestry' in info else None char.gender = info.gender if 'gender' in info else None char.security = info.security if 'security' in info else None char.titles = [strip_tags(i.titleName) for i in info.corporationTitles.row] if 'corporationTitles' in info else [] char.roles = [i.roleName for i in info.corporationRoles.row] if 'corporationRoles' in info else [] char.save() return char
def authenticate(identifier, password): """Given an e-mail address (or Yubikey OTP) and password, authenticate a user.""" ts = time() # Record the query = dict(active=True) # Gracefully handle extended characters in passwords. # The password storage algorithm works in binary. if isinstance(password, unicode): password = password.encode('utf8') # Build the MongoEngine query to find if '@' in identifier: query[b'email'] = identifier elif len(identifier) == 44: query[b'otp'] = identifier[:12] else: query[b'username'] = identifier user = User.objects(**query).first() if not user or not User.password.check(user.password, password) or ( user.rotp and len(user.otp) != 0 and not 'otp' in query): if user: LoginHistory(user, False, request.remote_addr).save() # Prevent basic timing attacks; always take at least one second to process. sleep(max(min(1 - (time() - ts), 0), 1)) return None # Validate Yubikey OTP if 'otp' in query: client = yubico.Yubico(config['yubico.client'], config['yubico.key'], boolean(config.get('yubico.secure', False))) try: status = client.verify(identifier, return_response=True) except: return None if not status: return None user.update(set__seen=datetime.utcnow()) # Record the fact the user signed in. LoginHistory(user, True, request.remote_addr).save() # Update the user's host user.host = request.remote_addr # Check for other accounts with this IP address if len(User.objects(host=request.remote_addr)) > 1: # Quite possibly the worst code ever for u in User.objects(host=request.remote_addr): User.add_duplicate(user, u, IP=True) user.save() return user.id, user
def post(self, **kw): data = Bunch(kw) try: data.key = int(data.key) if data.key <= int(config["core.minimum_key_id"]): return ( "json:", dict( success=False, message=_( "The key given (%d) must be above minimum reset floor value of %d. " "Please reset your EVE API Key." % (data.key, int(config["core.minimum_key_id"])) ), field="key", ), ) except ValueError: return "json:", dict(success=False, message=_("Key ID must be a number."), field="key") record = EVECredential(data.key, data.code, owner=user.id) try: record.save() # Necessary to guarantee that the pull finished before returning. record.pull() characters = [] for character in record.characters: characters.append(dict(identifier=character.identifier, name=character.name)) if request.is_xhr: return ( "json:", dict( success=True, message=_("Successfully added EVE API key."), identifier=str(record.id), key=record.key, code=record.code, characters=characters, violation=record.violation, ), ) except ValidationError: if request.is_xhr: return ( "json:", dict(success=False, message=_("Validation error: one or more fields are incorrect or missing.")), ) except NotUniqueError: if EVECredential.objects(key=data.key): # Mark both of these accounts as duplicates to each other. acc = User.objects(username=user.username).first() other = EVECredential.objects(key=data.key).first().owner User.add_duplicate(acc, other) return ( "json:", dict(success=False, message=_("This key has already been added to this or another account.")), ) raise HTTPFound(location="/key/")
def authenticate(identifier, password): """Given an e-mail address (or Yubikey OTP) and password, authenticate a user.""" ts = time() # Record the query = dict(active=True) # Gracefully handle extended characters in passwords. # The password storage algorithm works in binary. if isinstance(password, unicode): password = password.encode("utf8") # Build the MongoEngine query to find if "@" in identifier: query[b"email"] = identifier elif len(identifier) == 44: query[b"otp"] = identifier[:12] else: query[b"username"] = identifier user = User.objects(**query).first() if ( not user or not User.password.check(user.password, password) or (user.rotp and len(user.otp) != 0 and not "otp" in query) ): if user: LoginHistory(user, False, request.remote_addr).save() # Prevent basic timing attacks; always take at least one second to process. sleep(max(min(1 - (time() - ts), 0), 1)) return None # Validate Yubikey OTP if "otp" in query: client = yubico.Yubico( config["yubico.client"], config["yubico.key"], boolean(config.get("yubico.secure", False)) ) try: status = client.verify(identifier, return_response=True) except: return None if not status: return None user.update(set__seen=datetime.utcnow()) # Record the fact the user signed in. LoginHistory(user, True, request.remote_addr).save() # Update the user's host user.host = request.remote_addr # Check for other accounts with this IP address if len(User.objects(host=request.remote_addr)) > 1: # Quite possibly the worst code ever for u in User.objects(host=request.remote_addr): User.add_duplicate(user, u, IP=True) user.save() return user.id, user
def pull_character(self, info): """This always updates all information on the character, so that we do not end up with inconsistencies. There is some weirdness that, if a user already has a key with full permissions, and adds a limited one, we'll erase information on that character. We should probably check for and refresh info from the most-permissioned key instead of this.""" from brave.core.character.model import EVEAlliance, EVECorporation, EVECharacter try: char = EVECharacter(identifier=info.characterID).save() new = True except NotUniqueError: char = EVECharacter.objects(identifier=info.characterID)[0] new = False if char.owner and self.owner != char.owner: log.warning( "Security violation detected. Multiple accounts trying to register character %s, ID %d. " "Actual owner is %s. User adding this character is %s.", char.name, info.characterID, EVECharacter.objects( identifier=info.characterID).first().owner, self.owner) self.violation = "Character" # Mark both accounts as duplicates of each other. User.add_duplicate(self.owner, char.owner) return try: if self.mask.has_access(api.char.CharacterSheet.mask): info = api.char.CharacterSheet(self, characterID=info.characterID) elif self.mask.has_access(api.char.CharacterInfoPublic.mask): info = api.char.CharacterInfoPublic( self, characterID=info.characterID) except Exception: log.warning("An error occured while querying data for key %s.", self.key) if new: char.delete() raise char.corporation, char.alliance = self.get_membership(info) char.name = info.name if 'name' in info else info.characterName char.owner = self.owner if self not in char.credentials: char.credentials.append(self) char.race = info.race if 'race' in info else None char.bloodline = (info.bloodLine if 'bloodLine' in info else info.bloodline if 'bloodline' in info else None) char.ancestry = info.ancestry if 'ancestry' in info else None char.gender = info.gender if 'gender' in info else None char.security = info.security if 'security' in info else None char.titles = [ strip_tags(i.titleName) for i in info.corporationTitles.row ] if 'corporationTitles' in info else [] char.roles = [i.roleName for i in info.corporationRoles.row ] if 'corporationRoles' in info else [] char.save() return char
def post(self, **kw): data = Bunch(kw) try: data.key = int(data.key) if data.key <= int(config['core.minimum_key_id']): return 'json:', dict( success=False, message=_( "The key given (%d) must be above minimum reset floor value of %d. " "Please reset your EVE API Key." % (data.key, int(config['core.minimum_key_id']))), field='key') except ValueError: return 'json:', dict(success=False, message=_("Key ID must be a number."), field='key') record = EVECredential(data.key, data.code, owner=user.id) try: record.save() #Necessary to guarantee that the pull finished before returning. record.pull() characters = [] for character in record.characters: characters.append( dict(identifier=character.identifier, name=character.name)) if request.is_xhr: return 'json:', dict( success=True, message=_("Successfully added EVE API key."), identifier=str(record.id), key=record.key, code=record.code, characters=characters, violation=record.violation) except ValidationError: if request.is_xhr: return 'json:', dict( success=False, message= _("Validation error: one or more fields are incorrect or missing." ), ) except NotUniqueError: if EVECredential.objects(key=data.key): # Mark both of these accounts as duplicates to each other. acc = User.objects(username=user.username).first() other = EVECredential.objects(key=data.key).first().owner User.add_duplicate(acc, other) return 'json:', dict( success=False, message= _("This key has already been added to this or another account." ), ) raise HTTPFound(location='/key/')