diff options
-rwxr-xr-x | Bugzilla/Auth/Verify/RADIUS.pm | 64 | ||||
-rw-r--r-- | Bugzilla/Config/Auth.pm | 4 | ||||
-rw-r--r-- | Bugzilla/Config/Common.pm | 34 | ||||
-rwxr-xr-x | Bugzilla/Config/RADIUS.pm | 60 | ||||
-rw-r--r-- | Bugzilla/Install/Requirements.pm | 6 | ||||
-rw-r--r-- | docs/xml/installation.xml | 87 | ||||
-rwxr-xr-x | js/params.js | 61 | ||||
-rw-r--r-- | skins/standard/params.css | 6 | ||||
-rw-r--r-- | template/en/default/admin/params/auth.html.tmpl | 22 | ||||
-rw-r--r-- | template/en/default/admin/params/common.html.tmpl | 54 | ||||
-rw-r--r-- | template/en/default/admin/params/editparams.html.tmpl | 1 | ||||
-rwxr-xr-x | template/en/default/admin/params/radius.html.tmpl | 55 | ||||
-rw-r--r-- | template/en/default/filterexceptions.pl | 4 | ||||
-rw-r--r-- | template/en/default/global/code-error.html.tmpl | 3 |
14 files changed, 437 insertions, 24 deletions
diff --git a/Bugzilla/Auth/Verify/RADIUS.pm b/Bugzilla/Auth/Verify/RADIUS.pm new file mode 100755 index 000000000..da36c3bd1 --- /dev/null +++ b/Bugzilla/Auth/Verify/RADIUS.pm @@ -0,0 +1,64 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Marc Schumann. +# Portions created by Marc Schumann are Copyright (c) 2007 Marc Schumann. +# All rights reserved. +# +# Contributor(s): Marc Schumann <wurblzap@gmail.com> + +package Bugzilla::Auth::Verify::RADIUS; +use strict; +use base qw(Bugzilla::Auth::Verify); + +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; + +use Authen::Radius; + +use constant admin_can_create_account => 0; +use constant user_can_create_account => 0; + +sub check_credentials { + my ($self, $params) = @_; + my $dbh = Bugzilla->dbh; + my $address_suffix = Bugzilla->params->{'RADIUS_email_suffix'}; + my $username = $params->{username}; + + # If we're using RADIUS_email_suffix, we may need to cut it off from + # the login name. + if ($address_suffix) { + $username =~ s/\Q$address_suffix\E$//i; + } + + # Create RADIUS object. + my $radius = + new Authen::Radius(Host => Bugzilla->params->{'RADIUS_server'}, + Secret => Bugzilla->params->{'RADIUS_secret'}) + || return { failure => AUTH_ERROR, error => 'radius_preparation_error', + details => {errstr => Authen::Radius::strerror() } }; + + # Check the password. + $radius->check_pwd($username, $params->{password}, + Bugzilla->params->{'RADIUS_NAS_IP'} || undef) + || return { failure => AUTH_LOGINFAILED }; + + # Build the user account's e-mail address. + $params->{bz_username} = $username . $address_suffix; + + return $params; +} + +1; diff --git a/Bugzilla/Config/Auth.pm b/Bugzilla/Config/Auth.pm index 65ebc1b79..cbd94617a 100644 --- a/Bugzilla/Config/Auth.pm +++ b/Bugzilla/Config/Auth.pm @@ -76,8 +76,8 @@ sub get_param_list { { name => 'user_verify_class', - type => 's', - choices => [ 'DB', 'LDAP', 'DB,LDAP', 'LDAP,DB' ], + type => 'o', + choices => [ 'DB', 'RADIUS', 'LDAP' ], default => 'DB', checker => \&check_user_verify_class }, diff --git a/Bugzilla/Config/Common.pm b/Bugzilla/Config/Common.pm index 188ef0c90..14406019d 100644 --- a/Bugzilla/Config/Common.pm +++ b/Bugzilla/Config/Common.pm @@ -27,6 +27,7 @@ # Joseph Heenan <joseph@heenan.me.uk> # Erik Stambaugh <erik@dasbistro.com> # Frédéric Buclin <LpSolit@gmail.com> +# Marc Schumann <wurblzap@gmail.com> # package Bugzilla::Config::Common; @@ -64,8 +65,8 @@ sub check_multi { return ""; } - elsif ($param->{'type'} eq "m") { - foreach my $chkParam (@$value) { + elsif ($param->{'type'} eq 'm' || $param->{'type'} eq 'o') { + foreach my $chkParam (split(',', $value)) { unless (scalar(grep {$_ eq $chkParam} (@{$param->{'choices'}}))) { return "Invalid choice '$chkParam' for multi-select list param '$param->{'name'}'"; } @@ -268,18 +269,27 @@ sub check_user_verify_class { # So don't do that. my ($list, $entry) = @_; + $list || return 'You need to specify at least one authentication mechanism'; for my $class (split /,\s*/, $list) { my $res = check_multi($class, $entry); return $res if $res; if ($class eq 'DB') { # No params - } elsif ($class eq 'LDAP') { + } + elsif ($class eq 'RADIUS') { + eval "require Authen::Radius"; + return "Error requiring Authen::Radius: '$@'" if $@; + return "RADIUS servername (RADIUS_server) is missing" unless Bugzilla->params->{"RADIUS_server"}; + return "RADIUS_secret is empty" unless Bugzilla->params->{"RADIUS_secret"}; + } + elsif ($class eq 'LDAP') { eval "require Net::LDAP"; return "Error requiring Net::LDAP: '$@'" if $@; - return "LDAP servername is missing" unless Bugzilla->params->{"LDAPserver"}; + return "LDAP servername (LDAPserver) is missing" unless Bugzilla->params->{"LDAPserver"}; return "LDAPBaseDN is empty" unless Bugzilla->params->{"LDAPBaseDN"}; - } else { - return "Unknown user_verify_class '$class' in check_user_verify_class"; + } + else { + return "Unknown user_verify_class '$class' in check_user_verify_class"; } } return ""; @@ -363,9 +373,8 @@ sub check_timezone { # b -- A boolean value (either 1 or 0) # m -- A list of values, with many selectable (shows up as a select box) # To specify the list of values, make the 'choices' key be an array -# reference of the valid choices. The 'default' key should be an array -# reference for the list of selected values (which must appear in the -# first anonymous array), i.e.: +# reference of the valid choices. The 'default' key should be a string +# with a list of selected values (as a comma-separated list), i.e.: # { # name => 'multiselect', # desc => 'A list of options, choose many', @@ -381,6 +390,11 @@ sub check_timezone { # &check_multi should always be used as the param verification function # for list (single and multiple) parameter types. # +# o -- A list of values, orderable, and with many selectable (shows up as a +# JavaScript-enhanced select box if JavaScript is enabled, and a text +# entry field if not) +# Set up in the same way as type m. +# # s -- A list of values, with one selectable (shows up as a select box) # To specify the list of values, make the 'choices' key be an array # reference of the valid choices. The 'default' key should be one of @@ -422,7 +436,7 @@ All parameter checking functions are called with two parameters: =item C<check_multi> -Checks that a multi-valued parameter (ie type C<s> or type C<m>) satisfies +Checks that a multi-valued parameter (ie types C<s>, C<o> or C<m>) satisfies its contraints. =item C<check_numeric> diff --git a/Bugzilla/Config/RADIUS.pm b/Bugzilla/Config/RADIUS.pm new file mode 100755 index 000000000..6701d6f08 --- /dev/null +++ b/Bugzilla/Config/RADIUS.pm @@ -0,0 +1,60 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Marc Schumann. +# Portions created by Marc Schumann are Copyright (c) 2007 Marc Schumann. +# All rights reserved. +# +# Contributor(s): Marc Schumann <wurblzap@gmail.com> +# + +package Bugzilla::Config::RADIUS; + +use strict; + +use Bugzilla::Config::Common; + +$Bugzilla::Config::RADIUS::sortkey = "09"; + +sub get_param_list { + my $class = shift; + my @param_list = ( + { + name => 'RADIUS_server', + type => 't', + default => '' + }, + + { + name => 'RADIUS_secret', + type => 't', + default => '' + }, + + { + name => 'RADIUS_NAS_IP', + type => 't', + default => '' + }, + + { + name => 'RADIUS_email_suffix', + type => 't', + default => '' + }, + ); + return @param_list; +} + +1; diff --git a/Bugzilla/Install/Requirements.pm b/Bugzilla/Install/Requirements.pm index 7cc51a5e6..321f90f8d 100644 --- a/Bugzilla/Install/Requirements.pm +++ b/Bugzilla/Install/Requirements.pm @@ -170,6 +170,12 @@ sub OPTIONAL_MODULES { feature => 'LDAP Authentication' }, { + package => 'RadiusPerl', + module => 'Authen::Radius', + version => 0, + feature => 'RADIUS Authentication' + }, + { package => 'SOAP-Lite', module => 'SOAP::Lite', version => 0, diff --git a/docs/xml/installation.xml b/docs/xml/installation.xml index 0f859bbf0..337c1b431 100644 --- a/docs/xml/installation.xml +++ b/docs/xml/installation.xml @@ -1,5 +1,5 @@ <!-- <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"> --> -<!-- $Id: installation.xml,v 1.139 2007/07/24 18:22:02 timeless%mozdev.org Exp $ --> +<!-- $Id: installation.xml,v 1.140 2007/08/02 22:38:43 wurblzap%gmail.com Exp $ --> <chapter id="installing-bugzilla"> <title>Installing Bugzilla</title> @@ -420,6 +420,13 @@ <listitem> <para> + Authen::Radius + (&min-authen-radius-ver;) for RADIUS Authentication + </para> + </listitem> + + <listitem> + <para> <link linkend="install-modules-soap-lite">SOAP::Lite</link> (&min-soap-lite-ver;) for the web service interface </para> @@ -1506,6 +1513,72 @@ c:\perl\bin\perl.exe -xc:\bugzilla -wT "%s" %s </section> + <section id="bzradius"> + <title>RADIUS Authentication</title> + + <para>RADIUS authentication is a module for Bugzilla's plugin + authentication architecture. + Most caveats that apply to LDAP authentication apply to RADIUS + authentication as well. + </para> + + <para>Parameters required to use RADIUS Authentication:</para> + + <variablelist> + <varlistentry id="param-user_verify_class"> + <term>user_verify_class</term> + <listitem> + <para>If you want to list <quote>RADIUS</quote> here, + make sure to have set up the other parameters listed below. + Unless you have other (working) authentication methods listed as + well, you may otherwise not be able to log back in to Bugzilla once + you log out. + If this happens to you, you will need to manually edit + <filename>data/params</filename> and set user_verify_class to + <quote>DB</quote>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="param-RADIUS_server"> + <term>RADIUS_server</term> + <listitem> + <para>This parameter should be set to the name (and optionally the + port) of your RADIUS server. + </para> + </listitem> + </varlistentry> + + <varlistentry id="param-RADIUS_secret"> + <term>RADIUS_secret</term> + <listitem> + <para>This parameter should be set to the RADIUS server's secret. + </para> + </listitem> + </varlistentry> + + <varlistentry id="param-RADIUS_email_suffix"> + <term>RADIUS_email_suffix</term> + <listitem> + <para>Bugzilla needs an e-mail address for each user account. + Therefore, it needs to determine the e-mail address corresponding + to a RADIUS user. + Bugzilla offers only a simple way to do this: it can concatenate + a suffix to the RADIUS user name to convert it into an e-mail + address. + You can specify this suffix in the RADIUS_email_suffix parameter. + </para> + <para>If this simple solution does not work for you, you'll + probably need to modify + <filename>Bugzilla/Auth/Verify/RADIUS.pm</filename> to match your + requirements. + </para> + </listitem> + </varlistentry> + </variablelist> + + </section> + <section id="bzldap"> <title>LDAP Authentication</title> @@ -1553,12 +1626,12 @@ c:\perl\bin\perl.exe -xc:\bugzilla -wT "%s" %s <varlistentry id="param-user_verify_class"> <term>user_verify_class</term> <listitem> - <para>This parameter should be set to <quote>LDAP</quote> - <emphasis>only</emphasis> if you will be using an LDAP directory - for authentication. If you set this param to <quote>LDAP</quote> but - fail to set up the other parameters listed below you will not be - able to log back in to Bugzilla one you log out. If this happens - to you, you will need to manually edit + <para>If you want to list <quote>LDAP</quote> here, + make sure to have set up the other parameters listed below. + Unless you have other (working) authentication methods listed as + well, you may otherwise not be able to log back in to Bugzilla once + you log out. + If this happens to you, you will need to manually edit <filename>data/params</filename> and set user_verify_class to <quote>DB</quote>. </para> diff --git a/js/params.js b/js/params.js new file mode 100755 index 000000000..453740799 --- /dev/null +++ b/js/params.js @@ -0,0 +1,61 @@ +/* The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Bugzilla Bug Tracking System. + * + * The Initial Developer of the Original Code is Marc Schumann. + * Portions created by Marc Schumann are Copyright (c) 2007 Marc Schumann. + * All rights reserved. + * + * Contributor(s): Marc Schumann <wurblzap@gmail.com> + */ + +function sortedList_moveItem(paramName, direction, separator) { + var select = document.getElementById('select_' + paramName); + var inputField = document.getElementById('input_' + paramName); + var currentIndex = select.selectedIndex; + var newIndex = currentIndex + direction; + var optionCurrentIndex; + var optionNewIndex; + + /* Return if no selection */ + if (currentIndex < 0) return; + /* Return if trying to move upward out of list */ + if (newIndex < 0) return; + /* Return if trying to move downward out of list */ + if (newIndex >= select.length) return; + + /* Move selection */ + optionNewIndex = select.options[newIndex]; + optionCurrentIndex = select.options[currentIndex]; + /* Because some browsers don't accept the same option object twice in a + * selection list, we need to put a blank option here first */ + select.options[newIndex] = new Option(); + select.options[currentIndex] = optionNewIndex; + select.options[newIndex] = optionCurrentIndex; + select.selectedIndex = newIndex; + populateInputField(select, inputField, separator); +} + +function populateInputField(select, inputField, separator) { + var i; + var stringRepresentation = ''; + + for (i = 0; i < select.length; i++) { + if (select.options[i].value == separator) { + break; + } + if (stringRepresentation != '') { + stringRepresentation += ','; + } + stringRepresentation += select.options[i].value; + } + inputField.value = stringRepresentation; +} diff --git a/skins/standard/params.css b/skins/standard/params.css index 8d34d0e05..4eec75261 100644 --- a/skins/standard/params.css +++ b/skins/standard/params.css @@ -48,6 +48,12 @@ dd { margin-bottom: 1.5em; } +.sortlist_separator { + font-weight: bold; + font-size: 80%; + background-color: #dddddd; +} + .contribute { border: 1px dotted black; padding: .5em; diff --git a/template/en/default/admin/params/auth.html.tmpl b/template/en/default/admin/params/auth.html.tmpl index 040190699..2e52426bf 100644 --- a/template/en/default/admin/params/auth.html.tmpl +++ b/template/en/default/admin/params/auth.html.tmpl @@ -18,6 +18,7 @@ # # Contributor(s): Dave Miller <justdave@bugzilla.org> # Frédéric Buclin <LpSolit@gmail.com> + # Marc Schumann <wurblzap@gmail.com> #%] [% title = "User Authentication" @@ -67,12 +68,23 @@ ${terms.Bugzilla}'s built-in authentication. This is the most common choice. </dd> + <dt>RADIUS</dt> + <dd> + RADIUS authentication using a RADIUS server. + This method is experimental; please see the + $terms.Bugzilla documentation for more information. + Using this method requires + <a href=\"?section=radius\">additional + parameters</a> to be set. + </dd> <dt>LDAP</dt> <dd> - LDAP authentication using an LDAP server. This method is - experimental; please see the $terms.Bugzilla documentation for more - information. Using this method requires additional parameters - to be set above. + LDAP authentication using an LDAP server. + This method is experimental; please see the + $terms.Bugzilla documentation for more information. + Using this method requires + <a href=\"?section=ldap\">additional + parameters</a> to be set. </dd> </dl>", @@ -121,4 +133,4 @@ "to be created. If this parameter is left blank, no users " _ "will be permitted to create their own accounts and all accounts " _ "will have to be created by an administrator." } -%]
\ No newline at end of file +%] diff --git a/template/en/default/admin/params/common.html.tmpl b/template/en/default/admin/params/common.html.tmpl index 34cd1d39c..4e2b8f821 100644 --- a/template/en/default/admin/params/common.html.tmpl +++ b/template/en/default/admin/params/common.html.tmpl @@ -17,11 +17,14 @@ # Rights Reserved. # # Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + # Marc Schumann <wurblzap@gmail.com> #%] [%# INTERFACE: # panel: hash representing the current panel. #%] +[% sortlist_separator = '---' %] + <dl> [% FOREACH param = panel.param_list %] <dt><a name="[% param.name FILTER html %]">[% param.name FILTER html %]</a></dt> @@ -53,6 +56,57 @@ </option> [% END %] </select> + [% ELSIF param.type == "o" %] + <script type="text/javascript"><!-- + document.write("<span style=\"display: none\">"); + // --> + </script> + <input id="input_[% param.name FILTER html %]" size="80" + name="[% param.name FILTER html %]" + value="[% Param(param.name) FILTER html %]"><br> + <script type="text/javascript"><!-- + document.write("<\/span>"); + // --> + </script> + [% boxSize = 7 %] + [% boxSize = 3 + param.choices.size IF param.choices.size < 7 %] + [% plist = Param(param.name).split(',') %] + + <script type="text/javascript"><!-- + document.write( + '<table>' + + ' <tr>' + + ' <td rowspan="2">' + + ' <select id="select_[% param.name FILTER html %]"' + + ' size="[% boxSize FILTER html %]"' + + ' name="select_[% param.name FILTER html %]">' + + [% FOREACH item = plist %] + ' <option value="[% item FILTER html %]">[% item FILTER html %]<\/option>' + + [% END %] + ' <option class="sortlist_separator"' + + ' disabled="disabled"' + + ' value="[% sortlist_separator %]">active↑ ↓inactive<\/option>' + + [% FOREACH item = param.choices %] + [% IF lsearch(plist, item) == -1 %] + ' <option value="[% item FILTER html %]">[% item FILTER html %]<\/option>' + + [% END %] + [% END %] + ' <\/select>' + + ' <\/td>' + + ' <td style="vertical-align: bottom">' + + ' <button type="button"' + + ' onClick="sortedList_moveItem(\'[% param.name FILTER html %]\', -1, \'[% sortlist_separator %]\');">↑<\/button>' + + ' <\/td>' + + ' <\/tr>' + + ' <tr>' + + ' <td style="vertical-align: top">' + + ' <button type="button"' + + ' onClick="sortedList_moveItem(\'[% param.name FILTER html %]\', +1, \'[% sortlist_separator %]\');">↓<\/button>' + + ' <\/td>' + + ' <\/tr>' + + '<\/table>'); + // --> + </script> [% ELSIF param.type == "s" %] <select name="[% param.name FILTER html %]"> [% FOREACH item = param.choices %] diff --git a/template/en/default/admin/params/editparams.html.tmpl b/template/en/default/admin/params/editparams.html.tmpl index 2a9b785a6..588abd5c5 100644 --- a/template/en/default/admin/params/editparams.html.tmpl +++ b/template/en/default/admin/params/editparams.html.tmpl @@ -57,6 +57,7 @@ title = title message = message style_urls = ['skins/standard/params.css'] + javascript_urls = ['js/params.js'] %] <table border="0" width="100%"> diff --git a/template/en/default/admin/params/radius.html.tmpl b/template/en/default/admin/params/radius.html.tmpl new file mode 100755 index 000000000..4fc5a8d87 --- /dev/null +++ b/template/en/default/admin/params/radius.html.tmpl @@ -0,0 +1,55 @@ +[%# 1.0@bugzilla.org %] +[%# The contents of this file are subject to the Mozilla Public + # License Version 1.1 (the "License"); you may not use this file + # except in compliance with the License. You may obtain a copy of + # the License at http://www.mozilla.org/MPL/ + # + # Software distributed under the License is distributed on an "AS + # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + # implied. See the License for the specific language governing + # rights and limitations under the License. + # + # The Original Code is the Bugzilla Bug Tracking System. + # + # The Initial Developer of the Original Code is Marc Schumann. + # Portions created by Marc Schumann are Copyright (c) 2007 Marc Schumann. + # All rights reserved. + # + # Contributor(s): Marc Schumann <wurblzap@gmail.com> + #%] +[% + title = "RADIUS" + desc = "Configure this first before choosing RADIUS as an authentication method" +%] + +[% param_descs = { + RADIUS_server => "The name (and optionally port) of your RADIUS server " _ + "(e.g. <code>radius.company.com</code>, or " _ + "<code>radius.company.com:portnum</code>).<br>" _ + "Required only if " _ + "<a href=\"?section=auth#user_verify_class\">the " _ + "<code>user_verify_class</code> parameter</a> contains " _ + "<code>RADIUS</code>.", + + RADIUS_secret => "Your RADIUS server's secret.<br>" _ + "Required only if " _ + "<a href=\"?section=auth#user_verify_class\">the " _ + "<code>user_verify_class</code> parameter</a> contains " _ + "<code>RADIUS</code>.", + + RADIUS_NAS_IP => "The NAS-IP-Address attribute to be used when exchanging " _ + "data with your RADIUS server. " _ + "If unspecified, <code>127.0.0.1</code> will be used.<br>" _ + "Useful only if " _ + "<a href=\"?section=auth#user_verify_class\">the " _ + "<code>user_verify_class</code> parameter</a> " _ + "contains <code>RADIUS</code>.", + + RADIUS_email_suffix => "Suffix to append to a RADIUS user name to form an " _ + "e-mail address.<br>" _ + "Useful only if " _ + "<a href=\"?section=auth#user_verify_class\">the " _ + "<code>user_verify_class</code> parameter</a> " _ + "contains <code>RADIUS</code>.", + } +%] diff --git a/template/en/default/filterexceptions.pl b/template/en/default/filterexceptions.pl index 7d1c369f7..73d42224a 100644 --- a/template/en/default/filterexceptions.pl +++ b/template/en/default/filterexceptions.pl @@ -463,6 +463,10 @@ 'link_uri' ], +'admin/params/common.html.tmpl' => [ + 'sortlist_separator', +], + 'admin/products/groupcontrol/confirm-edit.html.tmpl' => [ 'group.count', ], diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index a7b65b0f1..51f52b4fe 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -337,6 +337,9 @@ outside the package. This function may only be called from a subclass of <code>[% superclass FILTER html %]</code>. + [% ELSIF error == "radius_preparation_error" %] + An error occurred while preparing for a RADIUS authentication request: + <code>[% errstr FILTER html %]</code>. [% ELSIF error == "unknown_comparison_type" %] Specified comparison type is not supported. |