def test_new_wrong_user(self): # First we add a Dataset with user 'test_new' user = Account.by_name('test_new') assert user.api_key == 'd0610659-627b-4403-8b7f-6e2820ebc95d' u = url(controller='api/version2', action='create') params = { 'metadata': 'https://dl.dropbox.com/u/3250791/sample-openspending-model.json', 'csv_file': 'http://mk.ucant.org/info/data/sample-openspending-dataset.csv' } apikey_header = 'apikey {0}'.format(user.api_key) response = self.app.post(u, params, {'Authorization': apikey_header}) assert "200" in response.status assert Dataset.by_name('openspending-example') is not None # After that we try to update the Dataset with user 'test_new2' user = Account.by_name('test_new2') assert user.api_key == 'c011c340-8dad-419c-8138-1c6ded86ead5' u = url(controller='api/version2', action='create') params = { 'metadata': 'https://dl.dropbox.com/u/3250791/sample-openspending-model.json', 'csv_file': 'http://mk.ucant.org/info/data/sample-openspending-dataset.csv' } apikey_header = 'apikey {0}'.format(user.api_key) response = self.app.post(u, params, {'Authorization': apikey_header}, expect_errors=True) assert '403' in response.status
def test_delete_source(self): """ Test source removal with a source that includes errors """ # Add and import source with errors (we want to remove it) # The source is added to a dataset called 'test-csv' (but # we'll just use source.dataset.name in case it changes) source = csvimport_fixture('import_errors') source.dataset.managers.append(Account.by_name('test')) importer = CSVImporter(source) importer.run() # Make sure the source is imported assert db.session.query(Source).filter_by(id=source.id).count() == 1, \ "Import of csv failed. Source not found" # Delete the source self.app.post(url(controller='source', action='delete', dataset=source.dataset.name, id=source.id), extra_environ={'REMOTE_USER': '******'}) # Check if source has been deleted assert db.session.query(Source).filter_by(id=source.id).count() == 0, \ "Deleting source unsuccessful. Source still exists."
def register(self): require.account.create() errors, values = {}, None if request.method == 'POST': try: schema = AccountRegister() values = request.params data = schema.deserialize(values) if Account.by_name(data['name']): raise colander.Invalid( AccountRegister.name, _("Login name already exists, please choose a " "different one")) if not data['password1'] == data['password2']: raise colander.Invalid(AccountRegister.password1, _("Passwords \ don't match!")) account = Account() account.name = data['name'] account.fullname = data['fullname'] account.email = data['email'] account.password = generate_password_hash(data['password1']) db.session.add(account) db.session.commit() who_api = get_api(request.environ) authenticated, headers = who_api.login({ "login": account.name, "password": data['password1'] }) response.headers.extend(headers) return redirect("/") except colander.Invalid, i: errors = i.asdict()
def test_delete_successfully_loaded_source(self): """ Test source removal with a source that has been successfully loaded. Removing a source that has been successfully loaded should not be possible. """ # Add and import source without errors. # The source is added to a dataset called 'test-csv' (but # we'll just use source.dataset.name in case it changes) source = csvimport_fixture('successful_import') source.dataset.managers.append(Account.by_name('test')) importer = CSVImporter(source) importer.run() # Make sure the source is imported assert db.session.query(Source).filter_by(id=source.id).count() == 1, \ "Import of csv failed. Source not found" # Delete the source self.app.post(url(controller='source', action='delete', dataset=source.dataset.name, id=source.id), extra_environ={'REMOTE_USER': '******'}) # Check if source has been deleted assert db.session.query(Source).filter_by(id=source.id).count() == 1, \ "Deleting source succeeded. The source is gone."
def create_view(dataset, view_config): """ Create view for a provided dataset from a view provided as dict """ # Check if it exists (if not we create it) existing = View.by_name(dataset, view_config['name']) if existing is None: # Create the view view = View() # Set saved configurations view.widget = view_config['widget'] view.state = view_config['state'] view.name = view_config['name'] view.label = view_config['label'] view.description = view_config['description'] view.public = view_config['public'] # Set the dataset as the current dataset view.dataset = dataset # Try and set the account provided but if it doesn't exist # revert to shell account view.account = Account.by_name(view_config['account']) if view.account is None: view.account = shell_account() # Commit view to database db.session.add(view) db.session.commit()
def setup(self): super(TestRunController, self).setup() self.source = csvimport_fixture('import_errors') self.source.dataset.managers.append(Account.by_name('test')) self.importer = CSVImporter(self.source) self.importer.run()
def profile(self, name=None): """ Generate a profile page for a user (from the provided name) """ # Get the account, if it's none we return a 404 account = Account.by_name(name) if account is None: response.status = 404 return None # Set the account we got as the context variable 'profile' # Note this is not the same as the context variable 'account' # which is the account for a logged in user c.profile = account # Set a context boo if email/twitter should be shown, it is only shown # to administrators and to owner (account is same as context account) show_info = (c.account and c.account.admin) or (c.account == account) # ..or if the user has chosen to make it public c.show_email = show_info or account.public_email c.show_twitter = show_info or account.public_twitter # Collect and sort the account's datasets and views c.account_datasets = sorted(account.datasets, key=lambda d: d.label) c.account_views = sorted(account.views, key=lambda d: d.label) # Render the profile return templating.render('account/profile.html')
def register(self): require.account.create() self._disable_cache() errors, values = {}, None if request.method == 'POST': try: schema = AccountRegister() values = request.params data = schema.deserialize(values) if Account.by_name(data['name']): raise colander.Invalid( AccountRegister.name, _("Login name already exists, please choose a " "different one")) if not data['password1'] == data['password2']: raise colander.Invalid(AccountRegister.password1, _("Passwords don't match!")) account = Account() account.name = data['name'] account.fullname = data['fullname'] account.email = data['email'] account.password = generate_password_hash(data['password1']) db.session.add(account) db.session.commit() who_api = get_api(request.environ) authenticated, headers = who_api.login({ "login": account.name, "password": data['password1'] }) response.headers.extend(headers) return redirect("/") except colander.Invalid, i: errors = i.asdict()
def shell_account(): account = Account.by_name(SHELL_USER) if account is not None: return account account = Account() account.name = SHELL_USER db.session.add(account) return account
def profile(self, name=None): c.config = config account = Account.by_name(name) if account is None: response.status = 404 return None c.profile = account c.is_admin = True if (c.account and c.account.admin is True) else False return render('account/profile.html')
def authenticate(self, environ, identity): if 'login' not in identity or 'password' not in identity: return None account = Account.by_name(identity['login']) if account is None: return None if account.password is None: return None if check_password_hash(account.password, identity['password']): return account.name return None
def __before__(self, action, **params): account_name = request.environ.get('REMOTE_USER', None) if account_name: c.account = Account.by_name(account_name) else: c.account = None i18n.handle_request(request, c) c._cache_disabled = False c._must_revalidate = False c.content_section = c.dataset = None c.detected_l10n_languages = i18n.get_language_pairs()
def grant_admin(username): from openspending.model import meta as db from openspending.model.account import Account a = Account.by_name(username) if a is None: print "Account `%s` not found." % username return 1 a.admin = True db.session.add(a) db.session.commit() return 0
def test_new_dataset(self): user = Account.by_name('test_new') assert user.api_key == 'd0610659-627b-4403-8b7f-6e2820ebc95d' u = url(controller='api/version2', action='create') params = { 'metadata': 'https://dl.dropbox.com/u/3250791/sample-openspending-model.json', 'csv_file': 'http://mk.ucant.org/info/data/sample-openspending-dataset.csv' } apikey_header = 'apikey {0}'.format(user.api_key) response = self.app.post(u, params, {'Authorization': apikey_header}) assert "200" in response.status assert Dataset.by_name('openspending-example') is not None
def team_update(self, dataset, format='html'): self._get_dataset(dataset) require.dataset.update(c.dataset) errors, accounts = {}, [] for account_name in request.params.getall('accounts'): account = Account.by_name(account_name) if account is None: errors[account_name] = _("User account cannot be found.") else: accounts.append(account) if c.account not in accounts: accounts.append(c.account) if not len(errors): c.dataset.managers = accounts c.dataset.updated_at = datetime.utcnow() db.session.commit() h.flash_success(_("The team has been updated.")) return self.team_edit(dataset, errors=errors, accounts=accounts)
def register(self): require.account.create() c.config = config self._disable_cache() errors, values = {}, None if request.method == 'POST': try: schema = AccountRegister() values = request.params data = schema.deserialize(values) if Account.by_name(data['name']): raise colander.Invalid( AccountRegister.name, _("Login name already exists, please choose a " "different one")) if not data['password1'] == data['password2']: raise colander.Invalid(AccountRegister.password1, _("Passwords don't match!")) account = Account() account.name = data['name'] account.fullname = data['fullname'] account.email = data['email'] account.password = generate_password_hash(data['password1']) db.session.add(account) db.session.commit() who_api = get_api(request.environ) authenticated, headers = who_api.login({ "login": account.name, "password": data['password1'] }) errors = subscribe_lists(('community', 'developer'), data) if errors: h.flash_notice( _("Subscription to the following mailing " + "lists probably failed: %s.") % ', '.join(errors)) response.headers.extend(headers) return redirect("/") except colander.Invalid, i: errors = i.asdict()
def command(self): super(GrantAdminCommand, self).command() self._check_args_length(1) from openspending.model.account import Account username = self.args[0] account = Account.by_name(username) if account is None: print "Account `%s' not found." % username return False roles = set(account["_roles"]) roles |= set([u'admin']) account["_roles"] = list(roles) Account.save(account)
def make_account(name='test', fullname='Test User', email='*****@*****.**', twitter='testuser', admin=False): from openspending.model.account import Account # First see if the account already exists and if so, return it account = Account.by_name(name) if account: return account # Account didn't exist so we create it and return it account = Account() account.name = name account.fullname = fullname account.email = email account.twitter_handle = twitter account.admin = admin db.session.add(account) db.session.commit() return account
def register(self): require.account.create() c.config = config self._disable_cache() errors, values = {}, None if request.method == 'POST': try: schema = AccountRegister() values = request.params data = schema.deserialize(values) if Account.by_name(data['name']): raise colander.Invalid( AccountRegister.name, _("Login name already exists, please choose a " "different one")) if not data['password1'] == data['password2']: raise colander.Invalid(AccountRegister.password1, _("Passwords don't match!")) account = Account() account.name = data['name'] account.fullname = data['fullname'] account.email = data['email'] account.password = generate_password_hash(data['password1']) db.session.add(account) db.session.commit() who_api = get_api(request.environ) authenticated, headers = who_api.login({ "login": account.name, "password": data['password1'] }) errors = subscribe_lists(('community', 'developer'), data) if errors: h.flash_notice(_("Subscription to the following mailing " + "lists probably failed: %s.") % ', '.join(errors)) response.headers.extend(headers) return redirect("/") except colander.Invalid, i: errors = i.asdict()
def test_user_scoreboard(self): """ Test if the user scoreboard works and is only accessible by administrators """ # Create dataset and users and make normal user owner of dataset admin_user = make_account('test_admin', admin=True) dataset = load_fixture('cra') normal_user = make_account('test_user') normal_user.datasets.append(dataset) db.session.add(normal_user) db.session.commit() # Refetch the accounts into scope after the commit admin_user = Account.by_name('test_admin') normal_user = Account.by_name('test_user') # Get the URL to user scoreboard scoreboard_url = url(controller='account', action='scoreboard') # Get the home page (could be just any page user_response = self.app.get( url(controller='home', action='index'), extra_environ={'REMOTE_USER': str(normal_user.name)}) admin_response = self.app.get( url(controller='home', action='index'), extra_environ={'REMOTE_USER': str(admin_user.name)}) # Admin user should be the only one to see a link # to the user scoreboard (not the normal user) assert scoreboard_url not in user_response.body, \ "Normal user can see scoreboard url on the home page" assert scoreboard_url in admin_response.body, \ "Admin user cannot see the scoreboard url on the home page" # Normal user should not be able to access the scoreboard url user_response = self.app.get( scoreboard_url, expect_errors=True, extra_environ={'REMOTE_USER': str(normal_user.name)}) assert '403' in user_response.status, \ "Normal user is authorized to see user scoreboard" # Administrator should see scoreboard and users should be there in # in the following order normal user - admin user (with 10 and 0 points # respectively) admin_response = self.app.get( scoreboard_url, extra_environ={'REMOTE_USER': str(admin_user.name)}) assert '200' in admin_response.status, \ "Administrator did not get a 200 status for user scoreboard" # We need to remove everything before an 'Dataset Maintainers' because # the admin user name comes first because of the navigational bar heading_index = admin_response.body.find('Dataset Maintainers') check_body = admin_response.body[heading_index:] admin_index = check_body.find(admin_user.name) user_index = check_body.find(normal_user.name) assert admin_index > user_index, \ "Admin index comes before normal user index" # Same thing as with the username we check for the scores # they are represented as <p>10</p> and <p>0</p> admin_index = check_body.find('<p>0</p>') user_index = check_body.find('<p>10</p>') assert admin_index > user_index, \ "Admin score does not come before the user score"
def register(self): """ Perform registration of a new user """ # We must allow account creation require.account.create() # We add the config as a context variable in case anything happens # (like with login we need this to allow subscriptions to mailing lists) c.config = config # Disable the cache (don't want anything getting in the way) self._disable_cache() # Initial values and errors errors, values = {}, None # If this is a POST operation somebody is trying to register if request.method == 'POST': try: # Get the account register schema (for validation) schema = AccountRegister() # Set values from the request parameters # (for validation and so we can autofill forms) values = request.params # Grab the actual data and validate it data = schema.deserialize(values) # Check if the username already exists, return an error if so if Account.by_name(data['name']): raise colander.Invalid( AccountRegister.name, _("Login name already exists, please choose a " "different one")) # Check if passwords match, return error if not if not data['password1'] == data['password2']: raise colander.Invalid(AccountRegister.password1, _("Passwords don't match!")) # Create the account account = Account() # Set username and full name account.name = data['name'] account.fullname = data['fullname'] # Set email and if email address should be public account.email = data['email'] account.public_email = data['public_email'] # Hash the password and store the hash account.password = generate_password_hash(data['password1']) # Commit the new user account to the database db.session.add(account) db.session.commit() # Perform a login for the user who_api = get_api(request.environ) authenticated, headers = who_api.login({ "login": account.name, "password": data['password1'] }) # Add the login headers response.headers.extend(headers) # Subscribe the user to the mailing lists errors = subscribe_lists(('community', 'developer'), data) # Notify if the mailing list subscriptions failed if errors: h.flash_notice(_("Subscription to the following mailing " + "lists probably failed: %s.") % ', '.join(errors)) # Registration successful - Redirect to the front page return redirect("/") except colander.Invalid, i: # Mark colander errors errors = i.asdict()
def register(self): """ Perform registration of a new user """ # We must allow account creation require.account.create() # We add the config as a context variable in case anything happens # (like with login we need this for subscriptions to mailing lists) c.config = config # Disable the cache (don't want anything getting in the way) self._disable_cache() # Initial values and errors errors, values = {}, None # If this is a POST operation somebody is trying to register if request.method == 'POST': try: # Get the account register schema (for validation) schema = AccountRegister() # Set values from the request parameters # (for validation and so we can autofill forms) values = request.params # Grab the actual data and validate it data = schema.deserialize(values) # Check if the username already exists, return an error if so if Account.by_name(data['name']): raise colander.Invalid( AccountRegister.name, _("Login name already exists, please choose a " "different one")) # Check if passwords match, return error if not if not data['password1'] == data['password2']: raise colander.Invalid(AccountRegister.password1, _("Passwords don't match!")) # Create the account account = Account() # Set username and full name account.name = data['name'] account.fullname = data['fullname'] # Set email and if email address should be public account.email = data['email'] account.public_email = data['public_email'] # Hash the password and store the hash account.password = generate_password_hash(data['password1']) # Commit the new user account to the database db.session.add(account) db.session.commit() # Perform a login for the user who_api = get_api(request.environ) authenticated, headers = who_api.login({ "login": account.name, "password": data['password1'] }) # Add the login headers response.headers.extend(headers) # Subscribe the user to the mailing lists errors = subscribe_lists(('community', 'developer'), data) # Notify if the mailing list subscriptions failed if errors: h.flash_notice( _("Subscription to the following mailing " + "lists probably failed: %s.") % ', '.join(errors)) # Registration successful - Redirect to the front page return redirect("/") except colander.Invalid as i: # Mark colander errors errors = i.asdict() # Show the templates (with possible errors and form values) return templating.render('account/login.html', form_fill=values, form_errors=errors)
def test_user_scoreboard(self): """ Test if the user scoreboard works and is only accessible by administrators """ # Create dataset and users and make normal user owner of dataset admin_user = make_account('test_admin', admin=True) dataset = load_fixture('cra') normal_user = make_account('test_user') normal_user.datasets.append(dataset) db.session.add(normal_user) db.session.commit() # Refetch the accounts into scope after the commit admin_user = Account.by_name('test_admin') normal_user = Account.by_name('test_user') # Get the URL to user scoreboard scoreboard_url = url(controller='account', action='scoreboard') # Get the home page (could be just any page user_response = self.app.get(url(controller='home', action='index'), extra_environ={'REMOTE_USER': str(normal_user.name)}) admin_response = self.app.get(url(controller='home', action='index'), extra_environ={'REMOTE_USER': str(admin_user.name)}) # Admin user should be the only one to see a link # to the user scoreboard (not the normal user) assert scoreboard_url not in user_response.body, \ "Normal user can see scoreboard url on the home page" assert scoreboard_url in admin_response.body, \ "Admin user cannot see the scoreboard url on the home page" # Normal user should not be able to access the scoreboard url user_response = self.app.get(scoreboard_url, expect_errors=True, extra_environ={'REMOTE_USER': str(normal_user.name)}) assert '403' in user_response.status, \ "Normal user is authorized to see user scoreboard" # Administrator should see scoreboard and users should be there in # in the following order normal user - admin user (with 10 and 0 points # respectively) admin_response = self.app.get(scoreboard_url, extra_environ={'REMOTE_USER': str(admin_user.name)}) assert '200' in admin_response.status, \ "Administrator did not get a 200 status for user scoreboard" # We need to remove everything before an 'Dataset Maintainers' because # the admin user name comes first because of the navigational bar heading_index = admin_response.body.find('Dataset Maintainers') check_body = admin_response.body[heading_index:] admin_index = check_body.find(admin_user.name) user_index = check_body.find(normal_user.name) assert admin_index > user_index, \ "Admin index comes before normal user index" # Same thing as with the username we check for the scores # they are represented as <p>10</p> and <p>0</p> admin_index = check_body.find('<p>0</p>') user_index = check_body.find('<p>10</p>') assert admin_index > user_index, \ "Admin score does not come before the user score"