class Game(db.Model): __tablename__ = 'games' id = db.Column(db.Integer, primary_key=True) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp()) def __repr__(self): return '<Game %r>' % self.id
class Message(db.Model): __tablename__ = 'messages' id = db.Column(db.Integer, primary_key=True) channel = db.relationship('Channel', back_populates='messages') channel_id = db.Column(db.Integer, db.ForeignKey('channels.id'), nullable=False) listener = db.relationship('Listener', back_populates='messages') listener_id = db.Column(db.Integer, db.ForeignKey('listeners.id')) content = db.Column(db.Text, nullable=False) encrypted_content = db.Column(db.Text, nullable=False) sender = db.Column(db.String, nullable=False) intercepted_at = db.Column(db.TIMESTAMP) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(),onupdate=func.current_timestamp()) def __repr__(self): return '<Message %r>' % self.id def create(content, channel_id, sender): message = Message(channel_id=channel_id,content=content,sender=sender, encrypted_content= Message.encrypt(content, 0)) db.session.add(message) try: db.session.commit() except IntegrityError as e: print(str(e)) raise ResourceMissing("Could not find channel {channel_id}, message was not delivered".format(channel_id=channel_id), 404) if message.channel.has_listeners(): # Assuming there is only one listener message.listener = message.channel.listeners[0] message.intercepted_at = datetime.now() message.encrypted_content = Message.encrypt(content, message.listener.decrypt_level()) db.session.commit() return message def serialize(self): # Return the encrypted messages to red team, not for blue team use return { 'id': self.id, 'channel_id': self.channel_id, 'intercepted_at': self.intercepted_at, 'created_at': self.created_at, 'sender': self.sender, 'content': self.encrypted_content } def encrypt(content, decrypt_level): length = len(content) decrypted_chars = [] for _ in range(math.ceil(length * (1-decrypt_level))): char_index = random.randint(0, length) if char_index not in decrypted_chars: decrypted_chars.append(char_index) return "".join([('*' if index in decrypted_chars else char) for index, char in enumerate(content)])
class Listener(db.Model): __tablename__ = 'listeners' id = db.Column(db.Integer, primary_key=True) channel = db.relationship('Channel', back_populates='listeners') channel_id = db.Column(db.Integer, db.ForeignKey('channels.id')) messages = db.relationship('Message', back_populates='listener', lazy=True) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp()) def __repr__(self): return '<Listener %r>' % self.id def find(instance_id): # Find instance by id return Listener.query.filter(Listener.id == instance_id).first() def create(): new_listener = Listener() db.session.add(new_listener) db.session.commit() return True def listen_to(self, channel_id): channel = Channel.query.filter(Channel.id == channel_id).first() if channel: self.channel = channel db.session.commit() return True else: raise ResourceMissing("No channel found with id %r" % channel_id, 404) def serialize_messages(self): return [message.serialize() for message in self.messages] def decrypt_level(self): intercepts = len(self.messages) if intercepts >= 10: return 1 elif intercepts == 0: return 0 else: return intercepts / 10.0
class Channel(db.Model): __tablename__ = 'channels' id = db.Column(db.Integer, primary_key=True) messages = db.relationship('Message', back_populates='channel', lazy=True) listeners = db.relationship('Listener', back_populates='channel', lazy=True) created_at =db.Column(db.TIMESTAMP, server_default=func.now()) updated_at =db.Column(db.TIMESTAMP, server_default=func.now(),onupdate=func.current_timestamp()) def __repr__(self): return '<Channel %r>' % self.name def create(): new_channel = Channel() db.session.add(new_channel) db.session.commit() return True def has_listeners(self): return len(self.listeners) > 0
class Player(db.Model): __tablename__ = 'players' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False, unique=True) agents = db.relationship('Agent', back_populates='player') description = db.Column(db.Text) api_key = db.Column(db.String) ip_address = db.Column(db.String) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(),onupdate=func.current_timestamp()) def __repr__(self): return '<Player %r>' % self.id def create(name, description): new_player = Player(name=name, description=description) db.session.add(new_player) new_player.generate_api_key() db.session.commit() return new_player def find_or_assign(ip_address): player = Player.query.filter(Player.ip_address == ip_address).first() if player: return player else: players = Player.query.filter(Player.ip_address == None).all() if len(players) == 0: raise FullGame("Sorry this game is already full", 423) player = random.choice(players) player.ip_address = ip_address db.session.commit() return player def current_user(api_key): return Player.query.filter(Player.api_key == api_key).first() def generate_api_key(self): self.api_key = '%030x' % random.randrange(16**30) def serialize_agents(self,requestor): if (requestor == 'Alpha' or requestor == 'Charlie') and self.name == 'Alpha': return [agent.serialize(requestor) for agent in self.agents if requestor == 'Alpha' or (not agent.in_safehouse() and requestor == 'Charlie')] elif (requestor == 'Charlie' or requestor == 'Bravo') and self.name == 'Charlie': return [agent.serialize(requestor) for agent in self.agents] else: raise Unauthenticated("You are not permitted to access this resource!", 401)
class Safehouse(db.Model): __tablename__ = 'safehouses' id = db.Column(db.Integer, primary_key=True) agents = db.relationship('Agent', back_populates='safehouse') packages = db.relationship('Package', back_populates='safehouse') x = db.Column(db.Integer, nullable=False, index=True) y = db.Column(db.Integer, nullable=False, index=True) dropoff = db.Column(db.Boolean, server_default='t', nullable=False) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp()) def __repr__(self): return '<Safehouse %r>' % self.id def find(instance_id): # Find instance by id return Safehouse.query.filter(Safehouse.id == instance_id).first() def create(position, dropoff): new_safehouse = Safehouse(dropoff=dropoff, x=position[0], y=position[1]) db.session.add(new_safehouse) db.session.commit() return new_safehouse def all(): return Safehouse.query.all() def has_packages(self): return len(self.packages) > 0 def serialize(self): return { 'id': self.id, 'x': self.x, 'y': self.y, 'dropoff': self.dropoff, 'packages': len(self.packages) }
class Package(db.Model): __tablename__ = 'packages' id = db.Column(db.Integer, primary_key=True) agent = db.relationship('Agent', back_populates='packages') agent_id = db.Column(db.Integer, db.ForeignKey('agents.id'), index=True) safehouse = db.relationship('Safehouse', back_populates='packages') safehouse_id = db.Column(db.Integer, db.ForeignKey('safehouses.id'), index=True) x = db.Column(db.Integer, nullable=False, index=True) y = db.Column(db.Integer, nullable=False, index=True) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp()) def __repr__(self): return '<Package %r>' % self.id def find(instance_id): # Find instance by id return Package.query.filter(Package.id == instance_id).first() def all(): return Package.query.all() def create(position): new_package = Package(x=position[0], y=position[1]) db.session.add(new_package) db.session.commit() return new_package def in_safehouse(self): return not self.safehouse_id == None def pickup(self, agent): self.agent = agent db.session.commit() def drop(self): self.agent_id = None db.session.commit() def enter_safehouse(self, safehouse): self.safehouse = safehouse self.agent_id = None db.session.commit() def move(self, x, y): if x == self.x and y == self.y: # We are already here, don't waste db cycle return self.x = x self.y = y db.session.commit() def serialize(self): return { 'id': self.id, 'x': self.x, 'y': self.y, 'agent_id': self.agent_id, 'in_safehouse': self.in_safehouse() }
class Agent(db.Model): __tablename__ = 'agents' id = db.Column(db.Integer, primary_key=True) player = db.relationship('Player', back_populates='agents') player_id = db.Column(db.Integer, db.ForeignKey('players.id'), nullable=False, index=True) safehouse = db.relationship('Safehouse', back_populates='agents') safehouse_id = db.Column(db.Integer, db.ForeignKey('safehouses.id'), index=True) packages = db.relationship('Package', back_populates='agent') x = db.Column(db.Integer, nullable=False, index=True) y = db.Column(db.Integer, nullable=False, index=True) alive = db.Column(db.Boolean, server_default='t', nullable=False) created_at = db.Column(db.TIMESTAMP, server_default=func.now()) updated_at = db.Column(db.TIMESTAMP, server_default=func.now(), onupdate=func.current_timestamp()) def __repr__(self): return '<Agent %r>' % self.id def find(instance_id): # Find instance by id return Agent.query.filter(Agent.id == instance_id).first() def create(player, position): new_agent = Agent(player_id=player.id, x=position[0], y=position[1]) db.session.add(new_agent) db.session.commit() return new_agent def has_packages(self): return len(self.packages) > 0 def in_safehouse(self): return not self.safehouse_id == None def kill(self): self.alive = False [package.drop() for package in self.packages] db.session.commit() def legal_moves(self): moves = [(self.x + 1, self.y), (self.x - 1, self.y), (self.x, self.y + 1), (self.x, self.y - 1)] return [ move for move in moves if move[0] > -1 and move[0] < 100 and move[1] > -1 and move[1] < 100 ] def serialize(self, requestor): if requestor == 'Alpha' and self.player.name == 'Alpha': return { 'id': self.id, 'x': self.x, 'y': self.y, 'safehouse': self.in_safehouse(), 'packages': len(self.packages) } elif requestor == 'Charlie' and (self.player.name == 'Alpha' or self.player.name == 'Charlie'): return {'id': self.id, 'x': self.x, 'y': self.y} elif requestor == 'Bravo' and (self.player.name == 'Charlie'): return {'id': self.id, 'x': self.x, 'y': self.y} def leave_safehouse(self): self.safehouse_id = None db.session.commit() def enter_safehouse(self, safehouse): self.safehouse = safehouse db.session.commit() def move(self, x, y): if not self.alive: raise IsDead("Agent {agent_id} is dead".format(agent_id=self.id), 404) if (x, y) not in self.legal_moves(): raise IllegalMove( "Agent {agent_id} can't move to {x},{y}, only to [{legal_moves}]" .format(agent_id=self.id, x=x, y=y, legal_moves=",".join( [str(move) for move in self.legal_moves()])), 404) self.x = x self.y = y db.session.commit() self.resolve_move() def resolve_move(self): if self.safehouse_id != None and self.player.name != "Charlie": self.leave_safehouse() # Quite a few things need to happen and we need to get the priority right #First is there any agents already here? other_agents = Agent.query.filter(Agent.x == self.x and Agent.y == self.y and Agent.id != self.id and Agent.alive == True).all() for agent in other_agents: if agent.player.name == "Charlie" and (self.player.name == "Bravo" or self.player.name == "Alpha"): self.kill() raise IsDead( "Agent {agent_id} is dead".format(agent_id=self.id), 404) elif self.player.name == "Charlie" and not agent.in_safehouse( ) and (agent.player.name == "Bravo" or agent.player.name == "Alpha"): agent.kill() if self.player.name == "Charlie": # Charlie does not care about packages or safehouses return True packages = Package.query.filter( Package.x == self.x and Package.y == self.y and Package.agent_id == None and Package.safehouse_id == None).all() [package.pickup(self) for package in packages] [package.move(self.x, self.y) for package in self.packages] safehouse = Safehouse.query.filter(Safehouse.x == self.x and Safehouse.y == self.y).first() if safehouse: self.enter_safehouse(safehouse) if safehouse.dropoff: [ package.enter_safehouse(safehouse) for package in self.packages ] return True