def decorate_instance(self, instance, assocs, user_id, ctx, kwargs): # if one of the objects has a non-list relation to this class, add it # Slightly dangerous... nullables = [] found = False for inst in assocs: relations = inst.__class__.__mapper__.relationships for reln in relations: if uses_list(reln): continue if getattr(inst, reln.key, None) is not None: # This was already set, assume it was set correctly found = True continue # Do not decorate nullable columns if all((col.nullable and col.info.get("pseudo_nullable", True) for col in reln.local_columns)): nullables.append(reln) continue if issubclass(self._instance.__class__, reln.mapper.class_): # print "Setting3 ", inst, reln.key, self._instance setattr(inst, reln.key, self._instance) found = True break if nullables and not found: reln = nullables[0] getLogger().debug("Setting nullable column" + reln) setattr(inst, reln.key, self._instance) super(InstanceContext, self).decorate_instance( instance, assocs, user_id, ctx, kwargs)
def decorate_query( self, query, owner_alias, coll_alias, parent_instance, ctx): # This will decorate a query with a join on the relation. inv = self.back_relation if inv: query = query.join(owner_alias, getattr(coll_alias, inv.key)) else: # hope for the best try: query = query.join(owner_alias) except InvalidRequestError: getLogger().error("Could not join %s to %s" % (owner_alias, query)) # This is very likely to fail downstream return query found_key = False if inv and not uses_list(inv): # Try to constrain on coll_alias's key vs owner_alias. # Difficult cases happen when tombstone is part of the # reln's columns for column in inv.local_columns: for fk in column.foreign_keys: if fk.column.table == parent_instance.__class__.__table__: query = query.filter( getattr(coll_alias, column.name) == parent_instance.id) found_key = True if not found_key: query = query.filter(owner_alias.id == parent_instance.id) return query
def decorate_instance(self, instance, assocs, user_id, ctx, kwargs): # if one of the objects has a non-list relation to this class, add it # Slightly dangerous... nullables = [] found = False for inst in assocs: relations = inst.__class__.__mapper__.relationships for reln in relations: if uses_list(reln): continue if getattr(inst, reln.key, None) is not None: # This was already set, assume it was set correctly found = True continue # Do not decorate nullable columns if all((col.nullable and col.info.get("pseudo_nullable", True) for col in reln.local_columns)): nullables.append(reln) continue if issubclass(self._instance.__class__, reln.mapper.class_): # print "Setting3 ", inst, reln.key, self._instance setattr(inst, reln.key, self._instance) found = True break if nullables and not found: reln = nullables[0] getLogger().debug("Setting nullable column" + reln) setattr(inst, reln.key, self._instance) super(InstanceContext, self).decorate_instance(instance, assocs, user_id, ctx, kwargs)
def resolve(self, next, source, gargs, context, info, *args, **kwargs): if source is None: modified_variables = {} for key, value in info.variable_values.items(): if 'password' in key or 'Password' in key: modified_variables[key] = 'xxxxxxxxxxxxx' else: modified_variables[key] = value getLogger().debug( 'graphql', op=info.operation.operation, opname=info.operation.name.value, vars=modified_variables) return next(source, gargs, context, info, *args, **kwargs)
def get_graphql_params(request, data): query, variables, operation_name = original_get_graphql_params(request, data) modified_variables = {} if variables is not None: for key, value in variables.items(): if 'password' in key or 'Password' in key: modified_variables[key] = 'xxxxxxxxxxxxx' else: modified_variables[key] = value operation = query.split()[0] getLogger().debug( 'graphql', op=operation, opname=operation_name, vars=modified_variables) return query, variables, operation_name
def intermediate_commit(contents): logger = logging.getLogger() count = 0 changes = get_changes() for content in contents: count += 1 if count % 100 == 0: logger.info('{0} items read'.format(count)) if count % 500 == 0: #transaction.commit() changes.tpc_finish(None) logger.info('{0} items indexed'.format(count)) yield content #we can't do a real commit, we got DetachedInstanceError #transaction.commit() changes.tpc_finish(None) logger.info('{0} items indexed'.format(count))
def resolve(self, next, source, gargs, context, info, *args, **kwargs): log = getLogger() if source is None: modified_variables = {} for key, value in info.variable_values.items(): if 'password' in key or 'Password' in key: modified_variables[key] = 'xxxxxxxxxxxxx' else: modified_variables[key] = value log.debug('graphql', op=info.operation.operation, opname=info.operation.name.value, vars=modified_variables) try: return next(source, gargs, context, info, *args, **kwargs) except Exception as e: log.error("graphql_error", exc_info=True) raise e
UpdateProposal, DeleteProposal) from assembl.graphql.utils import get_fields, get_root_thematic_for_phase from assembl.graphql.preferences import UpdateHarvestingTranslationPreference from assembl.lib.locale import strip_country from assembl.lib.sqla_types import EmailString from assembl.models.action import SentimentOfPost from assembl.models.post import countable_publication_states from assembl.nlp.translation_service import DummyGoogleTranslationService from assembl.graphql.permissions_helpers import require_instance_permission from assembl.auth import CrudPermissions from assembl.utils import get_ideas, get_posts_for_phases convert_sqlalchemy_type.register(EmailString)(convert_column_to_string) models.Base.query = models.Base.default_db.query_property() log = logging.getLogger() # For security, always use only_fields in the Meta class to be sure we don't # expose every fields and relations. You need at least only_fields = ('id', ) # to take effect. # Auto exposing everything will automatically convert relations # like AgentProfile.posts_created and create dynamically the # object types Post, PostConnection which will conflict with those added # manually. class Query(graphene.ObjectType): node = Node.Field(description=docs.Schema.node) root_idea = graphene.Field( IdeaUnion, discussion_phase_id=graphene.Int(
def reset_password(request): identifier = request.json_body.get('identifier') user_id = request.json_body.get('user_id') slug = request.json_body.get('discussion_slug') logger = logging.getLogger() discussion = None if slug: discussion = Discussion.default_db.query( Discussion).filter_by(slug=slug).first() email = None user = None localizer = request.localizer if user_id: user = AgentProfile.get(int(user_id)) if not user: if not discussion.preferences['generic_errors']: raise JSONError( localizer.translate(_("The user does not exist")), code=HTTPNotFound.code) else: raise JSONError( localizer.translate(generic_error_message), code=HTTPNotFound.code) logger.error("[Password reset] The user with the identifier %s does not exist" % (identifier)) if identifier: for account in user.accounts: if identifier == account.email: email = identifier break elif identifier: user, account = from_identifier(identifier) if not user: if not discussion.preferences['generic_errors']: raise JSONError( localizer.translate(_("This email does not exist")), code=HTTPNotFound.code) else: raise JSONError( localizer.translate(_(generic_error_message)), code=HTTPNotFound.code) logger.error("This email does not exist.") if account: email = account.email else: error = localizer.translate(_("Please give an identifier")) raise JSONError(error) if not email: email = user.get_preferred_email() if not email: if not discussion.preferences['generic_errors']: error = localizer.translate(_("This user has no email")) else: error = localizer.translate(_(generic_error_message)) logger.error("This user has no email.") raise JSONError(error, code=HTTPPreconditionFailed.code) if not isinstance(user, User): if not discussion.preferences['generic_errors']: error = localizer.translate(_("This is not a user")) else: error = localizer.translate(_(generic_error_message)) logger.error("This is not a user.") raise JSONError(error, code=HTTPPreconditionFailed.code) send_change_password_email(request, user, email, discussion=discussion) return HTTPOk()
def reset_password(request): identifier = request.json_body.get('identifier') user_id = request.json_body.get('user_id') slug = request.json_body.get('discussion_slug') logger = logging.getLogger() discussion = None if slug: discussion = Discussion.default_db.query(Discussion).filter_by( slug=slug).first() email = None user = None localizer = request.localizer if user_id: user = AgentProfile.get(int(user_id)) if not user: if not discussion.preferences['generic_errors']: raise JSONError(localizer.translate( _("The user does not exist")), code=HTTPNotFound.code) else: raise JSONError(localizer.translate(generic_error_message), code=HTTPNotFound.code) logger.error( "[Password reset] The user with the identifier %s does not exist" % (identifier)) if identifier: for account in user.accounts: if identifier == account.email: email = identifier break elif identifier: user, account = from_identifier(identifier) if not user: if not discussion.preferences['generic_errors']: raise JSONError(localizer.translate( _("This email does not exist")), code=HTTPNotFound.code) else: raise JSONError(localizer.translate(_(generic_error_message)), code=HTTPNotFound.code) logger.error("This email does not exist.") if account: email = account.email else: error = localizer.translate(_("Please give an identifier")) raise JSONError(error) if not email: email = user.get_preferred_email() if not email: if not discussion.preferences['generic_errors']: error = localizer.translate(_("This user has no email")) else: error = localizer.translate(_(generic_error_message)) logger.error("This user has no email.") raise JSONError(error, code=HTTPPreconditionFailed.code) if not isinstance(user, User): if not discussion.preferences['generic_errors']: error = localizer.translate(_("This is not a user")) else: error = localizer.translate(_(generic_error_message)) logger.error("This is not a user.") raise JSONError(error, code=HTTPPreconditionFailed.code) send_change_password_email(request, user, email, discussion=discussion) return HTTPOk()
import transaction from sqlalchemy.sql.functions import count from webtest import TestRequest from webob.request import environ_from_url from pyramid.threadlocal import manager from contextlib import contextmanager from assembl.lib.sqla import ( configure_engine, get_session_maker, get_metadata, is_zopish, mark_changed) from assembl.lib import logging from assembl.auth import R_PARTICIPANT log = logging.getLogger() class FakeLocalizer(object): def translate(self, message): return message class PyramidWebTestRequest(TestRequest): """ A mock Pyramid web request this pushes itself onto the threadlocal stack that also contains the Assembl user_id according to authentication model This is very useful because throughout the model logic, a request is often required to determine the current_user, but outside of a Pyramid view. The way a request is injected is via the current_thread from threadlocal.
def reset_password(request): identifier = request.json_body.get('identifier') user_id = request.json_body.get('user_id') slug = request.json_body.get('discussion_slug') logger = logging.getLogger() discussion = None if slug: discussion = Discussion.default_db.query(Discussion).filter_by( slug=slug).first() email = None user = None localizer = request.localizer if user_id: user = AgentProfile.get(int(user_id)) if not user: logger.error( "[Password reset] The user with the identifier %s does not exist" % (identifier)) return HTTPOk(location=maybe_contextual_route( request, 'password_change_sent', profile_id=user_id, _query=dict(email=identifier if '@' in identifier else ''))) if identifier: for account in user.accounts: if identifier == account.email: email = identifier break elif identifier: user, account = from_identifier(identifier) if not user: logger.error( "[Password reset] The user with the identifier %s does not exist" % (identifier)) return HTTPOk(location=maybe_contextual_route( request, 'password_change_sent', profile_id=user_id, _query=dict(email=identifier if '@' in identifier else ''))) if account: email = account.email else: error = localizer.translate(_("Please give an identifier")) raise JSONError(error) if not email: if user: email = user.get_preferred_email() else: email = None if not email: logger.error( "[Password reset] The user with the identifier %s does not exist" % (user.id)) return HTTPOk(location=maybe_contextual_route( request, 'password_change_sent', profile_id=user_id, _query=dict(email=identifier if '@' in identifier else ''))) if not isinstance(user, User): logger.error("This is not a user.") return HTTPOk(location=maybe_contextual_route( request, 'password_change_sent', profile_id=user_id, _query=dict(email=identifier if '@' in identifier else ''))) send_change_password_email(request, user, email, discussion=discussion) return HTTPOk()
def assembl_register_user(request): forget(request) localizer = request.localizer session = AgentProfile.default_db json = request.json logger = logging.getLogger() discussion = discussion_from_request(request) permissions = get_permissions( Everyone, discussion.id if discussion else None) name = json.get('real_name', '').strip() errors = JSONError() if not name or len(name) < 3: errors.add_error(localizer.translate(_( "Please use a name of at least 3 characters")), ErrorTypes.SHORT_NAME) password = json.get('password', '').strip() # TODO: Check password strength. maybe pwdmeter? email = None for account in json.get('accounts', ()): email = account.get('email', None) if not is_email(email): errors.add_error(localizer.translate(_( "This is not a valid email")), ErrorTypes.INVALID_EMAIL) continue email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email).count(): if not discussion.preferences['generic_errors']: errors.add_error(localizer.translate(_( "We already have a user with this email.")), ErrorTypes.EXISTING_EMAIL, HTTPConflict.code) else: errors.add_error(localizer.translate( generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error("[User creation]: We already have a user with this email %s" % email) if not email: errors.add_error(localizer.translate(_("No email.")), ErrorTypes.INVALID_EMAIL) username = json.get('username', None) if username: if session.query(Username).filter( func.lower(Username.username) == username.lower()).count(): if not discussion.preferences['generic_errors']: errors.add_error(localizer.translate(_( "We already have a user with this username.")), ErrorTypes.EXISTING_USERNAME, HTTPConflict.code) else: errors.add_error(localizer.translate( generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error("We already have a user with username %s" % username) if len(username) > 20: errors.add_error(localizer.translate(_( "The username must be less than 20 characters.")), ErrorTypes.USERNAME_TOO_LONG, HTTPBadRequest.code) if discussion: check_subscription = discussion.preferences['whitelist_on_register'] whitelist = discussion.preferences['require_email_domain'] if check_subscription and whitelist: status = discussion.check_email(email) if not status: admin_emails = discussion.get_admin_emails() num = len(admin_emails) errors.add_error( localizer.pluralize( _("Your email domain has not been approved for registration. Please contact ${emails} for support."), _("Your email domain has not been approved for registration. Please contact one of ${emails} for support."), num, mapping={'emails': ", ".join(admin_emails)} ) ) if errors: raise errors # This logic needs to be above the JSONError checks to ensure that whitelisting is applied # even if the discussion does not have a P_SELF_REGISTER on system.Everyone if discussion and not ( P_SELF_REGISTER in permissions or P_SELF_REGISTER_REQUEST in permissions): # Consider it without context discussion = None validate_registration = asbool(config.get( 'assembl.validate_registration_emails')) old_autoflush = session.autoflush session.autoflush = False try: now = datetime.utcnow() user = User( name=name, password=password, verified=not validate_registration, creation_date=now ) session.add(user) session.flush() user.update_from_json(json, user_id=user.id) account = user.accounts[0] email = account.email account.verified = not validate_registration if discussion: agent_status = AgentStatusInDiscussion( agent_profile=user, discussion=discussion, first_visit=now, last_visit=now, user_created_on_this_discussion=True) session.add(agent_status) session.flush() # create the profile fields for custom fields for global_id, value in json.get('profileFields', {}).iteritems(): configurable_field_id = from_global_id(global_id)[1] configurable_field = AbstractConfigurableField.get(configurable_field_id) profile_field = ProfileField( agent_profile=user, configurable_field=configurable_field, discussion=configurable_field.discussion, value_data={ u'value': value } ) session.add(profile_field) session.flush() if validate_registration: send_confirmation_email(request, account) else: user.verified = True for account in user.accounts: account.verified = True user.successful_login() if asbool(config.get('pyramid.debug_authorization')): # for debugging purposes from assembl.auth.password import email_token print "email token:", request.route_url( 'user_confirm_email', token=email_token(account)) if discussion: check_subscription = discussion.preferences['whitelist_on_register'] maybe_auto_subscribe(user, discussion, check_authorization=check_subscription) session.flush() return CreationResponse(user, Everyone, permissions) finally: session.autoflush = old_autoflush
def assembl_register_user(request): forget(request) localizer = request.localizer session = AgentProfile.default_db json = request.json logger = logging.getLogger() discussion = discussion_from_request(request) permissions = get_permissions(Everyone, discussion.id if discussion else None) name = json.get('real_name', '').strip() errors = JSONError() if not name or len(name) < 3: errors.add_error( localizer.translate( _("Please use a name of at least 3 characters")), ErrorTypes.SHORT_NAME) password = json.get('password', '').strip() # TODO: Check password strength. maybe pwdmeter? email = None for account in json.get('accounts', ()): email = account.get('email', None) if not is_email(email): errors.add_error( localizer.translate(_("This is not a valid email")), ErrorTypes.INVALID_EMAIL) continue email = EmailString.normalize_email_case(email) # Find agent account to avoid duplicates! if session.query(AbstractAgentAccount).filter_by( email_ci=email).count(): if not discussion.preferences['generic_errors']: errors.add_error( localizer.translate( _("We already have a user with this email.")), ErrorTypes.EXISTING_EMAIL, HTTPConflict.code) else: errors.add_error(localizer.translate(generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error( "[User creation]: We already have a user with this email %s" % email) if not email: errors.add_error(localizer.translate(_("No email.")), ErrorTypes.INVALID_EMAIL) username = json.get('username', None) if username: if session.query(Username).filter( func.lower(Username.username) == username.lower()).count(): if not discussion.preferences['generic_errors']: errors.add_error( localizer.translate( _("We already have a user with this username.")), ErrorTypes.EXISTING_USERNAME, HTTPConflict.code) else: errors.add_error(localizer.translate(generic_error_message), ErrorTypes.GENERIC, HTTPConflict.code) logger.error("We already have a user with username %s" % username) if len(username) > 20: errors.add_error( localizer.translate( _("The username must be less than 20 characters.")), ErrorTypes.USERNAME_TOO_LONG, HTTPBadRequest.code) if discussion: check_subscription = discussion.preferences['whitelist_on_register'] whitelist = discussion.preferences['require_email_domain'] if check_subscription and whitelist: status = discussion.check_email(email) if not status: admin_emails = discussion.get_admin_emails() num = len(admin_emails) errors.add_error( localizer.pluralize( _("Your email domain has not been approved for registration. Please contact ${emails} for support." ), _("Your email domain has not been approved for registration. Please contact one of ${emails} for support." ), num, mapping={'emails': ", ".join(admin_emails)})) if errors: raise errors # This logic needs to be above the JSONError checks to ensure that whitelisting is applied # even if the discussion does not have a P_SELF_REGISTER on system.Everyone if discussion and not (P_SELF_REGISTER in permissions or P_SELF_REGISTER_REQUEST in permissions): # Consider it without context discussion = None validate_registration = asbool( config.get('assembl.validate_registration_emails')) old_autoflush = session.autoflush session.autoflush = False try: now = datetime.utcnow() user = User(name=name, password=password, verified=not validate_registration, creation_date=now) session.add(user) session.flush() user.update_from_json(json, user_id=user.id) account = user.accounts[0] email = account.email account.verified = not validate_registration if discussion: agent_status = AgentStatusInDiscussion( agent_profile=user, discussion=discussion, first_visit=now, last_visit=now, user_created_on_this_discussion=True) session.add(agent_status) session.flush() # create the profile fields for custom fields for global_id, value in json.get('profileFields', {}).iteritems(): configurable_field_id = from_global_id(global_id)[1] configurable_field = AbstractConfigurableField.get( configurable_field_id) profile_field = ProfileField( agent_profile=user, configurable_field=configurable_field, discussion=configurable_field.discussion, value_data={u'value': value}) session.add(profile_field) session.flush() if validate_registration: send_confirmation_email(request, account) else: user.verified = True for account in user.accounts: account.verified = True user.successful_login() if asbool(config.get('pyramid.debug_authorization')): # for debugging purposes from assembl.auth.password import email_token print "email token:", request.route_url( 'user_confirm_email', token=email_token(account)) if discussion: check_subscription = discussion.preferences[ 'whitelist_on_register'] maybe_auto_subscribe(user, discussion, check_authorization=check_subscription) session.flush() return CreationResponse(user, Everyone, permissions) finally: session.autoflush = old_autoflush
from urllib import urlencode from urlparse import urlparse, urlunparse, parse_qs from HTMLParser import HTMLParser from simplejson import loads from social_core.backends.base import BaseAuth from .encryption import MediactiveAESCryptor from assembl.lib.logging import getLogger log = getLogger('assembl') def clean(html_parser, s): return html_parser.unescape(s).title() class Mediactive(BaseAuth): """A simple SocialAuth backend agreed to with Mediactive. Security is based on a shared secret key. In order to activate, must add the following keys to the settings: SOCIAL_AUTH_AUTHENTICATION_BACKENDS: Add this class, eg. assembl.auth.mediactive.Mediactive SOCIAL_AUTH_MEDIACTIVE_SECRET: The shared secret key SOCIAL_AUTH_MEDIACTIVE_LOGOUT_URL: The logout URL SOCIAL_AUTH_MEDIACTIVE_KEY: An unused key, but presence needed. Typically, put `unused` SOCIAL_AUTH_MEDIACTIVE_LOGIN_URL: The login URL, including the `next` query_string """ name = 'mediactive' USERNAME_KEY = 'username' html_parser = HTMLParser()
All utility methods, classes and functions needed for testing applications """ from builtins import str from builtins import object from itertools import chain from webtest import TestRequest from webob.request import environ_from_url from pyramid.request import apply_request_extensions from pyramid.threadlocal import manager from assembl.lib.sqla import (get_session_maker, get_metadata, mark_changed) from assembl.lib import logging log = logging.getLogger('pytest.assembl') class PyramidWebTestRequest(TestRequest): """ A mock Pyramid web request this pushes itself onto the threadlocal stack that also contains the user_id according to authentication model. This is very useful because throughout the model logic, a request is often required to determine the current_user, but outside of a Pyramid view. The way a request is injected is via the current_thread from threadlocal. """ def __init__(self, *args, **kwargs): super(PyramidWebTestRequest, self).__init__(*args, **kwargs) manager.push({'request': self, 'registry': self.registry}) self._base_pyramid_request = self._pyramid_app.request_factory( self.environ)