summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByron Jones <bjones@mozilla.com>2013-11-20 08:00:04 +0100
committerByron Jones <bjones@mozilla.com>2013-11-20 08:00:04 +0100
commitd4e9febd6cfbcf96e87809b68425e6f19a0b9f39 (patch)
tree66ba948c6833dd0ede10c7462fd82631792274c6
parentf1adcd997f1dc08b60004cd2fe9b356ace295f54 (diff)
downloadbugzilla-d4e9febd6cfbcf96e87809b68425e6f19a0b9f39.tar.gz
bugzilla-d4e9febd6cfbcf96e87809b68425e6f19a0b9f39.tar.xz
Bug 935570: create an anti-spam extension
-rw-r--r--extensions/AntiSpam/Extension.pm184
-rw-r--r--extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl16
-rw-r--r--extensions/AntiSpam/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl (renamed from extensions/ProjectHoneyPot/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl)0
-rw-r--r--extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl13
-rw-r--r--extensions/EditTable/Config.pm (renamed from extensions/ProjectHoneyPot/Config.pm)8
-rw-r--r--extensions/EditTable/Extension.pm180
-rw-r--r--extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl11
-rw-r--r--extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl17
-rw-r--r--extensions/EditTable/template/en/default/pages/edit_table.html.tmpl43
-rw-r--r--extensions/EditTable/web/js/edit_table.js131
-rw-r--r--extensions/EditTable/web/styles/edit_table.css39
-rw-r--r--extensions/ProjectHoneyPot/Extension.pm63
12 files changed, 637 insertions, 68 deletions
diff --git a/extensions/AntiSpam/Extension.pm b/extensions/AntiSpam/Extension.pm
index 5cb9928db..ef31a0b2d 100644
--- a/extensions/AntiSpam/Extension.pm
+++ b/extensions/AntiSpam/Extension.pm
@@ -12,7 +12,178 @@ use warnings;
use base qw(Bugzilla::Extension);
-our $VERSION = '0';
+use Bugzilla::Error;
+use Bugzilla::Group;
+use Bugzilla::Util qw(remote_ip trick_taint);
+use Email::Address;
+use Encode;
+use Socket;
+use Sys::Syslog qw(:DEFAULT setlogsock);
+
+our $VERSION = '1';
+
+#
+# project honeypot integration
+#
+
+sub _project_honeypot_blocking {
+ my ($self, $api_key, $login) = @_;
+ my $ip = remote_ip();
+ return unless $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ my $lookup = "$api_key.$4.$3.$2.$1.dnsbl.httpbl.org";
+ return unless my $packed = gethostbyname($lookup);
+ my $honeypot = inet_ntoa($packed);
+ return unless $honeypot =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ my ($status, $days, $threat, $type) = ($1, $2, $3, $4);
+
+ return if $status != 127
+ || $threat < Bugzilla->params->{honeypot_threat_threshold};
+
+ _syslog(sprintf("[audit] blocked <%s> from creating %s, honeypot %s", $ip, $login, $honeypot));
+ ThrowUserError('account_creation_restricted');
+}
+
+sub config_modify_panels {
+ my ($self, $args) = @_;
+ push @{ $args->{panels}->{auth}->{params} }, {
+ name => 'honeypot_api_key',
+ type => 't',
+ default => '',
+ };
+ push @{ $args->{panels}->{auth}->{params} }, {
+ name => 'honeypot_threat_threshold',
+ type => 't',
+ default => '32',
+ };
+}
+
+#
+# comment blocking
+#
+
+sub _comment_blocking {
+ my ($self, $params) = @_;
+
+ # as we want to use this sparingly, we only block comments on bugs which
+ # the user didn't report, and skip it completely if the user is in the
+ # editbugs group.
+ my $user = Bugzilla->user;
+ return if $user->in_group('editbugs');
+ # new bug
+ return unless $params->{bug_id};
+ # existing bug
+ my $bug = ref($params->{bug_id})
+ ? $params->{bug_id}
+ : Bugzilla::Bug->new($params->{bug_id});
+ return if $bug->reporter->id == $user->id;
+
+ my $blocklist = Bugzilla->dbh->selectcol_arrayref(
+ 'SELECT word FROM antispam_comment_blocklist'
+ );
+ return unless @$blocklist;
+
+ my $regex = '\b(?:' . join('|', map { quotemeta } @$blocklist) . ')\b';
+ if ($params->{thetext} =~ /$regex/i) {
+ ThrowUserError('antispam_comment_blocked');
+ }
+}
+
+#
+# domain blocking
+#
+
+sub _domain_blocking {
+ my ($self, $login) = @_;
+ my $address = Email::Address->new(undef, $login);
+ my $blocked = Bugzilla->dbh->selectrow_array(
+ "SELECT 1 FROM antispam_domain_blocklist WHERE domain=?",
+ undef,
+ $address->host
+ );
+ if ($blocked) {
+ _syslog(sprintf("[audit] blocked <%s> from creating %s, blacklisted domain", remote_ip(), $login));
+ ThrowUserError('account_creation_restricted');
+ }
+}
+
+#
+# ip blocking
+#
+
+sub _ip_blocking {
+ my ($self, $login) = @_;
+ my $ip = remote_ip();
+ trick_taint($ip);
+ my $blocked = Bugzilla->dbh->selectrow_array(
+ "SELECT 1 FROM antispam_ip_blocklist WHERE ip_address=?",
+ undef,
+ $ip
+ );
+ if ($blocked) {
+ _syslog(sprintf("[audit] blocked <%s> from creating %s, blacklisted IP", $ip, $login));
+ ThrowUserError('account_creation_restricted');
+ }
+}
+
+#
+# hooks
+#
+
+sub object_end_of_create_validators {
+ my ($self, $args) = @_;
+ if ($args->{class} eq 'Bugzilla::Comment') {
+ $self->_comment_blocking($args->{params});
+ }
+}
+
+sub user_verify_login {
+ my ($self, $args) = @_;
+ if (my $api_key = Bugzilla->params->{honeypot_api_key}) {
+ $self->_project_honeypot_blocking($api_key, $args->{login});
+ }
+ $self->_ip_blocking($args->{login});
+ $self->_domain_blocking($args->{login});
+}
+
+sub editable_tables {
+ my ($self, $args) = @_;
+ my $tables = $args->{tables};
+ # allow these tables to be edited with the EditTables extension
+ $tables->{antispam_domain_blocklist} = {
+ id_field => 'id',
+ order_by => 'domain',
+ blurb => 'List of fully qualified domain names to block at account creation time.',
+ group => 'can_configure_antispam',
+ };
+ $tables->{antispam_comment_blocklist} = {
+ id_field => 'id',
+ order_by => 'word',
+ blurb => "List of whole words that will cause comments containing \\b\$word\\b to be blocked.\n" .
+ "This only applies to comments on bugs which the user didn't report.\n" .
+ "Users in the editbugs group are exempt from comment blocking.",
+ group => 'can_configure_antispam',
+ };
+ $tables->{antispam_ip_blocklist} = {
+ id_field => 'id',
+ order_by => 'ip_address',
+ blurb => 'List of IPv4 addresses which are prevented from creating accounts.',
+ group => 'can_configure_antispam',
+ };
+}
+
+#
+# installation
+#
+
+sub install_before_final_checks {
+ if (!Bugzilla::Group->new({ name => 'can_configure_antispam' })) {
+ Bugzilla::Group->create({
+ name => 'can_configure_antispam',
+ description => 'Can configure Anti-Spam measures',
+ isbuggroup => 0,
+ });
+ }
+}
sub db_schema_abstract_schema {
my ($self, $args) = @_;
@@ -83,4 +254,15 @@ sub db_schema_abstract_schema {
};
}
+#
+# utilities
+#
+
+sub _syslog {
+ my $message = shift;
+ openlog('apache', 'cons,pid', 'local4');
+ syslog('notice', encode_utf8($message));
+ closelog();
+}
+
__PACKAGE__->NAME;
diff --git a/extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl b/extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl
new file mode 100644
index 000000000..e55475d98
--- /dev/null
+++ b/extensions/AntiSpam/template/en/default/hook/admin/admin-end_links_right.html.tmpl
@@ -0,0 +1,16 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF user.in_group('can_configure_antispam') %]
+ <dt id="antispam" >AntiSpam</dt>
+ <dd>
+ <a href="page.cgi?id=edit_table.html&amp;table=antispam_domain_blocklist">Domain Blocklist</a><br>
+ <a href="page.cgi?id=edit_table.html&amp;table=antispam_comment_blocklist">Comment Blocklist</a><br>
+ <a href="page.cgi?id=edit_table.html&amp;table=antispam_ip_blocklist">IP Address Blocklist</a><br>
+ </dd>
+[% END %]
diff --git a/extensions/ProjectHoneyPot/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl b/extensions/AntiSpam/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
index e8e67eccb..e8e67eccb 100644
--- a/extensions/ProjectHoneyPot/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
+++ b/extensions/AntiSpam/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
diff --git a/extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl
new file mode 100644
index 000000000..44410ca2f
--- /dev/null
+++ b/extensions/AntiSpam/template/en/default/hook/global/user-error-errors.html.tmpl
@@ -0,0 +1,13 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF error == "antispam_comment_blocked" %]
+ [% title = "Comment Blocked" %]
+ Your comment contains one or more words deemed inappropriate for use by the
+ administrators of this site.
+[% END %]
diff --git a/extensions/ProjectHoneyPot/Config.pm b/extensions/EditTable/Config.pm
index 83d8d313e..d601951a4 100644
--- a/extensions/ProjectHoneyPot/Config.pm
+++ b/extensions/EditTable/Config.pm
@@ -5,11 +5,11 @@
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
-package Bugzilla::Extension::ProjectHoneyPot;
+package Bugzilla::Extension::EditTable;
use strict;
-use constant NAME => 'ProjectHoneyPot';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'EditTable';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/EditTable/Extension.pm b/extensions/EditTable/Extension.pm
new file mode 100644
index 000000000..a10a30e57
--- /dev/null
+++ b/extensions/EditTable/Extension.pm
@@ -0,0 +1,180 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+
+# this is a quick and dirty table editor, designed to allow admins to quickly
+# maintain tables.
+#
+# each table must be defined via the editable_tables hook
+#
+# this extension doesn't currently provide any ability to modify or validate
+# values. use with caution!
+
+package Bugzilla::Extension::EditTable;
+
+use strict;
+use warnings;
+
+use base qw(Bugzilla::Extension);
+
+use Bugzilla::Error;
+use Bugzilla::Hook;
+use Bugzilla::Util qw(trick_taint);
+use JSON;
+use Storable qw(dclone);
+
+our $VERSION = '1';
+
+# definitions for tables which we can edit with the quick-and-dirty editor
+#
+# $table_name => {
+# id_field => name of the "id" field
+# order_by => the field to sort rows by (optional, defaults to the id_field)
+# blurb => text which describes the table
+# group => group required to edit this table (optional, defaults to "admin")
+# }
+#
+# example:
+# 'antispam_domain_blocklist' => {
+# id_field => 'id',
+# order_by => 'domain',
+# blurb => 'List of fully qualified domain names to block at account creation time.',
+# group => 'can_configure_antispam',
+# },
+
+sub EDITABLE_TABLES {
+ my $tables = {};
+ Bugzilla::Hook::process("editable_tables", { tables => $tables });
+ return $tables;
+}
+
+sub page_before_template {
+ my ($self, $args) = @_;
+ my ($vars, $page) = @$args{qw(vars page_id)};
+ return unless $page eq 'edit_table.html';
+ my $input = Bugzilla->input_params;
+
+ # we only support editing a particular set of tables
+ my $table_name = $input->{table};
+ exists $self->EDITABLE_TABLES()->{$table_name}
+ || ThrowUserError('edittable_unsupported', { table => $table_name } );
+ my $table = $self->EDITABLE_TABLES()->{$table_name};
+ my $id_field = $table->{id_field};
+ my $order_by = $table->{order_by} || $id_field;
+ my $group = $table->{group} || 'admin';
+ trick_taint($table_name);
+
+ Bugzilla->user->in_group($group)
+ || ThrowUserError('auth_failure', { group => $group,
+ action => 'edit',
+ object => 'tables' });
+
+ # load columns
+ my $dbh = Bugzilla->dbh;
+ my @fields = sort
+ grep { $_ ne $id_field && $_ ne $order_by; }
+ $dbh->bz_table_columns($table_name);
+ if ($order_by ne $id_field) {
+ unshift @fields, $order_by;
+ }
+
+ # update table
+ my $data = $input->{table_data};
+ my $edits = [];
+ if ($data) {
+ $data = from_json($data)->{data};
+ $edits = dclone($data);
+ eval {
+ $dbh->bz_start_transaction;
+
+ foreach my $row (@$data) {
+ map { trick_taint($_) } @$row;
+ if ($row->[0] eq '-') {
+ # add
+ shift @$row;
+ next unless grep { $_ ne '' } @$row;
+ my $placeholders = join(',', split(//, '?' x scalar(@fields)));
+ $dbh->do(
+ "INSERT INTO $table_name(" . join(',', @fields) . ") " .
+ "VALUES ($placeholders)",
+ undef,
+ @$row
+ );
+ }
+ elsif ($row->[0] < 0) {
+ # delete
+ $dbh->do(
+ "DELETE FROM $table_name WHERE $id_field=?",
+ undef,
+ -$row->[0]
+ );
+ }
+ else {
+ # update
+ my $id = shift @$row;
+ $dbh->do(
+ "UPDATE $table_name " .
+ "SET " . join(',', map { "$_ = ?" } @fields) . " " .
+ "WHERE $id_field = ?",
+ undef,
+ @$row, $id
+ );
+ }
+ }
+
+ $dbh->bz_commit_transaction;
+ $vars->{updated} = 1;
+ $edits = [];
+ };
+ if ($@) {
+ my $error = $@;
+ $error =~ s/^DBD::[^:]+::db do failed: //;
+ $error =~ s/^(.+) \[for Statement ".+$/$1/s;
+ $vars->{error} = $error;
+ $dbh->bz_rollback_transaction;
+ }
+ }
+
+ # load data from table
+ unshift @fields, $id_field;
+ $data = $dbh->selectall_arrayref(
+ "SELECT " . join(',', @fields) . " FROM $table_name ORDER BY $order_by"
+ );
+
+ # we don't support nulls currently
+ foreach my $row (@$data) {
+ if (grep { !defined($_) } @$row) {
+ ThrowUserError('edittable_nulls', { table => $table_name } );
+ }
+ }
+
+ # apply failed edits
+ foreach my $edit (@$edits) {
+ if ($edit->[0] eq '-') {
+ push @$data, $edit;
+ }
+ else {
+ my $id = $edit->[0];
+ foreach my $row (@$data) {
+ if ($row->[0] == $id) {
+ @$row = @$edit;
+ last;
+ }
+ }
+ }
+ }
+
+ $vars->{table_name} = $table_name;
+ $vars->{blurb} = $table->{blurb};
+ $vars->{table_data} = to_json({
+ fields => \@fields,
+ id_field => $id_field,
+ data => $data,
+ });
+}
+
+__PACKAGE__->NAME;
diff --git a/extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl b/extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl
new file mode 100644
index 000000000..f86fb4c86
--- /dev/null
+++ b/extensions/EditTable/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl
@@ -0,0 +1,11 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF object == 'tables' %]
+ tables
+[% END %]
diff --git a/extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl
new file mode 100644
index 000000000..d87270b98
--- /dev/null
+++ b/extensions/EditTable/template/en/default/hook/global/user-error-errors.html.tmpl
@@ -0,0 +1,17 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF error == "edittable_unsupported" %]
+ [% title = "Unsupported Table" %]
+ You cannot edit the table '[% table FILTER html %]'.
+
+[% ELSIF error == "edittable_nulls" %]
+ [% title = "Table Contains NULLs" %]
+ EditTable cannot edit the table '[% table FILTER html %]' as it contains NULL
+ values.
+[% END %]
diff --git a/extensions/EditTable/template/en/default/pages/edit_table.html.tmpl b/extensions/EditTable/template/en/default/pages/edit_table.html.tmpl
new file mode 100644
index 000000000..d81291640
--- /dev/null
+++ b/extensions/EditTable/template/en/default/pages/edit_table.html.tmpl
@@ -0,0 +1,43 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+[% PROCESS global/header.html.tmpl
+ title = "Edit Table"
+ javascript_urls = [ 'extensions/EditTable/web/js/edit_table.js' ]
+ style_urls = [ "extensions/EditTable/web/styles/edit_table.css" ]
+%]
+
+<h2>[% table_name FILTER html %]</h2>
+
+[% IF updated %]
+ <p id="message">Table [% table_name FILTER html %] updated.</p>
+[% ELSIF error %]
+ <p class="throw_error">[% error FILTER html FILTER html_line_break %]</p>
+[% END %]
+
+<p>[% blurb FILTER html FILTER html_line_break %]</p>
+
+<div id="edit_table"></div>
+<br>
+<form method="post" action="page.cgi" enctype="multipart/form-data"
+ onsubmit="editTable.to_json('table_data')">
+<input type="hidden" name="id" value="edit_table.html">
+<input type="hidden" name="table" value="[% table_name FILTER html %]">
+<input type="hidden" name="table_data" id="table_data">
+<input type="submit" value="Commit Changes" id="commit_btn" class="bz_default_hidden">
+</form>
+
+<script>
+ var table_data = [% table_data FILTER none %];
+ var editTable = new EditTable('edit_table', table_data);
+ editTable.render();
+</script>
+
+[% PROCESS global/footer.html.tmpl %]
diff --git a/extensions/EditTable/web/js/edit_table.js b/extensions/EditTable/web/js/edit_table.js
new file mode 100644
index 000000000..ae239759b
--- /dev/null
+++ b/extensions/EditTable/web/js/edit_table.js
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+
+function EditTable(parent_el, table_data) {
+ this.parent_el = YAHOO.util.Dom.get(parent_el);
+ this.table_data = table_data;
+ this.field_count = table_data.fields.length;
+ if (!JSON) JSON = YAHOO.lang.JSON;
+
+ this.render = function() {
+ // create table
+ this.parent_el.innerHTML = '';
+ var table = document.createElement('table');
+
+ // header
+ var tr = document.createElement('tr');
+ for (var i = 0; i < this.field_count; i++) {
+ var th = document.createElement('th');
+ th.appendChild(document.createTextNode(this.table_data.fields[i]));
+ tr.appendChild(th);
+ }
+ var td = document.createElement('td');
+ td.innerHTML = '&nbsp;&nbsp;';
+ tr.appendChild(td);
+ table.appendChild(tr);
+
+ // rows
+ for (var i = 0; i < table_data.data.length; i++) {
+ // skip deleted rows
+ if (this.table_data.data[i][0] < 0)
+ continue;
+ var tr = document.createElement('tr');
+ for (var j = 0; j < this.field_count; j++) {
+ var td = document.createElement('td');
+ td.appendChild(document.createTextNode(this.table_data.data[i][j]));
+ tr.appendChild(td);
+
+ if (this.table_data.fields[j] != this.table_data.id_field) {
+ td.className = 'editable';
+ td.contentEditable = true;
+ YAHOO.util.Event.addListener(td, 'keydown', this._edit_keydown, this);
+ YAHOO.util.Event.addListener(td, 'blur', this._save, this);
+ d = td;
+ }
+ }
+ var td = document.createElement('td');
+ var a = document.createElement('a');
+ a.href = '#';
+ a.innerHTML = 'x';
+ YAHOO.util.Event.addListener(a, 'click', this._remove_row, this);
+ td.appendChild(a);
+ td.className = 'action';
+ tr.appendChild(td);
+ table.appendChild(tr);
+ }
+
+ this.parent_el.appendChild(table);
+
+ var add_btn = document.createElement('button');
+ add_btn.innerHTML = 'Add';
+ YAHOO.util.Event.addListener(add_btn, 'click', this._add_row, this);
+ this.parent_el.appendChild(add_btn);
+ },
+
+ this.to_json = function(target) {
+ YAHOO.util.Dom.get(target).value = JSON.stringify(this.table_data);
+ },
+
+ this._add_row = function(event, obj) {
+ var row = [];
+ for (var i = 0; i < obj.field_count; i++) {
+ row.push(obj.table_data.fields[i] == obj.table_data.id_field ? '-' : '');
+ }
+ obj.table_data.data.push(row);
+ obj.render();
+ YAHOO.util.Dom.removeClass('commit_btn', 'bz_default_hidden');
+ event.preventDefault();
+ },
+
+ this._remove_row = function(event, obj) {
+ var row = event.target.parentElement.parentElement.rowIndex - 1;
+ if (obj.table_data.data[row][0] == '-') {
+ // removing a newly added row
+ obj.table_data.data.splice(row, 1);
+ }
+ else {
+ // to remove a db row we set its id to negative
+ // it'll be skipped by render, and the update script knows which id to delete
+ obj.table_data.data[row][0] = -obj.table_data.data[row][0];
+ }
+ obj.render();
+ YAHOO.util.Dom.removeClass('commit_btn', 'bz_default_hidden');
+ event.preventDefault();
+ },
+
+ this._save = function(event, obj) {
+ var row = event.target.parentElement.rowIndex - 1;
+ var col = event.target.cellIndex;
+ var value = event.target.textContent;
+ if (obj.table_data.data[row][col] != event.target.textContent) {
+ obj.table_data.data[row][col] = event.target.textContent;
+ YAHOO.util.Dom.removeClass('commit_btn', 'bz_default_hidden');
+ }
+ },
+
+ this._revert = function(event, obj) {
+ var row = event.target.parentElement.rowIndex - 1;
+ var col = event.target.cellIndex;
+ event.target.replaceChild(
+ document.createTextNode(obj.table_data.data[row][col]),
+ event.target.firstChild
+ );
+ },
+
+ this._edit_keydown = function(event, obj) {
+ if (event.keyCode == 13) {
+ event.preventDefault();
+ obj._save(event, obj);
+ document.activeElement.blur(event.target);
+ }
+ else if (event.keyCode == 27) {
+ event.preventDefault();
+ obj._revert(event, obj);
+ }
+ }
+};
diff --git a/extensions/EditTable/web/styles/edit_table.css b/extensions/EditTable/web/styles/edit_table.css
new file mode 100644
index 000000000..0b1c72db6
--- /dev/null
+++ b/extensions/EditTable/web/styles/edit_table.css
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+
+#edit_table table {
+ border-spacing: 0;
+ border-collapse: collapse;
+ margin-bottom: 1em;
+}
+
+#edit_table td, #edit_table th {
+ padding: 5px;
+}
+
+#edit_table th {
+ background: #ccc;
+ text-align: left;
+}
+
+#edit_table .editable {
+ background: #fff;
+}
+
+#edit_table tr:hover {
+ background: #eee;
+}
+
+#edit_table .action {
+ display: none;
+}
+
+#edit_table tr:hover .action {
+ display: block;
+}
+
diff --git a/extensions/ProjectHoneyPot/Extension.pm b/extensions/ProjectHoneyPot/Extension.pm
deleted file mode 100644
index 856fe7f1e..000000000
--- a/extensions/ProjectHoneyPot/Extension.pm
+++ /dev/null
@@ -1,63 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-# This Source Code Form is "Incompatible With Secondary Licenses", as
-# defined by the Mozilla Public License, v. 2.0.
-
-package Bugzilla::Extension::ProjectHoneyPot;
-
-use strict;
-use warnings;
-
-use base qw(Bugzilla::Extension);
-
-use Encode;
-use Bugzilla::Error;
-use Bugzilla::Util qw(remote_ip);
-use Socket;
-use Sys::Syslog qw(:DEFAULT setlogsock);
-
-our $VERSION = '1';
-
-sub user_verify_login {
- my ($self, $args) = @_;
- return unless my $api_key = Bugzilla->params->{honeypot_api_key};
- my $ip = remote_ip();
- return unless $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
- my $lookup = "$api_key.$4.$3.$2.$1.dnsbl.httpbl.org";
- return unless my $packed = gethostbyname($lookup);
- my $honeypot = inet_ntoa($packed);
- return unless $honeypot =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
- my ($status, $days, $threat, $type) = ($1, $2, $3, $4);
-
- return if $status != 127
- || $threat < Bugzilla->params->{honeypot_threat_threshold};
-
- _syslog(sprintf("[audit] blocked <%s> from creating %s, honeypot %s",
- $ip, $args->{login}, $honeypot));
- ThrowUserError('account_creation_restricted');
-}
-
-sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{auth}->{params} }, {
- name => 'honeypot_api_key',
- type => 't',
- default => '',
- };
- push @{ $args->{panels}->{auth}->{params} }, {
- name => 'honeypot_threat_threshold',
- type => 't',
- default => '32',
- };
-}
-
-sub _syslog {
- my $message = shift;
- openlog('apache', 'cons,pid', 'local4');
- syslog('notice', encode_utf8($message));
- closelog();
-}
-
-__PACKAGE__->NAME;