Пример #1
0
    def post(self):
        """ Block the given user.

        Request:
            `POST` `/users/block`

        Request body:
            {'user_id': @user_id@}

        """
        user = _get_user(self.request.validated['user_id'])
        user.blocked = True

        # suspend account in Discourse (suspending an account prevents a login)
        try:
            client = get_discourse_client(self.request.registry.settings)
            block_duration = 99999  # 99999 days = 273 years
            client.suspend(user.id, block_duration,
                           'account blocked by moderator')
        except:
            log.error('Suspending account in Discourse failed: %d',
                      user.id,
                      exc_info=True)
            raise HTTPInternalServerError(
                'Suspending account in Discourse failed')

        return {}
Пример #2
0
    def post(self):
        request = self.request
        user = request.validated['user']
        user.password = request.validated['password']

        # The user was validated by the nonce so we can log in
        token = log_validated_user_i_know_what_i_do(user, request)

        if token:
            settings = request.registry.settings
            response = token_to_response(user, token, request)
            try:
                client = get_discourse_client(settings)
                r = client.redirect_without_nonce(user)
                response['redirect_internal'] = r
            except:
                # Since only the password is changed, any error with discourse
                # must not prevent login and validation.
                log.error(
                    'Error logging into discourse for %d', user.id,
                    exc_info=True)

            user.clear_validation_nonce()
            try:
                DBSession.flush()
            except:
                log.warning('Error persisting user', exc_info=True)
                raise HTTPInternalServerError('Error persisting user')

            return response
        else:
            request.errors.status = 403
            request.errors.add('body', 'user', 'Login failed')
            return None
Пример #3
0
    def post(self):
        request = self.request
        username = request.validated['username']
        password = request.validated['password']
        user = DBSession.query(User). \
            filter(User.username == username).first()

        token = try_login(user, password, request) if user else None
        if token:
            response = token_to_response(user, token, request)
            if 'discourse' in request.json:
                settings = request.registry.settings
                client = get_discourse_client(settings)
                try:
                    if 'sso' in request.json and 'sig' in request.json:
                        sso = request.json['sso']
                        sig = request.json['sig']
                        redirect = client.redirect(user, sso, sig)
                        response['redirect'] = redirect
                    else:
                        r = client.redirect_without_nonce(user)
                        response['redirect_internal'] = r
                except:
                    # Any error with discourse should not prevent login
                    log.warning('Error logging into discourse for %d',
                                user.id,
                                exc_info=True)
            return response
        else:
            request.errors.status = 403
            request.errors.add('body', 'user', 'Login failed')
            return None
Пример #4
0
    def post(self):
        request = self.request
        username = request.validated['username']
        password = request.validated['password']
        user = DBSession.query(User). \
            filter(User.username == username).first()

        token = try_login(user, password, request) if user else None
        if token:
            response = token_to_response(user, token, request)
            if 'discourse' in request.json:
                settings = request.registry.settings
                client = get_discourse_client(settings)
                try:
                    if 'sso' in request.json and 'sig' in request.json:
                        sso = request.json['sso']
                        sig = request.json['sig']
                        redirect = client.redirect(user, sso, sig)
                        response['redirect'] = redirect
                    else:
                        r = client.redirect_without_nonce(user)
                        response['redirect_internal'] = r
                except:
                    # Any error with discourse should not prevent login
                    log.warning(
                        'Error logging into discourse for %d', user.id,
                        exc_info=True)
            return response
        else:
            request.errors.status = 403
            request.errors.add('body', 'user', 'Login failed')
            return None
Пример #5
0
    def post(self):
        request = self.request
        user = request.validated['user']
        user.password = request.validated['password']

        # The user was validated by the nonce so we can log in
        token = log_validated_user_i_know_what_i_do(user, request)

        if token:
            settings = request.registry.settings
            response = token_to_response(user, token, request)
            try:
                client = get_discourse_client(settings)
                r = client.redirect_without_nonce(user)
                response['redirect_internal'] = r
            except:
                # Since only the password is changed, any error with discourse
                # must not prevent login and validation.
                log.error('Error logging into discourse for %d',
                          user.id,
                          exc_info=True)

            user.clear_validation_nonce()
            try:
                DBSession.flush()
            except:
                log.warning('Error persisting user', exc_info=True)
                raise HTTPInternalServerError('Error persisting user')

            return response
        else:
            request.errors.status = 403
            request.errors.add('body', 'user', 'Login failed')
            return None
Пример #6
0
    def collection_post(self):
        settings = self.request.registry.settings

        locale = self.request.validated['locale']

        title = "{}_{}".format(locale.document_id, locale.lang)
        content = '<a href="{}">{}</a>'.format(
                self.request.referer,
                locale.title)
        category = settings['discourse.category']
        # category could be id or name
        try:
            category = int(category)
        except:
            pass

        client = get_discourse_client(settings)
        try:
            response = client.client.create_post(content,
                                                 title=title,
                                                 category=category)
        except:
            raise HTTPInternalServerError('Error with Discourse')

        if "topic_id" in response:
            document_topic = DocumentTopic(topic_id=response['topic_id'])
            locale.document_topic = document_topic
            update_cache_version_direct(locale.document_id)
            DBSession.flush()

        return response
Пример #7
0
    def post(self):
        request = self.request
        username = request.validated["username"]
        password = request.validated["password"]
        user = DBSession.query(User).filter(User.username == username).first()

        token = try_login(user, password, request) if user else None
        if token:
            response = token_to_response(user, token, request)
            if "discourse" in request.json:
                settings = request.registry.settings
                client = get_discourse_client(settings)
                try:
                    if "sso" in request.json and "sig" in request.json:
                        sso = request.json["sso"]
                        sig = request.json["sig"]
                        redirect = client.redirect(user, sso, sig)
                        response["redirect"] = redirect
                    else:
                        r = client.redirect_without_nonce(user)
                        response["redirect_internal"] = r
                except:
                    # Any error with discourse should not prevent login
                    log.warning("Error logging into discourse for %d", user.id, exc_info=True)
            return response
        else:
            request.errors.status = 403
            request.errors.add("body", "user", "Login failed")
            return None
Пример #8
0
    def post(self):
        """ Unblock the given user.

        Request:
            `POST` `/users/unblock`

        Request body:
            {'user_id': @user_id@}

        """
        user = _get_user(self.request.validated['user_id'])
        user.blocked = False
        # Make sure the rate limiting counter is reset
        # (it might be the reason the user was blocked):
        user.ratelimit_times = 0

        # unsuspend account in Discourse
        try:
            client = get_discourse_client(self.request.registry.settings)
            client.unsuspend(user.id)
        except:
            log.error('Unsuspending account in Discourse failed: %d',
                      user.id,
                      exc_info=True)
            raise HTTPInternalServerError(
                'Unsuspending account in Discourse failed')

        return {}
Пример #9
0
    def get(self):
        settings = self.request.registry.settings
        userid = self.request.authenticated_userid

        client = get_discourse_client(settings)
        d_username = client.get_username(userid)
        messages = client.client.private_messages_unread(d_username)

        count = len(messages['topic_list']['topics'])
        link = '%s/users/%s/messages' % (client.discourse_public_url,
                                         d_username)

        return {link: link, count: count}
Пример #10
0
    def get(self):
        settings = self.request.registry.settings
        userid = self.request.authenticated_userid

        client = get_discourse_client(settings)
        d_username = client.get_username(userid)
        messages = client.client.private_messages_unread(d_username)

        count = len(messages['topic_list']['topics'])
        link = '%s/users/%s/messages' % (
            client.discourse_public_url, d_username)

        return {link: link, count: count}
Пример #11
0
 def post(self):
     request = self.request
     userid = request.authenticated_userid
     result = {"user": userid}
     remove_token(extract_token(request))
     if "discourse" in request.json:
         try:
             settings = request.registry.settings
             client = get_discourse_client(settings)
             result["logged_out_discourse_user"] = client.logout(userid)
         except:
             # Any error with discourse should not prevent logout
             log.warning("Error logging out of discourse for %d", userid, exc_info=True)
     return result
Пример #12
0
 def post(self):
     user = self.request.validated['sso_user']
     token = log_validated_user_i_know_what_i_do(user, self.request)
     response = token_to_response(user, token, self.request)
     if 'discourse' in self.request.json:
         client = get_discourse_client(self.request.registry.settings)
         try:
             r = client.redirect_without_nonce(user)
             response['redirect_internal'] = r
         except Exception:
             # Any error with discourse should not prevent login
             log.warning('Error logging into discourse for %d',
                         user.id,
                         exc_info=True)
     return response
Пример #13
0
    def collection_post(self):
        settings = self.request.registry.settings

        locale = self.request.validated['locale']
        document = self.request.validated['document']
        document_type = association_keys_for_types[document.type]

        document_path = "/{}/{}/{}".format(document_type, locale.document_id,
                                           locale.lang)

        content = '<a href="https://www.camptocamp.org{}">{}</a>'.format(
            document_path, locale.title or document_path)

        category = settings['discourse.category']
        # category could be id or name
        try:
            category = int(category)
        except Exception:
            pass

        client = get_discourse_client(settings)
        try:
            title = "{}_{}".format(locale.document_id, locale.lang)
            response = client.client.create_post(content,
                                                 title=title,
                                                 category=category)
        except Exception as e:
            log.error('Error with Discourse: {}'.format(str(e)), exc_info=True)
            raise HTTPInternalServerError('Error with Discourse')

        if "topic_id" in response:
            topic_id = response['topic_id']

            document_topic = DocumentTopic(topic_id=topic_id)
            locale.document_topic = document_topic
            update_cache_version_direct(locale.document_id)
            DBSession.flush()

            if locale.type == document_types.OUTING_TYPE:
                try:
                    self.invite_participants(client, locale, topic_id)
                except Exception:
                    log.error(
                        'Inviting participants of outing {} failed'.format(
                            locale.document_id),
                        exc_info=True)

        return response
Пример #14
0
 def post(self):
     request = self.request
     userid = request.authenticated_userid
     result = {'user': userid}
     remove_token(extract_token(request))
     if 'discourse' in request.json:
         try:
             settings = request.registry.settings
             client = get_discourse_client(settings)
             result['logged_out_discourse_user'] = client.logout(userid)
         except:
             # Any error with discourse should not prevent logout
             log.warning('Error logging out of discourse for %d',
                         userid,
                         exc_info=True)
     return result
Пример #15
0
    def post(self):
        request = self.request
        user = request.validated['user']
        user.clear_validation_nonce()
        user.email = user.email_to_validate
        user.email_to_validate = None

        # Synchronize the new email (and other parameters)
        try:
            client = get_discourse_client(request.registry.settings)
            client.sync_sso(user)
        except:
            log.error('Error syncing email with discourse', exc_info=True)
            raise HTTPInternalServerError('Error with Discourse')

        try:
            DBSession.flush()
        except:
            log.warning('Error persisting user', exc_info=True)
            raise HTTPInternalServerError('Error persisting user')
Пример #16
0
    def post(self):
        request = self.request
        user = request.validated['user']
        user.clear_validation_nonce()
        user.email = user.email_to_validate
        user.email_to_validate = None

        # Synchronize the new email (and other parameters)
        try:
            client = get_discourse_client(request.registry.settings)
            client.sync_sso(user)
        except:
            log.error('Error syncing email with discourse', exc_info=True)
            raise HTTPInternalServerError('Error with Discourse')

        try:
            DBSession.flush()
        except:
            log.warning('Error persisting user', exc_info=True)
            raise HTTPInternalServerError('Error persisting user')
Пример #17
0
    def post(self):
        request = self.request
        user = request.validated['user']
        user.clear_validation_nonce()
        user.email_validated = True

        # the user profile can be indexed once the account is confirmed
        notify_es_syncer(self.request.registry.queue_config)

        # Synchronizing to Discourse is unnecessary as it will be done
        # during the redirect_without_nonce call below.

        # The user was validated by the nonce so we can log in
        token = log_validated_user_i_know_what_i_do(user, request)

        if token:
            response = token_to_response(user, token, request)
            settings = request.registry.settings
            try:
                client = get_discourse_client(settings)
                r = client.redirect_without_nonce(user)
                response['redirect_internal'] = r
            except:
                # Any error with discourse must prevent login and validation
                log.error('Error logging into discourse for %d',
                          user.id,
                          exc_info=True)
                raise HTTPInternalServerError('Error with Discourse')

            try:
                DBSession.flush()
            except:
                log.warning('Error persisting user', exc_info=True)
                raise HTTPInternalServerError('Error persisting user')

            return response
        else:
            request.errors.status = 403
            request.errors.add('body', 'user', 'Login failed')
            return None
Пример #18
0
    def post(self):
        request = self.request
        user = request.validated['user']
        user.clear_validation_nonce()
        user.email_validated = True

        # the user profile can be indexed once the account is confirmed
        notify_es_syncer(self.request.registry.queue_config)

        # Synchronizing to Discourse is unnecessary as it will be done
        # during the redirect_without_nonce call below.

        # The user was validated by the nonce so we can log in
        token = log_validated_user_i_know_what_i_do(user, request)

        if token:
            response = token_to_response(user, token, request)
            settings = request.registry.settings
            try:
                client = get_discourse_client(settings)
                r = client.redirect_without_nonce(user)
                response['redirect_internal'] = r
            except:
                # Any error with discourse must prevent login and validation
                log.error(
                    'Error logging into discourse for %d', user.id,
                    exc_info=True)
                raise HTTPInternalServerError('Error with Discourse')

            try:
                DBSession.flush()
            except:
                log.warning('Error persisting user', exc_info=True)
                raise HTTPInternalServerError('Error persisting user')

            return response
        else:
            request.errors.status = 403
            request.errors.add('body', 'user', 'Login failed')
            return None
Пример #19
0
 def setUp(self):  # noqa
     self.original_discourse_client = get_discourse_client(self.settings)
     self._prefix = "/users"
     self._model = User
     BaseTestRest.setUp(self)
     self.set_discourse_up()
Пример #20
0
    def post(self):
        user = self.get_user()
        request = self.request
        validated = request.validated

        result = {}

        # Before all, check whether the user knows the current password
        current_password = validated['currentpassword']
        if not user.validate_password(current_password):
            request.errors.add('body', 'currentpassword', 'Invalid password')
            return

        sync_sso = False

        # update password if a new password is provided
        if 'newpassword' in validated:
            user.password = validated['newpassword']

        # start email validation procedure if a new email is provided
        email_link = None
        if 'email' in validated and validated['email'] != user.email:
            user.email_to_validate = validated['email']
            user.update_validation_nonce(
                    Purpose.change_email,
                    VALIDATION_EXPIRE_DAYS)
            email_service = get_email_service(self.request)
            nonce = user.validation_nonce
            settings = request.registry.settings
            link = settings['mail.validate_change_email_url_template'].format(
                '#', nonce)
            email_link = link
            result['email'] = validated['email']
            result['sent_email'] = True
            sync_sso = True

        update_search_index = False
        if 'name' in validated:
            user.name = validated['name']
            result['name'] = user.name
            update_search_index = True
            sync_sso = True

        if 'forum_username' in validated:
            user.forum_username = validated['forum_username']
            result['forum_username'] = user.forum_username
            update_search_index = True
            sync_sso = True

        if 'is_profile_public' in validated:
            user.is_profile_public = validated['is_profile_public']

        # Synchronize everything except the new email (still stored
        # in the email_to_validate attribute while validation is pending).
        if sync_sso:
            try:
                client = get_discourse_client(request.registry.settings)
                client.sync_sso(user)
            except:
                log.error('Error syncing with discourse', exc_info=True)
                raise HTTPInternalServerError('Error with Discourse')

        try:
            DBSession.flush()
        except:
            log.warning('Error persisting user', exc_info=True)
            raise HTTPInternalServerError('Error persisting user')

        if email_link:
            email_service.send_change_email_confirmation(user, link)

        if update_search_index:
            # when user name changes, the search index has to be updated
            notify_es_syncer(self.request.registry.queue_config)

            # also update the cache version of the user profile
            update_cache_version(user.profile)

        return result
Пример #21
0
    def post(self):
        user = self.get_user()
        request = self.request
        validated = request.validated

        result = {}

        # Before all, check whether the user knows the current password
        current_password = validated['currentpassword']
        if not user.validate_password(current_password):
            request.errors.add('body', 'currentpassword', 'Invalid password')
            return

        sync_sso = False

        # update password if a new password is provided
        if 'newpassword' in validated:
            user.password = validated['newpassword']

        # start email validation procedure if a new email is provided
        email_link = None
        if 'email' in validated and validated['email'] != user.email:
            user.email_to_validate = validated['email']
            user.update_validation_nonce(Purpose.change_email,
                                         VALIDATION_EXPIRE_DAYS)
            email_service = get_email_service(self.request)
            nonce = user.validation_nonce
            settings = request.registry.settings
            link = settings['mail.validate_change_email_url_template'].format(
                '#', nonce)
            email_link = link
            result['email'] = validated['email']
            result['sent_email'] = True
            sync_sso = True

        update_search_index = False
        if 'name' in validated:
            user.name = validated['name']
            result['name'] = user.name
            update_search_index = True
            sync_sso = True

        if 'forum_username' in validated:
            user.forum_username = validated['forum_username']
            result['forum_username'] = user.forum_username
            update_search_index = True
            sync_sso = True

        if 'is_profile_public' in validated:
            user.is_profile_public = validated['is_profile_public']

        # Synchronize everything except the new email (still stored
        # in the email_to_validate attribute while validation is pending).
        if sync_sso:
            try:
                client = get_discourse_client(request.registry.settings)
                client.sync_sso(user)
            except Exception:
                log.error('Error syncing with discourse', exc_info=True)
                raise HTTPInternalServerError('Error with Discourse')

        try:
            DBSession.flush()
        except Exception:
            log.warning('Error persisting user', exc_info=True)
            raise HTTPInternalServerError('Error persisting user')

        if email_link:
            email_service.send_change_email_confirmation(user, link)

        if update_search_index:
            # when user name changes, the search index has to be updated
            notify_es_syncer(self.request.registry.queue_config)

            # also update the cache version of the user profile
            update_cache_version(user.profile)

        return result
Пример #22
0
 def __init__(self, *args, **kwargs):
     BaseTestRest.__init__(self, *args, **kwargs)
     self.original_discourse_client = get_discourse_client(self.settings)
Пример #23
0
    def post(self):
        """
        Synchronize user details and return authentication url.
        Important: Email addresses need to be validated by external site.
        """
        request = self.request
        sso_key = request.validated['sso_key']
        sso_external_id = request.validated['sso_external_id']
        user = request.validated['sso_user']

        if user is None:
            # create new user
            user = User(
                username=request.validated['username'],
                name=request.validated['name'],
                forum_username=request.validated['forum_username'],
                email=request.validated['email'],
                email_validated=True,  # MUST be validated by external site
                lang=request.validated['lang'],
                password=generate_token()  # random password
            )
            # directly create the user profile, the document id of the profile
            # is the user id
            lang = user.lang
            user.profile = UserProfile(
                categories=['amateur'],
                locales=[DocumentLocale(lang=lang, title='')],
            )
            DBSession.add(user)
            DBSession.flush()

        if sso_external_id is None:
            sso_external_id = SsoExternalId(
                domain=sso_key.domain,
                external_id=request.validated['external_id'],
                user=user,
            )
            DBSession.add(sso_external_id)

        sso_external_id.token = generate_token()
        sso_external_id.expire = sso_expire_from_now()

        client = get_discourse_client(request.registry.settings)
        discourse_userid = call_discourse(get_discourse_userid, client,
                                          user.id)
        if discourse_userid is None:
            call_discourse(client.sync_sso, user)
            discourse_userid = client.get_userid(user.id)  # From cache

        # Groups are added to discourse, not removed
        group_ids = []
        discourse_groups = None
        groups = request.validated['groups'] or ''
        for group_name in groups.split(','):
            if group_name == '':
                continue
            group_id = None
            if discourse_groups is None:
                discourse_groups = call_discourse(client.client.groups)

            group_id = None
            for discourse_group in discourse_groups:
                if discourse_group['name'] == group_name:
                    group_id = discourse_group['id']

            if group_id is None:
                # If group is not found, we ignore it as we want to return
                # a valid token for user authentication
                pass
            else:
                group_ids.append(group_id)

        for group_id in group_ids:
            call_discourse(client.client.add_user_to_group, group_id,
                           discourse_userid)

        return {
            'url':
            '{}/sso-login?no_redirect&{}'.format(
                request.registry.settings['ui.url'],
                urlencode({'token': sso_external_id.token}))
        }