udata 7.0.4.dev27593__py2.py3-none-any.whl → 7.0.4.dev27605__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of udata might be problematic. Click here for more details.

Files changed (31) hide show
  1. udata/core/discussions/models.py +23 -5
  2. udata/core/spam/api.py +1 -2
  3. udata/core/spam/fields.py +1 -2
  4. udata/core/spam/models.py +6 -17
  5. udata/core/spam/tests/__init__.py +0 -0
  6. udata/core/spam/tests/test_spam.py +26 -0
  7. udata/core/user/commands.py +12 -0
  8. udata/notifications/mattermost.py +2 -7
  9. udata/static/chunks/{11.c0ccea08914b6b41568e.js → 11.a23c110811a9ac943478.js} +3 -3
  10. udata/static/chunks/{11.c0ccea08914b6b41568e.js.map → 11.a23c110811a9ac943478.js.map} +1 -1
  11. udata/static/chunks/{13.526a25163ababaa44409.js → 13.0889e093f8664e38568c.js} +2 -2
  12. udata/static/chunks/{13.526a25163ababaa44409.js.map → 13.0889e093f8664e38568c.js.map} +1 -1
  13. udata/static/chunks/{16.7901839b4227881947f6.js → 16.f41599478d3e97ad9a30.js} +2 -2
  14. udata/static/chunks/{16.7901839b4227881947f6.js.map → 16.f41599478d3e97ad9a30.js.map} +1 -1
  15. udata/static/chunks/{19.471d5a2a08eef6e5338a.js → 19.2b534a26af8b17e9170b.js} +3 -3
  16. udata/static/chunks/{19.471d5a2a08eef6e5338a.js.map → 19.2b534a26af8b17e9170b.js.map} +1 -1
  17. udata/static/chunks/{5.98904a7a544eeb258e23.js → 5.bd822f0e9689f45bd582.js} +3 -3
  18. udata/static/chunks/{5.98904a7a544eeb258e23.js.map → 5.bd822f0e9689f45bd582.js.map} +1 -1
  19. udata/static/chunks/{6.e56975229e6065f68d2a.js → 6.16bb24fb8240f2746488.js} +3 -3
  20. udata/static/chunks/{6.e56975229e6065f68d2a.js.map → 6.16bb24fb8240f2746488.js.map} +1 -1
  21. udata/static/chunks/{9.534426728626f11f4571.js → 9.3e752966ff14e47e11f2.js} +2 -2
  22. udata/static/chunks/{9.534426728626f11f4571.js.map → 9.3e752966ff14e47e11f2.js.map} +1 -1
  23. udata/static/common.js +1 -1
  24. udata/static/common.js.map +1 -1
  25. udata/tests/test_discussions.py +4 -6
  26. {udata-7.0.4.dev27593.dist-info → udata-7.0.4.dev27605.dist-info}/METADATA +4 -1
  27. {udata-7.0.4.dev27593.dist-info → udata-7.0.4.dev27605.dist-info}/RECORD +31 -29
  28. {udata-7.0.4.dev27593.dist-info → udata-7.0.4.dev27605.dist-info}/LICENSE +0 -0
  29. {udata-7.0.4.dev27593.dist-info → udata-7.0.4.dev27605.dist-info}/WHEEL +0 -0
  30. {udata-7.0.4.dev27593.dist-info → udata-7.0.4.dev27605.dist-info}/entry_points.txt +0 -0
  31. {udata-7.0.4.dev27593.dist-info → udata-7.0.4.dev27605.dist-info}/top_level.txt +0 -0
@@ -19,6 +19,23 @@ class Message(SpamMixin, db.EmbeddedDocument):
19
19
 
20
20
  def texts_to_check_for_spam(self):
21
21
  return [self.content]
22
+
23
+ def spam_report_message(self, breadcrumb):
24
+ message = f"Spam potentiel dans le message"
25
+ if self.posted_by:
26
+ message += f" de [{self.posted_by.fullname}]({self.posted_by.external_url})"
27
+
28
+ if len(breadcrumb) != 2:
29
+ log.warning(f"`spam_report_message` called on message with a breadcrumb of {len(breadcrumb)} elements.", extra={ 'breadcrumb': breadcrumb})
30
+ return message
31
+
32
+ discussion = breadcrumb[0]
33
+ if not isinstance(discussion, Discussion):
34
+ log.warning(f"`spam_report_message` called on message with a breadcrumb not containing a Discussion at index 0.", extra={ 'breadcrumb': breadcrumb})
35
+ return message
36
+
37
+ message += f" sur la discussion « [{discussion.title}]({discussion.external_url}) »"
38
+ return message
22
39
 
23
40
 
24
41
  class Discussion(SpamMixin, db.Document):
@@ -60,11 +77,12 @@ class Discussion(SpamMixin, db.Document):
60
77
  _anchor='discussion-{id}'.format(id=self.id),
61
78
  _external=True)
62
79
 
63
- def spam_report_title(self):
64
- return self.title
65
-
66
- def spam_report_link(self):
67
- return self.external_url
80
+ def spam_report_message(self, breadcrumb):
81
+ message = f"Spam potentiel sur la discussion « [{self.title}]({self.external_url}) »"
82
+ if self.user:
83
+ message += f" de [{self.user.fullname}]({self.user.external_url})"
84
+
85
+ return message
68
86
 
69
87
  @spam_protected()
70
88
  def signal_new(self):
udata/core/spam/api.py CHANGED
@@ -53,6 +53,5 @@ class SpamAPI(API):
53
53
  discussions = Discussion.objects(Q(spam__status=POTENTIAL_SPAM) | Q(discussion__spam__status=POTENTIAL_SPAM))
54
54
 
55
55
  return [{
56
- 'title': discussion.spam_report_title(),
57
- 'link': discussion.spam_report_link(),
56
+ 'message': discussion.spam_report_message([discussion]),
58
57
  } for discussion in discussions]
udata/core/spam/fields.py CHANGED
@@ -6,7 +6,6 @@ spam_fields = api.model('Spam', {
6
6
  })
7
7
 
8
8
  potential_spam_fields = api.model('PotentialSpam', {
9
- 'title': fields.String(readonly=True),
10
- 'link': fields.String(readonly=True),
9
+ 'message': fields.String(readonly=True),
11
10
  })
12
11
 
udata/core/spam/models.py CHANGED
@@ -97,7 +97,7 @@ class SpamMixin(object):
97
97
 
98
98
  # Language detection is not working well with texts of a few words.
99
99
  if SpamMixin.allowed_langs() and len(text) > SpamMixin.minimum_string_length_for_lang_check():
100
- lang = detect(text)
100
+ lang = detect(text.lower())
101
101
  if lang not in SpamMixin.allowed_langs():
102
102
  self.spam.status = POTENTIAL_SPAM
103
103
  self._report(text=text, breadcrumb=breadcrumb, reason=f"not allowed language \"{lang}\"")
@@ -143,11 +143,8 @@ class SpamMixin(object):
143
143
  def embeds_to_check_for_spam(self):
144
144
  return []
145
145
 
146
- def spam_report_title(self):
147
- return type(self).__name__
148
-
149
- def spam_report_link(self):
150
- return None
146
+ def spam_report_message(self):
147
+ return f"Spam potentiel sur {type(self).__name__}"
151
148
 
152
149
  def _report(self, text, breadcrumb, reason):
153
150
  base_model = breadcrumb[0]
@@ -158,22 +155,14 @@ class SpamMixin(object):
158
155
  if document != base_model:
159
156
  return
160
157
 
161
- # Note that all the chain should be a SpamMixin, maybe we could filter out if it's not the case here…
162
- title = " → ".join(map(lambda o: o.spam_report_title(), breadcrumb))
163
-
164
- # Select the first link in the embed list (for example message doesn't have a link, so check if discussion
165
- # have one)
166
- for object in reversed(breadcrumb):
167
- link = object.spam_report_link()
168
- if link:
169
- break
158
+ message = self.spam_report_message(breadcrumb)
170
159
 
171
- on_new_potential_spam.send(self, title=title, link=link, text=text, reason=reason)
160
+ on_new_potential_spam.send(self, message=message, text=text, reason=reason)
172
161
 
173
162
  # We clean the listener here. Not sure if it's necessary either.
174
163
  signals.post_save.disconnect(report_after_save)
175
164
 
176
- # For things like `spam_report_link` we often need the ID of the document so we
165
+ # For `spam_report_message` we often need the ID of the document so we
177
166
  # must report after saving to have the ID available.
178
167
  # By default the signal is weak so it is dropped at the end of this function and it's
179
168
  # never called. We disconnect the signal in `report_after_save` to avoid leaks.
File without changes
@@ -0,0 +1,26 @@
1
+ import logging
2
+ import pytest
3
+ from udata.tests import TestCase
4
+ from udata.tests.helpers import assert_not_emit
5
+ from udata.models import db
6
+
7
+ from ..models import POTENTIAL_SPAM, SpamMixin
8
+
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+ class TestModel(SpamMixin, db.Document):
13
+ text = db.StringField(required=True)
14
+ _created = True
15
+
16
+ def texts_to_check_for_spam(self):
17
+ return [self.text]
18
+
19
+
20
+ class SpamTest(TestCase):
21
+ @pytest.mark.options(SPAM_WORDS=['spam'], SPAM_ALLOWED_LANGS=['fr'])
22
+ def test_uppercase_lang_detect(self):
23
+ model = TestModel(text="DONNEES DE RECENSEMENT - MARCHES PUBLICS")
24
+ model.detect_spam()
25
+ self.assertNotEqual(model.spam.status, POTENTIAL_SPAM)
26
+
@@ -88,3 +88,15 @@ def password(email):
88
88
  password = click.prompt('Enter new password', hide_input=True)
89
89
  user.password = hash_password(password)
90
90
  user.save()
91
+
92
+ @grp.command()
93
+ @click.argument('email')
94
+ def rotate_password(email):
95
+ '''
96
+ Ask user for password rotation on next login and reset any current session
97
+ '''
98
+ user = datastore.find_user(email=email)
99
+ user.password_rotation_demanded = datetime.utcnow()
100
+ user.save()
101
+ # Reset ongoing sessions by uniquifier
102
+ datastore.set_uniquifier(user)
@@ -5,16 +5,11 @@ from flask import current_app
5
5
 
6
6
  @on_new_potential_spam.connect
7
7
  def notify_potential_spam(sender, **kwargs):
8
- title = kwargs.get('title', 'no title')
9
- link = kwargs.get('link')
8
+ message = kwargs.get('message')
10
9
  reason = kwargs.get('reason')
11
10
  text = kwargs.get('text')
12
11
 
13
- message = ':warning: @all Spam potentiel sur '
14
- if link:
15
- message += f'[{title}]({link})'
16
- else:
17
- message += title
12
+ message = f':warning: @all {message}'
18
13
 
19
14
  if reason:
20
15
  message += f' ({reason})'