SOLUCIONAD"Backend de Customer:Customer inválido por default

Post Reply
ioviedo
Znuny newbie
Posts: 63
Joined: 22 Jan 2013, 19:26
Znuny Version: 3.1.12
Real Name: Iván Oviedo
Company: ESPOl

SOLUCIONAD"Backend de Customer:Customer inválido por default

Post by ioviedo »

Saludos estimados compañeros de otrs, por favor necesito su ayuda. Realice un backend de los customers, pero al ingresar a la interfaz de administración vi que todos los customer estaban en estado inválido. Como podría dejar el estado válido por default, a continuación les dejo mi conf.:

# This is an example configuration for an LDAP auth. backend.
# (take care that Net::LDAP is installed!)
$Self->{AuthModule} = 'Kernel::System::Auth::LDAP';
$Self->{'AuthModule::LDAP::Host'} = '192.168.0.17:389';
$Self->{'AuthModule::LDAP::BaseDN'} = 'dc=dominio,dc=org,dc=ec';
$Self->{'AuthModule::LDAP::UID'} = 'sAMAccountName';
$Self->{'AuthModule::LDAP::SearchUserDN'} = 'cn=usuario_buscador,ou=Users,dc=dominio,dc=org,dc=ec';
$Self->{'AuthModule::LDAP::SearchUserPw'} = 'passworusuario_buscador;

# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
$Self->{'AuthModule::LDAP::Params'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};

# --------------------------------------------------- #
# authentication sync settings #
# (enable agent data sync. after succsessful #
# authentication) #
# --------------------------------------------------- #
# This is an example configuration for an LDAP auth sync. backend.
# (take care that Net::LDAP is installed!)
$Self->{AuthSyncModule} = 'Kernel::System::Auth::Sync::LDAP';
$Self->{'AuthSyncModule::LDAP::Host'} = '192.168.0.17:389';
$Self->{'AuthSyncModule::LDAP::BaseDN'} = 'dc=dominio,dc=org,dc=ec';
$Self->{'AuthSyncModule::LDAP::UID'} = 'sAMAccountName';
$Self->{'AuthSyncModule::LDAP::SearchUserDN'} = 'cn=usuario_buscador,ou=Users,dc=dominio,dc=org,dc=ec';
$Self->{'AuthSyncModule::LDAP::SearchUserPw'} = 'passworusuario_buscador';

# AuthSyncModule::LDAP::UserSyncMap
# (map if agent should create/synced from LDAP to DB after successful login)
$Self->{'AuthSyncModule::LDAP::UserSyncMap'} = {
# DB -> LDAP
UserFirstname => 'givenName',
UserLastname => 'sn',
UserEmail => 'mail',
};

# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
$Self->{'AuthSyncModule::LDAP::Params'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};

# Die if backend can't work, e. g. can't connect to server.
$Self->{'AuthSyncModule::LDAP::Die'} = 1;


#----------------------------------------------------------------------#
# This is an example configuration for an LDAP auth. backend.
# (take care that Net::LDAP is installed!)
$Self->{'Customer::AuthModule'} = 'Kernel::System::CustomerAuth::LDAP';
$Self->{'Customer::AuthModule::LDAP::Host'} = '192.168.0.17:389';
$Self->{'Customer::AuthModule::LDAP::BaseDN'} = 'dc=dominio,dc=org,dc=ec';
$Self->{'Customer::AuthModule::LDAP::UID'} = 'sAMAccountName';
$Self->{'Customer::AuthModule::LDAP::SearchUserDN'} = 'cn=usuario_buscador,ou=Users,dc=dominio,dc=org,dc=ec';
$Self->{'Customer::AuthModule::LDAP::SearchUserPw'} = 'paswordusuario_buscador';

# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
$Self->{'Customer::AuthModule::LDAP::Params'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};


# --------------------------------------------------- #
# Start of config options!!! #
# CustomerUser stuff #
# --------------------------------------------------- #
# CustomerUser
# (customer user ldap backend and settings)
$Self->{CustomerUser2} = {
Name => 'LDAP Backend',
Module => 'Kernel::System::CustomerUser::LDAP',
Params => {
# ldap host
Host => '192.168.0.17:389',
# ldap base dn
BaseDN => 'dc=dominio,dc=org,dc=ec',
# search scope (one|sub)
SSCOPE => 'sub',
UserDN => 'cn=usuario_buscador,ou=Users,dc=dominio,dc=org,dc=ec',
UserPw => 'paswordusuario_buscador',

# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
Params => {
port => 389,
timeout => 120,
async => 0,
version => 3,
},
},
# customer unique id
CustomerKey => 'sAMAccountName',
# customer #
CustomerID => 'mail',
CustomerUserListFields => ['cn', 'mail'],
CustomerUserSearchFields => ['mail'],
CustomerUserSearchPrefix => '',
CustomerUserSearchSuffix => '*',
CustomerUserSearchListLimit => 20,
CustomerUserPostMasterSearchFields => ['mail'],
CustomerUserNameFields => ['givenname', 'sn'],

# cache time to live in sec. - cache any ldap queries
CacheTTL => 0,
Map => [
# note: Login, Email and CustomerID needed!
# var, frontend, storage, shown (1=always,2=lite), required, storage-type, http-link, readonly
[ 'UserTitle', 'Title', 'title', 1, 0, 'var', '', 0 ],
[ 'UserFirstname', 'Firstname', 'givenname', 1, 1, 'var', '', 0 ],
[ 'UserLastname', 'Lastname', 'sn', 1, 1, 'var', '', 0 ],
[ 'UserLogin', 'Username', 'sAMAccountName', 1, 1, 'var', '', 0 ],
[ 'UserPassword', 'Password', 'pw', 0, 0, 'var', '', 0 ],
[ 'UserEmail', 'Email', 'mail', 1, 1, 'var', '', 0 ],
[ 'UserCustomerID', 'CustomerID', 'mail', 0, 1, 'var', '', 0 ],
[ 'UserPhone', 'Phone', 'telephonenumber', 1, 0, 'var', '', 0 ],
[ 'UserAddress', 'Address', 'postaladdress', 1, 0, 'var', '', 0 ],
[ 'UserComment', 'Comment', 'description', 1, 0, 'var', '', 0 ],
[ 'ValidID', 'Valid', 'valid_id', 0, 1, 'int','', 0 ],
],
};




Gracias de antemano, saludos desde Ecuador.
Last edited by ioviedo on 01 Mar 2013, 03:12, edited 1 time in total.
carlosgallego
Znuny expert
Posts: 241
Joined: 17 Nov 2011, 18:17
Znuny Version: 2.4-3.0-3.1-3.2- 3.3
Real Name: Carlos Gallego
Location: Medellin, Colombia

Re: Backend de Customer: Customer inválido por default

Post by carlosgallego »

Saludos compañero,

Intenta comentando la linea

[ 'ValidID', 'Valid', 'valid_id', 0, 1, 'int','', 0 ],

Con esto debe ser suficiente para usarlo.

Saludos

Carlos.
OTRS 2.4.7 > 3.3 Ubuntu Server - Centos - RedHat, MySQL - PostgresSQL OracleDB
ioviedo
Znuny newbie
Posts: 63
Joined: 22 Jan 2013, 19:26
Znuny Version: 3.1.12
Real Name: Iván Oviedo
Company: ESPOl

Re: Backend de Customer: Customer inválido por default

Post by ioviedo »

Hola estimado Carlos Gallego,

Me funcionó modificando de la siguiente manera:

[ 'ValidID', 'Valid', 'badPwdCount', 0, 1, 'int','', 0 ],

Los customer ya me aparecen por default activados.


Ahora estoy teniendo un problema con la autenticación por ejemplo si dejo el archivo Config.pm TAL COMO ESTABA DESPUÉS DE LA INSTALACIÓN los customer que creo en otrs no me autentica. Me aparece el siguiente error en /var/log/httpd/error, cuando me autentico en la pàgina de customer:

[Thu Feb 28 09:41:02 2013] -e: Can't locate package Kernel::Language::es_backup for @Kernel::Language::ISA at /opt/otrs//Kernel/Language.pm line 180.
[Thu Feb 28 09:41:02 2013] -e: Can't locate package Kernel::Language::es_backup for @Kernel::Language::ISA at /opt/otrs//Kernel/Language.pm line 180.
ERROR: OTRS-CGI-10 Perl: 5.10.1 OS: linux Time: Thu Feb 28 09:41:02 2013

Message: Search failed! Bad filter

Traceback (2424):
Module: Kernel::System::CustomerAuth::LDAP::Auth (v1.38.2.1) Line: 221
Module: Kernel::System::CustomerAuth::Auth (v1.36) Line: 151
Module: Kernel::System::Web::InterfaceCustomer::Run (v1.63) Line: 206
Module: ModPerl::ROOT::ModPerl::Registry::opt_otrs_bin_cgi_2dbin_customer_2epl::handler (unknown version) Line: 46
Module: (eval) (v1.44) Line: 204
Module: ModPerl::RegistryCooker::run (v1.44) Line: 204
Module: ModPerl::RegistryCooker::default_handler (v1.44) Line: 170
Module: ModPerl::Registry::handler (v1.99) Line: 31

[Thu Feb 28 09:41:02 2013] -e: Can't locate package Kernel::Language::es_backup for @Kernel::Language::ISA at /opt/otrs/bin/cgi-bin/customer.pl line 46.
[Thu Feb 28 09:41:02 2013] -e: Can't locate package Kernel::Language::es_backup for @Kernel::Language::ISA at /opt/otrs/bin/cgi-bin/customer.pl line 46.

Y en la dirección: /var/log/messages sale:
Feb 28 09:09:07 srv01hd OTRS-CGI-10[2429]: [Error][Kernel::System::CustomerAuth::LDAP::Auth][Line:221]: Search failed! Bad filter
Feb 28 09:09:13 srv01hd OTRS-CGI-10[2422]: [Error][Kernel::System::CustomerAuth::LDAP::Auth][Line:221]: Search failed! Bad filter
Feb 28 09:19:35 srv01hd OTRS-CGI-10[2423]: [Error][Kernel::System::CustomerAuth::LDAP::Auth][Line:221]: Search failed! Bad filter
Feb 28 09:41:02 srv01hd OTRS-CGI-10[2424]: [Error][Kernel::System::CustomerAuth::LDAP::Auth][Line:221]: Search failed! Bad filter

Cualquier ayuda quedo agradecido.
carlosgallego
Znuny expert
Posts: 241
Joined: 17 Nov 2011, 18:17
Znuny Version: 2.4-3.0-3.1-3.2- 3.3
Real Name: Carlos Gallego
Location: Medellin, Colombia

Re: Backend de Customer: Customer inválido por default

Post by carlosgallego »

Hola

Lo que veo inicialmente seria.

En la definicion del host para la autenticacion usas el puerto. y lo pones de nuevo en los parametros.
$Self->{'Customer::AuthModule::LDAP::Host'} = '192.168.0.17:389';

$Self->{'AuthModule::LDAP::Params'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};
Yo cambiaria esta informacion:
$Self->{'Customer::AuthModule::LDAP::Host'} = '192.168.0.17';

En cuanto al error del Language, no te recomiendo tener ningun archivo adicional a los temas de idioma en la carpeta Language. alli solo deben estar los archivos de la herramienta. por el momento retira el es_backup que ahi tienes.

En la info de la data de los clientes que traes puedes agregar este valor.

AlwaysFilter => ''

De no funcionarte con esto deja los siguientes campos por defecto y con ello podrias validar uno por uno luego.

CustomerUserListFields => ['cn', 'mail'],
CustomerUserSearchFields => ['uid', 'cn', 'mail'],
CustomerUserSearchPrefix => '',
CustomerUserSearchSuffix => '*',
CustomerUserSearchListLimit => 250,
CustomerUserPostMasterSearchFields => ['mail'],
CustomerUserNameFields => ['givenname', 'sn'],

Tambien puedes tener en cuenta la info de crythias en el foro en ingles, te adjunto el link pues es una excelente documentacion.viewtopic.php?f=60&t=16543

Saludos
OTRS 2.4.7 > 3.3 Ubuntu Server - Centos - RedHat, MySQL - PostgresSQL OracleDB
ioviedo
Znuny newbie
Posts: 63
Joined: 22 Jan 2013, 19:26
Znuny Version: 3.1.12
Real Name: Iván Oviedo
Company: ESPOl

SOLUCIONADO"Backend de Customer: Customer inválido por defau

Post by ioviedo »

Saludos carlosgallego,
Compañero muchas gracias por tus indicaciones me han ayudado a culminar al 100% la integración de LDAP Agent y Customer a OTRS. Dejame detallarte la solución para que sea de utilidad para otros compañeros que pasen por el mismo problema:


1) El problema de "error del Language" se solucionó cambiandolo de ubicación a otra carpeta: el archivo "es_backup" que yo lo habia creado con las configuraciones originales del archivo "es.pm" para poder modificarlo con tranquilidad, teniendo un respaldo por si acaso.
2) El problema de la autenticación lo solucioné adicionándole el número "1" a las configuraciones correspondientes al Customer, mas abajo se podrá observar. Ojo esto es importante porque sino les sucede mi caso, que no reconocia la autenticación. Esto sucede ya que OTRS por defecto tiene su base de datos, entonces al agregar un "backend de customer" que para mi entender es algo parecido a una base de datos ADICIONAL con la información del direcctorio LDAP, razón por la cual es NECESARIO adicionarle este número para que se diferencien las bases de datos y para dejar en claro al OTRS que las configuraciones de autenticación son para el backend de customer y NO PARA LA BASE DE DATOS DEL OTRS.

Con esta configuración se tiene la ventaja de que los Agentes y Customer pueden ser tanto del directorio LDAP o crearse en la base de datos de OTRS.

Sin más ni menos les presento mi configuración, cualquier duda me la comunican con gusto los ayudaré así como yó recibe ayuda de ustedes. Particularmente del compañero carlosgallego.

Saludos desde Ecuador.


----------------------------------------------------------------------------------------------------------------------------------------------------------------------
# --
# Kernel/Config.pm - Config file for OTRS kernel
# Copyright (C) 2001-2011 xxx, http://otrs.org/
# --
# $Id: Config.pm.dist,v 1.25 2011/09/16 10:58:28 mg Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
# Note:
#
# -->> OTRS does have a lot of config settings. For more settings
# (Notifications, Ticket::ViewAccelerator, Ticket::NumberGenerator,
# LDAP, PostMaster, Session, Preferences, ...) see
# Kernel/Config/Defaults.pm and copy your wanted lines into "this"
# config file. This file will not be changed on update!
#
# --

package Kernel::Config;

use utf8;

sub Load {
my $Self = shift;
# ---------------------------------------------------- #
# ---------------------------------------------------- #
# #
# Start of your own config options!!! #
# #
# ---------------------------------------------------- #
# ---------------------------------------------------- #


# ---------------------------------------------------- #
# database settings #
# ---------------------------------------------------- #
# DatabaseHost
# (The database host.)
$Self->{'DatabaseHost'} = 'localhost';
# Database
# (The database name.)
$Self->{'Database'} = 'otrs';
# DatabaseUser
# (The database user.)
$Self->{'DatabaseUser'} = 'otrs';
# DatabasePw
# (The password of database user. You also can use bin/otrs.CryptPassword.pl
# for crypted passwords.)
$Self->{'DatabasePw'} = 'hot';
# DatabaseDSN
# (The database DSN for MySQL ==> more: "man DBD::mysql")
$Self->{DatabaseDSN} = "DBI:mysql:database=$Self->{Database};host=$Self->{DatabaseHost};";

# (The database DSN for PostgreSQL ==> more: "man DBD::Pg")
# if you want to use a local socket connection
# $Self->{DatabaseDSN} = "DBI:Pg:dbname=$Self->{Database};";
# if you want to use a tcpip connection
# $Self->{DatabaseDSN} = "DBI:Pg:dbname=$Self->{Database};host=$Self->{DatabaseHost};";
# if you have PostgresSQL 8.1 or earlier, activate the legacy driver with this line:
# $Self->{DatabasePostgresqlBefore82} = 1;






# ---------------------------------------------------- #
# fs root directory
# ---------------------------------------------------- #
$Self->{Home} = '/opt/otrs';

# ---------------------------------------------------- #
# insert your own config settings "here" #
# config settings taken from Kernel/Config/Defaults.pm #
# ---------------------------------------------------- #
# $Self->{SessionUseCookie} = 0;
# $Self->{CheckMXRecord} = 0;


# This is an example configuration for an LDAP auth. backend.
# (take care that Net::LDAP is installed!)
$Self->{AuthModule} = 'Kernel::System::Auth::LDAP';
$Self->{'AuthModule::LDAP::Host'} = '152.120.54.30:389';
$Self->{'AuthModule::LDAP::BaseDN'} = 'dc=dominio,dc=com';
$Self->{'AuthModule::LDAP::UID'} = 'sAMAccountName';
$Self->{'AuthModule::LDAP::SearchUserDN'} = 'cn=usuario_buscador,ou=Users,dc=dominio,dc=com';
$Self->{'AuthModule::LDAP::SearchUserPw'} = 'claveusuario_buscador';
# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
$Self->{'AuthModule::LDAP::Params'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};

# --------------------------------------------------- #
# authentication sync settings #
# (enable agent data sync. after succsessful #
# authentication) #
# --------------------------------------------------- #
# This is an example configuration for an LDAP auth sync. backend.
# (take care that Net::LDAP is installed!)
$Self->{AuthSyncModule} = 'Kernel::System::Auth::Sync::LDAP';
$Self->{'AuthSyncModule::LDAP::Host'} = '152.120.54.30:389';
$Self->{'AuthSyncModule::LDAP::BaseDN'} = 'dc=dominio,dc=com';
$Self->{'AuthSyncModule::LDAP::UID'} = 'sAMAccountName';
$Self->{'AuthSyncModule::LDAP::SearchUserDN'} = 'cn=usuario_buscador,ou=Users,dc=dominio,dc=com';
$Self->{'AuthSyncModule::LDAP::SearchUserPw'} = 'claveusuario_buscador';
# AuthSyncModule::LDAP::UserSyncMap
# (map if agent should create/synced from LDAP to DB after successful login)
$Self->{'AuthSyncModule::LDAP::UserSyncMap'} = {
# DB -> LDAP
UserFirstname => 'givenName',
UserLastname => 'sn',
UserEmail => 'mail',
};

# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
$Self->{'AuthSyncModule::LDAP::Params'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};

# Die if backend can't work, e. g. can't connect to server.
$Self->{'AuthSyncModule::LDAP::Die'} = 1;


#----------------------------------------------------------------------#
# This is an example configuration for an LDAP auth. backend.
# (take care that Net::LDAP is installed!)
$Self->{'Customer::AuthModule1'} = 'Kernel::System::CustomerAuth::LDAP';
$Self->{'Customer::AuthModule::LDAP::Host1'} = '152.120.54.30:389';
$Self->{'Customer::AuthModule::LDAP::BaseDN1'} = 'dc=dominio,dc=com';
$Self->{'Customer::AuthModule::LDAP::UID1'} = 'sAMAccountName';
$Self->{'Customer::AuthModule::LDAP::SearchUserDN1'} = 'cn=usuario_buscador,ou=Users,dc=dominio,dc=com';
$Self->{'Customer::AuthModule::LDAP::SearchUserPw1'} = 'claveusuario_buscador';
# Net::LDAP new params (if needed - for more info see perldoc Net::LDAP)
$Self->{'Customer::AuthModule::LDAP::Params1'} = {
port => 389,
timeout => 120,
async => 0,
version => 3,
};


# --------------------------------------------------- #
# Start of config options!!! #
# CustomerUser stuff #
# --------------------------------------------------- #



# CustomerUserLDAP
# (customer user ldap backend and settings)
$Self->{CustomerUser1} = {
Name => 'LDAP Backend',
Module => 'Kernel::System::CustomerUser::LDAP',
Params => {
# ldap host
Host => '152.120.54.30:389',
# ldap base dn
BaseDN => 'dc=dominio,dc=com',
# search scope (one|sub)
SSCOPE => 'sub',
UserDN => 'cn=usuario_buscador,ou=Users,dc=dominio,dc=com',
UserPw => 'claveusuario_buscador',
AlwaysFilter => '(mail=*)'

},
# customer unique id
CustomerKey => 'sAMAccountName',
# customer 'sAMAccountName', 'cn',
CustomerID => 'employeeid',
CustomerUserListFields => ['sAMAccountName','sn','mail'],
CustomerUserSearchFields => ['mail'],
CustomerUserSearchPrefix => '',
CustomerUserSearchSuffix => '*',
CustomerUserSearchListLimit => 100000,
CustomerUserPostMasterSearchFields => ['mail'],
CustomerUserNameFields => ['givenname', 'sn'],
Map => [
# note: Login, Email and CustomerID needed!
# var, frontend, storage, shown (1=always,2=lite), required, storage-type, http-link, readonly
[ 'UserTitle', 'Title', 'title', 1, 0, 'var', '', 0 ],
[ 'UserFirstname', 'Firstname', 'givenname', 1, 1, 'var', '', 0 ],
[ 'UserLastname', 'Lastname', 'sn', 1, 1, 'var', '', 0 ],
[ 'UserLogin', 'Username', 'sAMAccountName', 1, 1, 'var', '', 0 ],
[ 'UserPassword', 'Password', 'pw', 0, 0, 'var', '', 0 ],
[ 'UserEmail', 'Email', 'mail', 1, 1, 'var', '', 0 ],
[ 'UserCustomerID', 'CustomerID', 'employeeid', 0, 1, 'var', '', 0 ],
[ 'UserPhone', 'Phone', 'telephonenumber', 1, 0, 'var', '', 0 ],
[ 'UserAddress', 'Address', 'postaladdress', 1, 0, 'var', '', 0 ],
[ 'UserComment', 'Comment', 'description', 1, 0, 'var', '', 0 ],
# [ 'ValidID', 'Valid', '', 0, 1, 'int','', 0 ],
],

# default selections
Selections => {
UserTitle => {
'Mr.' => 'Mr.',
'Mrs.' => 'Mrs.',
},
},
};




# ---------------------------------------------------- #

# ---------------------------------------------------- #
# data inserted by installer #
# ---------------------------------------------------- #
$Self->{'LogModule::LogFile'} = '/tmp/log/otrs.log';

# ---------------------------------------------------- #
# ---------------------------------------------------- #
# #
# End of your own config options!!! #
# #
# ---------------------------------------------------- #
# ---------------------------------------------------- #
}

# ---------------------------------------------------- #
# needed system stuff (don't edit this) #
# ---------------------------------------------------- #
use strict;
use warnings;

use vars qw(@ISA $VERSION);
$VERSION = qw($Revision: 1.25 $)[1];

use Kernel::Config::Defaults;
push (@ISA, 'Kernel::Config::Defaults');

# -----------------------------------------------------#

1;
Last edited by ioviedo on 01 Mar 2013, 03:10, edited 1 time in total.
carlosgallego
Znuny expert
Posts: 241
Joined: 17 Nov 2011, 18:17
Znuny Version: 2.4-3.0-3.1-3.2- 3.3
Real Name: Carlos Gallego
Location: Medellin, Colombia

Re: Backend de Customer: Customer inválido por default

Post by carlosgallego »

Excelente compañero solo 2 cositas.

Recomendaciones con el archivo de idioma, no realices modificaciones sobre el archivo es.pm todo lo contrario a esto debes copiarlo con el nombre es_Custom.pm En este si puedes hacer las modificaciones que desees, ya que luego en actualizaciones o instalacion de paquetes te puede sobreescribir el es.pm asi como los paquetes se marcan como instalados con errores.

Ahhh no olvides editar la primera nota y cambiar el titulo del post a SOLUCIONADO.


Saludos.
OTRS 2.4.7 > 3.3 Ubuntu Server - Centos - RedHat, MySQL - PostgresSQL OracleDB
Post Reply