def delete_star(id): """ Delete a star """ # TODO: Should only accept POST instead of GET # TODO: Check permissions! # Load instance and creator persona s = Star.query.get(id) if s is None: abort(404) # Create deletion request data = dict({ "object_type": "Star", "object_id": s.id, "change": "delete", "change_time": datetime.datetime.now().isoformat() }) message = Message(message_type="change_notification", data=json.dumps(data)) if s.creator.sign_private is not None: message.sign(s.creator) # Delete instance from db db.session.delete(s) db.session.commit() # Distribute deletion request star_deleted.send(delete_star, message=message) app.logger.info("Deleted star {}".format(id)) return redirect(url_for('debug'))
def find_people(): """Search for and follow people""" form = FindPeopleForm(request.form) found = None error = None if request.method == 'POST' and form.validate(): # Compile message email = request.form['email'] email_hash = sha256(email).hexdigest() app.logger.info("Searching for {}".format(email)) data = {"email_hash": email_hash} message = Message(message_type='find_people', data=data) # Send message headers = {"Content-Type": "application/json"} url = "http://{host}/find-people".format( host=app.config['LOGIN_SERVER']) r = requests.post(url, message.json(), headers=headers) # Read response try: resp = r.json() except ValueError: app.logger.info("Error retrieving results: {}".format(r.data)) flash("Server error: Please try again.") # TODO: Use message_errors() instead if resp and resp['data'] and 'found' in resp['data']: found = resp['data']['found'] for p in found: if Persona.query.get(p['persona_id']) is None: app.logger.info("Storing new Persona {}".format( p['persona_id'])) p_new = Persona(id=p['persona_id'], username=p['username'], email=email, crypt_public=p['crypt_public'], sign_public=p['sign_public']) db.session.add(p_new) db.session.commit() else: error = "No record for {}. Check the spelling!".format(email) return render_template('find_people.html', form=form, found=found, error=error)
def register_persona(self, persona): """ Register a persona on the login server """ self.logger.info( "Registering persona {} with login server".format(persona)) # Create request data = { 'persona_id': persona.id, 'username': persona.username, 'email_hash': persona.get_email_hash(), 'sign_public': persona.sign_public, 'crypt_public': persona.crypt_public, 'reply_to': app.config['SYNAPSE_PORT'] } message = Message('create_persona', data) url = "http://{host}/p/{persona}/create".format( host=app.config['LOGIN_SERVER'], persona=persona.id) response, errors = self.server_request(url, message=message) if errors: self.logger.error("Error creating account on server:\n{}".format( "\n".join(errors))) # Evaluate response if 'session_id' in response['data']: self.logger.info("Registered {} with server.".format(persona)) self.sessions[persona.id] = { 'session_id': response['data']['session_id'], 'timeout': response['data']['timeout'], } self.update_peer_list(persona) self.queue_keepalive(persona)
def request_inventory_operator(self, address): """Request an starmap from the soma at address and reschedule the process in 180 seconds""" self.logger.info("Requesting starmap from {}".format( self.source_format(address))) m = Message("starmap_request", data=None) self.message_pool.spawn(self.send_message, address, m) self.message_pool.spawn_later(180, self.send_message, address, m)
class MessageTest(unittest.TestCase): def setUp(self): self.uuid = uuid4().hex self.change_time = datetime.datetime.now().isoformat() self.message_type = "change_notification" data = dict({ "object_type": "Persona", "object_id": self.uuid, "change": "insert", "change_time": self.change_time }) self.message = Message(message_type=self.message_type, data=data) def test_json(self): repr = self.message.json() #jsonification added timestamp self.assertTrue("timestamp" in repr) self.assertTrue(self.change_time in repr) #json encoding complete self.assertTrue("message_type" in repr) self.assertTrue(self.message_type in repr) self.assertTrue("data" in repr) self.assertTrue("Persona" in repr) self.assertTrue(self.uuid in repr) self.assertTrue("reply_to" in repr) # jsonification sensitive to new attributes self.assertFalse("signature" in repr) self.message.send_attributes.append("signature") self.message.signature="sincerly yours" repr = self.message.json() self.assertTrue("signature" in repr) def test_read(self): pass def test_sign(self): pass
def on_star_created(self, sender, message): """Add new stars to starmap and send notification message""" star = message data = dict({ "object_type": "Star", "object_id": star.id, "change": "insert", "change_time": star.modified.isoformat() }) message = Message(message_type="change_notification", data=data) message.sign(star.creator) self.logger.info("[{sender}] Distributing {msg}".format(sender=sender, msg=message)) self.distribute_message(message) orb = Orb("Star", star.id, star.modified, star.creator.id) self.starmap.add(orb) # Add to starmap
def handle_starmap_request(self, message, address): """ Send starmap of published objects to the given address """ data = { 'soma_id': app.config['SOMA_ID'], 'starmap': self.create_starmap() } m = Message("starmap", data) self.logger.info("Sending requested starmap of {} orbs to {}".format( len(data), self.source_format(address))) self.message_pool.spawn(self.send_message, address, m)
def handle_message(self, data, address): """Parse received Message objects and pass them on to the correct handler""" # Try parsing the message try: message = Message.read(data) except KeyError, e: self.logger.error( "[{source}] Message malformed (missing {key})".format( source=self.source_format(address), key=e)) return
def setUp(self): self.uuid = uuid4().hex self.change_time = datetime.datetime.now().isoformat() self.message_type = "change_notification" data = dict({ "object_type": "Persona", "object_id": self.uuid, "change": "insert", "change_time": self.change_time }) self.message = Message(message_type=self.message_type, data=data)
def request_object(self, object_type, object_id, address): """ Request an object from a peer """ self.logger.info( "Requesting <{object_type} {object_id}> from {source}".format( object_type=object_type, object_id=object_id[:6], source=self.source_format(address))) # Construct request data = {"object_type": object_type, "object_id": object_id} message = Message("object_request", data) # Send request self.send_message(address, message)
def login(self, persona): """ Create session at login server """ # Check current state url = "http://{host}/p/{persona_id}/".format( host=app.config['LOGIN_SERVER'], persona_id=persona.id) resp, errors = self.server_request(url) if errors: self.logger.error("Login failed with errors:\n{}".format( "\n".join(errors))) if not resp: return # Check error list for code 3 (persona not found) and register new persona if found if 3 in [t[0] for t in resp['data']['errors']]: self.register_persona(persona) return # Persona is already logged in elif 'session_id' in resp['data']: self.logger.info("Persona {} already logged in.".format(persona)) self.sessions[persona.id] = { 'session_id': resp['data']['session_id'], 'timeout': resp['data']['timeout'] } self.queue_keepalive(persona) return # Do login if 'auth' in resp['data']: data = { 'auth_signed': persona.sign(resp['data']['auth']), 'reply_to': app.config['SYNAPSE_PORT'] } r, errors = self.server_request(url, Message('session', data)) if errors: self.logger.error("Login failed:\n{}".format( "\n".join(errors))) else: self.sessions[persona.id] = { 'session_id': r['data']['session_id'], 'timeout': r['data']['timeout'], } self.logger.info("Persona {} logged in until {}".format( persona, dateutil_parse(r['data']['timeout']))) self.queue_keepalive(persona) self.update_peer_list(persona)
def create_persona(): """ Render page for creating new persona """ from uuid import uuid4 form = Create_persona_form() if form.validate_on_submit(): # This is a unique ID which identifies the persona across all contexts uuid = uuid4().hex # Save persona to DB p = Persona(uuid, request.form['name'], request.form['email']) # Create keypairs p.generate_keys(cache.get('password')) # TODO: Error message when user already exists db.session.add(p) db.session.commit() # Distribute "birth" certificate data = dict({ "object_type": "Persona", "object_id": uuid, "change": "insert", "change_time": p.modified.isoformat() }) message = Message(message_type="change_notification", data=data) persona_created.send(create_persona, message=message) flash("New persona {} created!".format(p.username)) return redirect(url_for('persona', id=uuid)) return render_template('create_persona.html', form=form, next=url_for('create_persona'))
def handle_object_request(self, message, address): """ Serve an object to address in response to a request """ object_id = message.data["object_id"] object_type = message.data["object_type"] # Load object obj = None if object_type == "Star": obj = Star.query.get(object_id) elif object_type == "Persona": obj = Persona.query.get(object_id) if obj is None: # TODO: Serve error message self.logger.error( "Requested object <{type} {id}> not found".format( type=object_type, id=object_id[:6])) self.socket.sendto(str(), address) return # Construct response data = { "object": obj.export(exclude=["sign_private, crypt_private"]), "object_type": object_type } message = Message("object", data) # Sign message if object_type == "Star" and obj.creator.sign_private is not None: message.sign(obj.creator) elif object_type == "Persona" and obj.sign_private is not None: message.sign(obj) # Send response self.send_message(address, message) self.logger.info("Sent {object_type} {object_id} to {address}".format( object_type=object_type, object_id=object_id, address=self.source_format(address)))
def request_inventory(self, address): """Request an starmap from the soma at address""" self.logger.info("Requesting starmap from {}".format( self.source_format(address))) m = Message("starmap_request", data=None) self.message_pool.spawn(self.send_message, address, m)
def create_star(): from uuid import uuid4 """ Create a new star """ # Load author drop down contents controlled_personas = Persona.query.filter( Persona.sign_private != None).all() creator_choices = [(p.id, p.username) for p in controlled_personas] active_persona = Persona.query.get(session['active_persona']) form = Create_star_form(default_creator=session['active_persona']) form.creator.choices = creator_choices if form.validate_on_submit(): uuid = uuid4().hex new_star = Star(uuid, request.form['text'], request.form['creator']) db.session.add(new_star) db.session.commit() flash('New star created!') app.logger.info('Created new star {}'.format(new_star.id)) if 'picture' in request.files and request.files[ 'picture'].filename != "": # compute hash picture_hash = sha256( request.files['picture'].stream.read()).hexdigest() request.files['picture'].stream.seek(0) # create or get planet planet = Planet.query.filter_by(id=picture_hash[:32]).first() if not planet: app.logger.info("Storing submitted file") filename = attachments.save(request.files['picture'], folder=picture_hash[:2], name=picture_hash[2:] + ".") planet = PicturePlanet(id=picture_hash[:32], filename=os.path.join( attachments.name, filename)) db.session.add(planet) # attach to star new_star.planets.append(planet) # commit db.session.add(new_star) db.session.commit() app.logger.info("Attached planet {} to new star".format(planet)) if 'link' in request.form and request.form['link'] != "": link_hash = sha256(request.form['link']).hexdigest()[:32] planet = Planet.query.filter_by(id=link_hash).first() if not planet: app.logger.info("Storing new Link") planet = LinkPlanet(id=link_hash, url=request.form['link']) db.session.add(planet) new_star.planets.append(planet) db.session.add(new_star) db.session.commit() app.logger.info("Attached planet {} to new star".format(planet)) # Create certificate data = dict({ "object_type": "Star", "object_id": uuid, "change": "insert", "change_time": new_star.modified.isoformat() }) message = Message(message_type="change_notification", data=data) message.sign(new_star.creator) star_created.send(create_star, message=new_star) return redirect(url_for('star', id=uuid)) return render_template('create_star.html', form=form, active_persona=active_persona)