async def get_poll_registrations(self, request): private_key, public_key, user = await self._authorize(request) election_id = request.match_info.get('electionId', '') if election_id == '': raise ApiBadRequest( 'The election ID is a required query string parameter') poll_book = await self._database.fetch_poll_book( election_id=election_id) if poll_book is None: raise ApiNotFound('No voters with the election id ' '{} was not found'.format(election_id)) election = await self._database.fetch_election_resource( election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) if election.get('admin_id') != user.get('voter_id'): raise ApiBadRequest( 'User is not the owner of the election with the id ' '{} .'.format(election_id)) return json_response(poll_book)
async def count_poll_registrations(self, request): private_key, public_key, user = await self._authorize(request) election_id = request.match_info.get('electionId', '') if election_id == '': raise ApiBadRequest( 'The election ID is a required query string parameter') count_poll_book = await self._database.count_poll_book( election_id=election_id) election = await self._database.fetch_election_resource( election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) if election.get('results_permission') != 'PUBLIC': poll_registration = await self._database.fetch_poll_book_registration( voter_id=user.get('voter_id'), election_id=election_id) if poll_registration is None and election.get( 'admin_id') != user.get('voter_id'): raise ApiBadRequest( 'Voter is not registered in the poll book of the election with the id ' '{} .'.format(election_id)) return json_response(count_poll_book)
async def update_poll_book_status(self, request): private_key, public_key, user = await self._authorize(request) voter_id = request.match_info.get('voterId', '') if voter_id == '': raise ApiBadRequest( 'The voter ID is a required query string parameter') election_id = request.match_info.get('electionId', '') if election_id == '': raise ApiBadRequest( 'The election ID is a required query string parameter') voter_poll_book = await self._database.fetch_poll_book_registration( election_id=election_id, voter_id=voter_id) if voter_poll_book is None: raise ApiNotFound('Voter with the voter id ' '{} was not found'.format(voter_id)) election = await self._database.fetch_election_resource( election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) current_time = get_time() if election.get('start_timestamp') < current_time: raise ApiBadRequest('Election with the election id ' '{} already start.'.format(election_id)) if election.get('admin_id') != user.get('voter_id'): raise ApiBadRequest( 'User is not the owner of the election with the id ' '{} .'.format(election_id)) if voter_poll_book.get('status') is True: status = 0 else: status = 1 await self._messenger.send_update_voter_poll_book_status_transaction( private_key=private_key, voter_id=voter_id, name=voter_poll_book.get('name'), election_id=election_id, status=status, timestamp=get_time()) return json_response( {'data': 'Update Poll Registration Status transaction submitted'})
async def get_election(self, request): private_key, public_key, user = await self._authorize(request) election_id = request.match_info.get('electionId', '') if election_id == '': raise ApiBadRequest( 'The election ID is a required query string parameter') if 'asAdmin' in request.rel_url.query: election = await self._database.fetch_election_with_can_vote_resource_admin( voter_id=user.get('voter_id'), election_id=election_id) else: election = await self._database.fetch_election_with_can_vote_resource( voter_id=user.get('voter_id'), election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) if election.get('results_permission') != 'PUBLIC': if election.get('can_vote') is False and election.get( 'admin_id') != user.get('voter_id'): raise ApiForbidden( 'Voter is not registered in the poll book of the election with the id ' '{}.'.format(election_id)) return json_response(election)
async def _send_and_wait_for_commit(self, batch): # Send transaction to validator submit_request = client_batch_submit_pb2.ClientBatchSubmitRequest( batches=[batch]) await self._connection.send( validator_pb2.Message.CLIENT_BATCH_SUBMIT_REQUEST, submit_request.SerializeToString()) # Send status request to validator batch_id = batch.header_signature status_request = client_batch_submit_pb2.ClientBatchStatusRequest( batch_ids=[batch_id], wait=True) validator_response = await self._connection.send( validator_pb2.Message.CLIENT_BATCH_STATUS_REQUEST, status_request.SerializeToString()) # Parse response status_response = client_batch_submit_pb2.ClientBatchStatusResponse() status_response.ParseFromString(validator_response.content) status = status_response.batch_statuses[0].status if status == client_batch_submit_pb2.ClientBatchStatus.INVALID: error = status_response.batch_statuses[0].invalid_transactions[0] raise ApiBadRequest(error.message) elif status == client_batch_submit_pb2.ClientBatchStatus.PENDING: raise ApiInternalError('Transaction submitted but timed out') elif status == client_batch_submit_pb2.ClientBatchStatus.UNKNOWN: raise ApiInternalError('Something went wrong. Try again later')
async def create_agent(self, request): body = await decode_request(request) required_fields = ['email', 'name', 'password'] validate_fields(required_fields, body) public_key, private_key = self._messenger.get_new_key_pair() email = body.get('email') await self._messenger.send_create_agent_transaction( private_key=private_key, email=email, name=body.get('name'), timestamp=get_time()) encrypted_private_key = encrypt_private_key( request.app['aes_key'], public_key, private_key) hashed_password = hash_password(body.get('password')) try: await self._database.create_auth_entry(email, public_key, encrypted_private_key, hashed_password) except IntegrityError: raise ApiBadRequest('The Email address has already been used!') token = generate_auth_token( request.app['secret_key'], public_key) return json_response({'authorization': token})
async def get_vote_election(self, request): private_key, public_key, user = await self._authorize(request) voter_id = request.match_info.get('voterId', '') if voter_id == '': raise ApiBadRequest( 'The voter ID is a required query string parameter') election_id = request.match_info.get('electionId', '') if election_id == '': raise ApiBadRequest( 'The election ID is a required query string parameter') if user.get('voter_id') != voter_id: raise ApiForbidden('Admin must be the authenticated one') vote = await self._database.fetch_my_vote__election_resource( voter_id=voter_id, election_id=election_id) return json_response(vote)
async def list_elections_current(self, request): private_key, public_key, user = await self._authorize(request) voterId = request.match_info.get('voterId', '') if voterId == '': raise ApiBadRequest( 'The voter ID is a required query string parameter') if user.get('voter_id') != voterId: raise ApiForbidden('Admin must be the authenticated one') current_elections_list = await self._database.fetch_current_elections_resources( voterId, get_time()) return json_response(current_elections_list)
async def get_voting_option(self, request): private_key, public_key, user = await self._authorize(request) voting_option_id = request.match_info.get('votingOptionId', '') if voting_option_id == '': raise ApiBadRequest( 'The voting option ID is a required query string parameter') voting_option = await self._database.fetch_voting_option_resource( voting_option_id=voting_option_id) if voting_option is None: raise ApiNotFound('No voting options with the id ' '{} was not found'.format(voting_option_id)) return json_response(voting_option)
async def update_voter_type(self, request): private_key, public_key, user = await self._authorize(request) body = await decode_request(request) required_fields = ['type'] validate_fields(required_fields, body) voter_id = request.match_info.get('voterId', '') if voter_id == '': raise ApiBadRequest( 'The voter ID is a required query string parameter') if user.get('type') != 'SUPERADMIN': raise ApiForbidden('Forbidden') voter = await self._database.fetch_voter_resource(voter_id=voter_id) if voter is None: raise ApiNotFound('No voter found') if body.get('type') == 'ADMIN' and (voter.get('type') == 'ADMIN' or voter.get('type') == 'SUPERADMIN'): raise ApiConflict( 'Voter {} is already an admin or superadmin'.format(voter_id)) elif body.get('type') == 'VOTER' and voter.get('type') == 'VOTER': raise ApiConflict('Voter {} is already a voter. '.format(voter_id)) auth_info = await self._database.fetch_auth_resource( public_key=voter.get('public_key')) voter_private_key = decrypt_private_key( request.app['aes_key'], voter.get('public_key'), auth_info.get('encrypted_private_key')) await self._messenger.send_update_voter_transaction( private_key=voter_private_key, voter_id=voter_id, public_key=voter.get('public_key'), name=voter.get('name'), created_at=get_time(), type=body.get('type')) return json_response({ 'voter': { 'voter_id': voter_id, 'name': voter.get('name'), 'type': 'ADMIN' } })
async def list_admin_elections(self, request): private_key, public_key, user = await self._authorize(request) voter_id = request.match_info.get('voterId', '') if voter_id == '': raise ApiBadRequest( 'The voter ID is a required query string parameter') if user.get('voter_id') != voter_id: raise ApiForbidden('Admin must be the authenticated one') if user.get('type') != 'ADMIN' and user.get('type') != 'SUPERADMIN': raise ApiForbidden('Voter must be an admin or superadmin') admin_elections_list = await self._database.fetch_admin_elections_resources( user.get('voter_id')) return json_response(admin_elections_list)
async def get_vote(self, request): private_key, public_key, user = await self._authorize(request) vote_id = request.match_info.get('voteId', '') if vote_id == '': raise ApiBadRequest( 'The vote ID is a required query string parameter') vote = await self._database.fetch_vote_resource(vote_id=vote_id) if vote is None: raise ApiNotFound('Vote with the vote id ' '{} was not found'.format(vote_id)) election_id = vote.get('election_id') election = await self._database.fetch_election_resource( election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) return json_response(vote)
def validate_fields(required_fields, body): for field in required_fields: if body.get(field) is None: raise ApiBadRequest("'{}' parameter is required".format(field))
async def decode_request(request): try: return await request.json() except JSONDecodeError: raise ApiBadRequest('Improper JSON format')
async def update_vote(self, request): private_key, public_key, user = await self._authorize(request) body = await decode_request(request) required_fields = ['voting_option_id'] validate_fields(required_fields, body) vote_id = request.match_info.get('voteId', '') if vote_id == '': raise ApiBadRequest( 'The vote ID is a required query string parameter') vote = await self._database.fetch_vote_resource(vote_id=vote_id) election_id = vote.get('election_id') if vote is None: raise ApiNotFound('Vote with the vote id ' '{} was not found'.format(vote_id)) if vote.get('voting_option_id') == body.get('voting_option_id'): raise ApiBadRequest('Vote must be different.') election = await self._database.fetch_election_resource( election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) if election.get('can_change_vote') == 0: raise ApiInternalError( 'Election with the election id ' '{} was not found don\'t permit to change vote'.format( election_id)) if election.get('can_change_vote') == 0: raise ApiInternalError( 'Election with the election id ' '{} was not found don\'t permit to change vote'.format( election_id)) current_time = get_time() if election.get('end_timestamp') < current_time or election.get( 'start_timestamp') > current_time: raise ApiBadRequest('Not in election time.'.format()) if election.get('admin_id') == user.get('voter_id'): raise ApiBadRequest( 'User is not the owner of the election with the id ' '{} .'.format(election_id)) new_voting_option_id = body.get('voting_option_id') old_voting_option_id = vote.get('voting_option_id') old_num_vote = await self._database.fetch_voting_option_num_vote_resource( voting_option_id=old_voting_option_id) new_num_vote = await self._database.fetch_voting_option_num_vote_resource( voting_option_id=new_voting_option_id) num_votes_remove = old_num_vote.get('num_votes') - 1 num_votes_update = new_num_vote.get('num_votes') + 1 await self._messenger.send_update_vote_transaction( private_key=private_key, vote_id=vote_id, timestamp=get_time(), voting_option_id=new_voting_option_id) # remove -1 to old voting option await self._database.update_voting_option_num_vote_resource( voting_option_id=old_voting_option_id, num_votes=num_votes_remove) # add +1 to new voting option await self._database.update_voting_option_num_vote_resource( voting_option_id=new_voting_option_id, num_votes=num_votes_update) return json_response({'data': 'Update Vote transaction submitted'})
async def update_election(self, request): private_key, public_key, user = await self._authorize(request) body = await decode_request(request) election_id = request.match_info.get('electionId', '') if election_id == '': raise ApiBadRequest( 'The election ID is a required query string parameter') election = await self._database.fetch_election_resource( election_id=election_id) if election is None: raise ApiNotFound('Election with the election id ' '{} was not found'.format(election_id)) current_time = get_time() if election.get('start_timestamp') < current_time: raise ApiBadRequest('Election with the election id ' '{} already start.'.format(election_id)) if election.get('admin_id') != user.get('voter_id'): raise ApiBadRequest( 'User is not the owner of the election with the id ' '{} .'.format(election_id)) await self._messenger.send_update_election_transaction( private_key=private_key, election_id=election_id, name=body.get('name') if body.get('name') is not None else election.get('name'), description=body.get('description') if body.get('description') is not None else election.get('description'), start_timestamp=body.get('start_timestamp') if body.get('start_timestamp') is not None else election.get('start_timestamp'), end_timestamp=body.get('end_timestamp') if body.get('end_timestamp') is not None else election.get('end_timestamp'), results_permission=body.get('results_permission') if body.get('results_permission') is not None else election.get('results_permission'), can_change_vote=body.get('can_change_vote') if body.get('can_change_vote') is not None else election.get('can_change_vote'), can_show_realtime=body.get('can_show_realtime') if body.get('can_show_realtime') is not None else election.get('can_show_realtime'), admin_id=user.get('voter_id'), status=body.get('status') if body.get('status') is not None else election.get('status'), timestamp=get_time()) if body.get('voting_options') is not None: for voting_option in body.get('voting_options'): voting_option_id = uuid.uuid1().hex await self._messenger.send_create_voting_option_transaction( private_key=private_key, voting_option_id=voting_option_id, name=voting_option.get('name'), description=voting_option.get('description'), election_id=election_id, status=1, timestamp=get_time()) await self._database.insert_voting_option_num_vote_resource( voting_option_id=voting_option_id, name=voting_option.get('name'), election_id=election_id) if body.get('poll_book') is not None: for poll_book in body.get('poll_book'): await self._messenger.send_create_poll_registration_transaction( private_key=private_key, voter_id=poll_book.get('id'), name=poll_book.get('name'), election_id=election_id, status=1, timestamp=get_time()) return json_response({'data': 'Update Election transaction submitted'})
async def create_vote(self, request): body = await decode_request(request) required_fields = [] validate_fields(required_fields, body) private_key, public_key, user = await self._authorize(request) voting_option_id = request.match_info.get('votingOptionId', '') if voting_option_id == '': raise ApiBadRequest( 'The voting option ID is a required query string parameter') voter = await self._database.fetch_voter_resource(public_key=public_key ) if voter is None: raise ApiNotFound('Voter with the public_key ' '{} was not found'.format(public_key)) voting_option = await self._database.fetch_voting_option_resource( voting_option_id=voting_option_id) vo_count_vote = await self._database.fetch_voting_option_num_vote_resource( voting_option_id=voting_option_id) if voting_option is None: raise ApiNotFound('Voting Option with the voting option id ' '{} was not found'.format(voting_option_id)) election_id = voting_option.get('election_id') election = await self._database.fetch_election_resource( election_id=election_id) if election.get('status') == 0: raise ApiBadRequest('Election with the election id ' '{} is cancelled'.format(election_id)) current_time = get_time() if election.get('end_timestamp') < current_time or election.get( 'start_timestamp') > current_time: raise ApiBadRequest('Not in election time.'.format()) poll_registration = await self._database.fetch_poll_book_registration( voter_id=user.get('voter_id'), election_id=election_id) if poll_registration is None: raise ApiBadRequest( 'Voter is not registered in the poll book of the election with the id ' '{} .'.format(election_id)) num_votes_update = vo_count_vote.get('num_votes') + 1 await self._messenger.send_create_vote_transaction( private_key=private_key, vote_id=uuid.uuid1().hex, timestamp=get_time(), voter_id=voter.get('voter_id'), election_id=voting_option.get('election_id'), voting_option_id=voting_option_id) await self._database.update_voting_option_num_vote_resource( voting_option_id=voting_option_id, num_votes=num_votes_update) return json_response({'data': 'Create vote transaction submitted'})