From 48ff98f3977e8d03a17d8133e9927d545483999e Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jul 16 2020 15:58:52 +0000 Subject: [PATCH 1/5] Transmit the message data to the decision handler Signed-off-by: Aurélien Bompard --- diff --git a/basset-worker b/basset-worker index 1c8a1fc..924affc 100755 --- a/basset-worker +++ b/basset-worker @@ -127,7 +127,7 @@ def callback(ch, method, properties, body): mids = core.store_decisions(mid, decisions) log.debug('Decision IDs: %s' % ', '.join(mids)) log.debug('Processing decision') - if core.process_decisions(decisions) is True: + if core.process_decisions(decisions, message_data=data) is True: log.debug('Decision processed. Acknowledging') ch.basic_ack(delivery_tag = method.delivery_tag) else: diff --git a/basset/core.py b/basset/core.py index bd10456..d1c71e6 100644 --- a/basset/core.py +++ b/basset/core.py @@ -225,7 +225,7 @@ class Core(object): log.error('Action not handled: %s' % action) raise Exception('Unknown action submitted: %s' % action) - def process_decision(self, decision): + def process_decision(self, decision, message_data): log.info('Processing decision %s' % decision) handled = False @@ -233,11 +233,11 @@ class Core(object): log.debug('Checking if %s handles it' % plugin) if plugin.handles(decision['action']): log.debug('Yes, requesting handling') - return plugin.handle(decision) + return plugin.handle(decision, message_data) log.warning('Nothing handled this decision?') - def process_decisions(self, decisions): + def process_decisions(self, decisions, message_data=None): if isinstance(decisions, dict): # If we have just one, we have just one decisions = [decisions] @@ -246,7 +246,7 @@ class Core(object): results = [] for decision in decisions: - results.append(self.process_decision(decision)) + results.append(self.process_decision(decision, message_data)) log.info('Processing results: %s' % results) if False in results: diff --git a/basset/decision/base.py b/basset/decision/base.py index 8c98be4..42f0e3e 100644 --- a/basset/decision/base.py +++ b/basset/decision/base.py @@ -32,5 +32,5 @@ class DecisionPlugin(Plugin): def generate_decisions(self, action, time, data, decision): raise NotImplementedError() - def handle(self, decision): + def handle(self, decision, message_data): raise NotImplementedError() diff --git a/basset/decision/fas.py b/basset/decision/fas.py index 670b835..fa88d89 100644 --- a/basset/decision/fas.py +++ b/basset/decision/fas.py @@ -68,7 +68,7 @@ class FASRegisterDecision(DecisionPlugin): else: return True - def handle(self, decision): + def handle(self, decision, message_data): if not self.coreconfig.get('core', 'do_fas') == 'true': self.log.info('Ignoring FAS decisions') return True @@ -130,7 +130,7 @@ class FASCLADecision(DecisionPlugin): self.log.error('Error removing user: %s' % res) return False - def handle(self, decision): + def handle(self, decision, message_data): if not self.coreconfig.get('core', 'do_fas') == 'true': self.log.info('Ignoring FAS decisions') return True diff --git a/basset/decision/mediawiki.py b/basset/decision/mediawiki.py index 9978ef7..9f783e1 100644 --- a/basset/decision/mediawiki.py +++ b/basset/decision/mediawiki.py @@ -85,7 +85,7 @@ class MediawikiDecision(DecisionPlugin): def _revert_page(self, page): raise NotImplementedError() - def handle(self, decision): + def handle(self, decision, message_data): if not self.coreconfig.get('core', 'do_wiki') == 'true': self.log.info('Ignoring wiki decisions') return True @@ -148,7 +148,7 @@ class MediawikiBlockDecision(DecisionPlugin): else: return True - def handle(self, decision): + def handle(self, decision, message_data): if not self.coreconfig.get('core', 'do_wiki') == 'true': self.log.info('Ignoring wiki decisions') return True diff --git a/basset/decision/trac.py b/basset/decision/trac.py index 9cd7d26..9277b18 100644 --- a/basset/decision/trac.py +++ b/basset/decision/trac.py @@ -111,7 +111,7 @@ class TracDecision(DecisionPlugin): def _block_user(self, trac_base_url, username): return self._call(trac_base_url, 'basset.blockUser', username) - def handle(self, decision): + def handle(self, decision, message_data): if not self.coreconfig.get('core', 'do_trac') == 'true': self.log.info('Ignoring trac decisions') return True From 77d8bd0b2d4c35529a49601aa7eab6e789608fbe Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jul 16 2020 15:59:35 +0000 Subject: [PATCH 2/5] Handle user registrations without a country code or a phone number Signed-off-by: Aurélien Bompard --- diff --git a/basset/score/details.py b/basset/score/details.py index 15c51e9..030763f 100644 --- a/basset/score/details.py +++ b/basset/score/details.py @@ -29,8 +29,8 @@ class DetailSanityScore(ScorePlugin): username = data['user']['username'] email = data['user']['email'] human_name = data['user']['human_name'] - country = data['user']['country_code'] - phone = data['user']['telephone'] + country = data['user'].get('country_code') + phone = data['user'].get('telephone') scores = {} scores['gpg_ssh_irc'] = self.get_gpg_ssh_irc_score(data['user']) @@ -93,6 +93,8 @@ class DetailSanityScore(ScorePlugin): return 0 def get_country_scores(self, country): + if country is None: + return 0 mapping = {} for option in self.config: if option.startswith('country_'): @@ -103,6 +105,8 @@ class DetailSanityScore(ScorePlugin): return 0 def get_phone_scores(self, country, phonenr): + if country is None or phonenr is None: + return 0 mapping = {} for option in self.config: if option.startswith('phone_prefix_'): From 1752e3f857b786de1ddd6986721c73eb782e2948 Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jul 16 2020 16:00:21 +0000 Subject: [PATCH 3/5] Add another way to get the registering user's ip address Signed-off-by: Aurélien Bompard --- diff --git a/basset/score/ip.py b/basset/score/ip.py index 7166f79..0ec6221 100644 --- a/basset/score/ip.py +++ b/basset/score/ip.py @@ -43,7 +43,11 @@ class IPScore(ScorePlugin): scores = {} if 'request_headers' in data: - ip = data['request_headers']['x-forwarded-for'] + ip = data['request_headers'].get('x-forwarded-for') + if not ip: + ip = data.get('request_ip') + if not ip: + return {} if ',' in ip: ip = ip.split(',')[0] From 6255bf087888146691b43e731db1a68b01f2b5c0 Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jul 16 2020 16:00:45 +0000 Subject: [PATCH 4/5] Minor bugfix Signed-off-by: Aurélien Bompard --- diff --git a/basset/score/ip.py b/basset/score/ip.py index 0ec6221..2ed3ee9 100644 --- a/basset/score/ip.py +++ b/basset/score/ip.py @@ -66,6 +66,7 @@ class IPScore(ScorePlugin): return requests.get(url).json() def get_ip_asn_score(self, ip): + mapping = {} for option in self.config: if option.startswith('asn_'): mapping[option[len('asn_'):]] = int(self.config[option]) From b85721d7edb73e8a075a21fff86af372340f22d5 Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jul 16 2020 16:01:29 +0000 Subject: [PATCH 5/5] Add a Noggin decision plugin Signed-off-by: Aurélien Bompard --- diff --git a/basset/core.py b/basset/core.py index d1c71e6..dcbe99b 100644 --- a/basset/core.py +++ b/basset/core.py @@ -32,6 +32,7 @@ from basset.score.content import ContentScore from basset.score.mx import MailExchangeScore from basset.decision.fas import FASRegisterDecision from basset.decision.fas import FASCLADecision +from basset.decision.noggin import NogginRegisterDecision from basset.decision.mediawiki import MediawikiDecision, MediawikiBlockDecision from basset.decision.trac import TracDecision @@ -47,7 +48,8 @@ PLUGINS = { FASCLADecision, MediawikiDecision, MediawikiBlockDecision, - TracDecision + TracDecision, + NogginRegisterDecision, ] } diff --git a/basset/decision/noggin.py b/basset/decision/noggin.py new file mode 100644 index 0000000..200abd5 --- /dev/null +++ b/basset/decision/noggin.py @@ -0,0 +1,78 @@ +# Copyright (c) 2016, Patrick Uiterwijk +# All rights reserved. +# +# This file is part of Basset. +# +# Basset is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Basset is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Basset. If not, see . + + +import requests + +from basset.decision.base import DecisionPlugin + + +class NogginRegisterDecision(DecisionPlugin): + name = 'nogginregistration' + handled_decisions = ['fedora.noggin.registration'] + + def tests_to_run(self, action, time, data): + return ['ip', 'details', 'mailexchange'] + + def generate_decisions(self, action, time, data, decision): + if decision is None: + return {'action': action, + 'username': data['user']['username'], + 'decision': 'manual'} + elif decision is True: + return {'action': action, + 'username': data['user']['username'], + 'decision': 'accept'} + else: + return {'action': action, + 'username': data['user']['username'], + 'decision': 'deny'} + + def _update_account_status(self, username, new_status, message_data): + self.log.info('Updating status for %s to %s' % (username, new_status)) + response = requests.post( + message_data["callback"], json={ + "token": message_data["token"], + "status": new_status, + } + ) + if not response.ok or response.json().get("status") != "success": + self.log.error('Error saving status: %s %s', + response.status_code, response.text) + return False + + return True + + def handle(self, decision, message_data): + if not self.coreconfig.get('core', 'do_noggin') == 'true': + self.log.info('Ignoring noggin decisions') + return True + + if decision['decision'] == 'deny': + return self._update_account_status( + decision['username'], 'spamcheck_denied', message_data) + elif decision['decision'] == 'manual': + return self._update_account_status( + decision['username'], 'spamcheck_manual', message_data) + elif decision['decision'] == 'accept': + return self._update_account_status( + decision['username'], 'active', message_data) + else: + raise ValueError( + 'Invalid decision value: {!r}'.format(decision['decision']) + ) diff --git a/worker.default.cfg b/worker.default.cfg index 36f7718..2d642a0 100644 --- a/worker.default.cfg +++ b/worker.default.cfg @@ -11,6 +11,7 @@ registration_check_manually = 5 registration_deny = 10 do_fas = true +do_noggin = true do_wiki = true do_trac = true