1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::System::User;
10
11use strict;
12use warnings;
13
14use Crypt::PasswdMD5 qw(unix_md5_crypt apache_md5_crypt);
15use Digest::SHA;
16
17our @ObjectDependencies = (
18    'Kernel::Config',
19    'Kernel::System::Cache',
20    'Kernel::System::CheckItem',
21    'Kernel::System::DB',
22    'Kernel::System::Encode',
23    'Kernel::System::Log',
24    'Kernel::System::Main',
25    'Kernel::System::SearchProfile',
26    'Kernel::System::DateTime',
27    'Kernel::System::Valid',
28);
29
30=head1 NAME
31
32Kernel::System::User - user lib
33
34=head1 DESCRIPTION
35
36All user functions. E. g. to add and updated user and other functions.
37
38=head1 PUBLIC INTERFACE
39
40=head2 new()
41
42Don't use the constructor directly, use the ObjectManager instead:
43
44    my $UserObject = $Kernel::OM->Get('Kernel::System::User');
45
46=cut
47
48sub new {
49    my ( $Type, %Param ) = @_;
50
51    # allocate new hash for object
52    my $Self = {};
53    bless( $Self, $Type );
54
55    # get config object
56    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
57
58    # get user table
59    $Self->{UserTable}       = $ConfigObject->Get('DatabaseUserTable')       || 'user';
60    $Self->{UserTableUserID} = $ConfigObject->Get('DatabaseUserTableUserID') || 'id';
61    $Self->{UserTableUserPW} = $ConfigObject->Get('DatabaseUserTableUserPW') || 'pw';
62    $Self->{UserTableUser}   = $ConfigObject->Get('DatabaseUserTableUser')   || 'login';
63
64    $Self->{CacheType} = 'User';
65    $Self->{CacheTTL}  = 60 * 60 * 24 * 20;
66
67    # set lower if database is case sensitive
68    $Self->{Lower} = '';
69    if ( $Kernel::OM->Get('Kernel::System::DB')->GetDatabaseFunction('CaseSensitive') ) {
70        $Self->{Lower} = 'LOWER';
71    }
72
73    return $Self;
74}
75
76=head2 GetUserData()
77
78get user data (UserLogin, UserFirstname, UserLastname, UserEmail, ...)
79
80    my %User = $UserObject->GetUserData(
81        UserID => 123,
82    );
83
84    or
85
86    my %User = $UserObject->GetUserData(
87        User          => 'franz',
88        Valid         => 1,       # not required -> 0|1 (default 0)
89                                  # returns only data if user is valid
90        NoOutOfOffice => 1,       # not required -> 0|1 (default 0)
91                                  # returns data without out of office infos
92    );
93
94=cut
95
96sub GetUserData {
97    my ( $Self, %Param ) = @_;
98
99    # check needed stuff
100    if ( !$Param{User} && !$Param{UserID} ) {
101        $Kernel::OM->Get('Kernel::System::Log')->Log(
102            Priority => 'error',
103            Message  => 'Need User or UserID!',
104        );
105        return;
106    }
107
108    # get config object
109    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
110
111    # get configuration for the full name order
112    my $FirstnameLastNameOrder = $ConfigObject->Get('FirstnameLastnameOrder') || 0;
113
114    # check if result is cached
115    if ( $Param{Valid} ) {
116        $Param{Valid} = 1;
117    }
118    else {
119        $Param{Valid} = 0;
120    }
121    if ( $Param{NoOutOfOffice} ) {
122        $Param{NoOutOfOffice} = 1;
123    }
124    else {
125        $Param{NoOutOfOffice} = 0;
126    }
127
128    my $CacheKey;
129    if ( $Param{User} ) {
130        $CacheKey = join '::', 'GetUserData', 'User',
131            $Param{User},
132            $Param{Valid},
133            $FirstnameLastNameOrder,
134            $Param{NoOutOfOffice};
135    }
136    else {
137        $CacheKey = join '::', 'GetUserData', 'UserID',
138            $Param{UserID},
139            $Param{Valid},
140            $FirstnameLastNameOrder,
141            $Param{NoOutOfOffice};
142    }
143
144    # check cache
145    my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
146        Type => $Self->{CacheType},
147        Key  => $CacheKey,
148    );
149    return %{$Cache} if $Cache;
150
151    # get initial data
152    my @Bind;
153    my $SQL = "SELECT $Self->{UserTableUserID}, $Self->{UserTableUser}, "
154        . " title, first_name, last_name, $Self->{UserTableUserPW}, valid_id, "
155        . " create_time, change_time FROM $Self->{UserTable} WHERE ";
156
157    if ( $Param{User} ) {
158        my $User = lc $Param{User};
159        $SQL .= " $Self->{Lower}($Self->{UserTableUser}) = ?";
160        push @Bind, \$User;
161    }
162    else {
163        $SQL .= " $Self->{UserTableUserID} = ?";
164        push @Bind, \$Param{UserID};
165    }
166
167    # get database object
168    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
169
170    return if !$DBObject->Prepare(
171        SQL   => $SQL,
172        Bind  => \@Bind,
173        Limit => 1,
174    );
175
176    my %Data;
177    while ( my @Row = $DBObject->FetchrowArray() ) {
178        $Data{UserID}        = $Row[0];
179        $Data{UserLogin}     = $Row[1];
180        $Data{UserTitle}     = $Row[2];
181        $Data{UserFirstname} = $Row[3];
182        $Data{UserLastname}  = $Row[4];
183        $Data{UserPw}        = $Row[5];
184        $Data{ValidID}       = $Row[6];
185        $Data{CreateTime}    = $Row[7];
186        $Data{ChangeTime}    = $Row[8];
187    }
188
189    # check data
190    if ( !$Data{UserID} ) {
191        if ( $Param{User} ) {
192            $Kernel::OM->Get('Kernel::System::Log')->Log(
193                Priority => 'notice',
194                Message  => "No UserData for user: '$Param{User}'.",
195            );
196            return;
197        }
198        else {
199            $Kernel::OM->Get('Kernel::System::Log')->Log(
200                Priority => 'notice',
201                Message  => "No UserData for user id: '$Param{UserID}'.",
202            );
203            return;
204        }
205    }
206
207    # Store CacheTTL locally so that we can reduce it for users that are out of office.
208    my $CacheTTL = $Self->{CacheTTL};
209
210    # check valid, return if there is locked for valid users
211    if ( $Param{Valid} ) {
212
213        my $Hit = 0;
214
215        for ( $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet() ) {
216            if ( $_ eq $Data{ValidID} ) {
217                $Hit = 1;
218            }
219        }
220
221        if ( !$Hit ) {
222
223            # set cache
224            $Kernel::OM->Get('Kernel::System::Cache')->Set(
225                Type  => $Self->{CacheType},
226                TTL   => $CacheTTL,
227                Key   => $CacheKey,
228                Value => {},
229            );
230            return;
231        }
232    }
233
234    # generate the full name and save it in the hash
235    my $UserFullname = $Self->_UserFullname(
236        %Data,
237        NameOrder => $FirstnameLastNameOrder,
238    );
239
240    # save the generated fullname in the hash.
241    $Data{UserFullname} = $UserFullname;
242
243    # get preferences
244    my %Preferences = $Self->GetPreferences( UserID => $Data{UserID} );
245
246    my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
247
248    # add last login timestamp
249    if ( $Preferences{UserLastLogin} ) {
250
251        my $UserLastLoginTimeObj = $Kernel::OM->Create(
252            'Kernel::System::DateTime',
253            ObjectParams => {
254                Epoch => $Preferences{UserLastLogin}
255            }
256        );
257
258        $Preferences{UserLastLoginTimestamp} = $UserLastLoginTimeObj->ToString();
259    }
260
261    # check compat stuff
262    if ( !$Preferences{UserEmail} ) {
263        $Preferences{UserEmail} = $Data{UserLogin};
264    }
265
266    # out of office check
267    if ( !$Param{NoOutOfOffice} ) {
268        if ( $Preferences{OutOfOffice} ) {
269
270            my $CurrentTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
271            my $CreateDTObject    = sub {
272                my %Param = @_;
273
274                return $Kernel::OM->Create(
275                    'Kernel::System::DateTime',
276                    ObjectParams => {
277                        String => sprintf(
278                            '%d-%02d-%02d %s',
279                            $Param{Year},
280                            $Param{Month},
281                            $Param{Day},
282                            $Param{Time}
283                        ),
284                    },
285                );
286            };
287
288            my $TimeStartObj = $CreateDTObject->(
289                Year  => $Preferences{OutOfOfficeStartYear},
290                Month => $Preferences{OutOfOfficeStartMonth},
291                Day   => $Preferences{OutOfOfficeStartDay},
292                Time  => '00:00:00',
293            );
294
295            my $TimeEndObj = $CreateDTObject->(
296                Year  => $Preferences{OutOfOfficeEndYear},
297                Month => $Preferences{OutOfOfficeEndMonth},
298                Day   => $Preferences{OutOfOfficeEndDay},
299                Time  => '23:59:59',
300            );
301
302            if ( $TimeStartObj < $CurrentTimeObject && $TimeEndObj > $CurrentTimeObject ) {
303                my $OutOfOfficeMessageTemplate =
304                    $ConfigObject->Get('OutOfOfficeMessageTemplate') || '*** out of office until %s (%s d left) ***';
305                my $TillDate = sprintf(
306                    '%04d-%02d-%02d',
307                    $Preferences{OutOfOfficeEndYear},
308                    $Preferences{OutOfOfficeEndMonth},
309                    $Preferences{OutOfOfficeEndDay}
310                );
311                my $Till = int( ( $TimeEndObj->ToEpoch() - $CurrentTimeObject->ToEpoch() ) / 60 / 60 / 24 );
312                $Preferences{OutOfOfficeMessage} = sprintf( $OutOfOfficeMessageTemplate, $TillDate, $Till );
313                $Data{UserFullname} .= ' ' . $Preferences{OutOfOfficeMessage};
314            }
315
316            # Reduce CacheTTL to one hour for users that are out of office to make sure the cache expires timely
317            #   even if there is no update action.
318            $CacheTTL = 60 * 60 * 1;
319        }
320    }
321
322    # merge hash
323    %Data = ( %Data, %Preferences );
324
325    # add preferences defaults
326    my $Config = $ConfigObject->Get('PreferencesGroups');
327    if ( $Config && ref $Config eq 'HASH' ) {
328
329        KEY:
330        for my $Key ( sort keys %{$Config} ) {
331
332            next KEY if !defined $Config->{$Key}->{DataSelected};
333
334            # check if data is defined
335            next KEY if defined $Data{ $Config->{$Key}->{PrefKey} };
336
337            # set default data
338            $Data{ $Config->{$Key}->{PrefKey} } = $Config->{$Key}->{DataSelected};
339        }
340    }
341
342    # set cache
343    $Kernel::OM->Get('Kernel::System::Cache')->Set(
344        Type  => $Self->{CacheType},
345        TTL   => $CacheTTL,
346        Key   => $CacheKey,
347        Value => \%Data,
348    );
349
350    return %Data;
351}
352
353=head2 UserAdd()
354
355to add new users
356
357    my $UserID = $UserObject->UserAdd(
358        UserFirstname => 'Huber',
359        UserLastname  => 'Manfred',
360        UserLogin     => 'mhuber',
361        UserPw        => 'some-pass', # not required
362        UserEmail     => 'email@example.com',
363        UserMobile    => '1234567890', # not required
364        ValidID       => 1,
365        ChangeUserID  => 123,
366    );
367
368=cut
369
370sub UserAdd {
371    my ( $Self, %Param ) = @_;
372
373    # check needed stuff
374    for (qw(UserFirstname UserLastname UserLogin UserEmail ValidID ChangeUserID)) {
375        if ( !$Param{$_} ) {
376            $Kernel::OM->Get('Kernel::System::Log')->Log(
377                Priority => 'error',
378                Message  => "Need $_!",
379            );
380            return;
381        }
382    }
383
384    # check if a user with this login (username) already exits
385    if ( $Self->UserLoginExistsCheck( UserLogin => $Param{UserLogin} ) ) {
386        $Kernel::OM->Get('Kernel::System::Log')->Log(
387            Priority => 'error',
388            Message  => "A user with the username '$Param{UserLogin}' already exists.",
389        );
390        return;
391    }
392
393    # check email address
394    if (
395        $Param{UserEmail}
396        && !$Kernel::OM->Get('Kernel::System::CheckItem')->CheckEmail( Address => $Param{UserEmail} )
397        && grep { $_ eq $Param{ValidID} } $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet()
398        )
399    {
400        $Kernel::OM->Get('Kernel::System::Log')->Log(
401            Priority => 'error',
402            Message  => "Email address ($Param{UserEmail}) not valid ("
403                . $Kernel::OM->Get('Kernel::System::CheckItem')->CheckError() . ")!",
404        );
405        return;
406    }
407
408    # check password
409    if ( !$Param{UserPw} ) {
410        $Param{UserPw} = $Self->GenerateRandomPassword();
411    }
412
413    # get database object
414    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
415
416    # Don't store the user's password in plaintext initially. It will be stored in a
417    #   hashed version later with SetPassword().
418    my $RandomPassword = $Self->GenerateRandomPassword();
419
420    # sql
421    return if !$DBObject->Do(
422        SQL => "INSERT INTO $Self->{UserTable} "
423            . "(title, first_name, last_name, "
424            . " $Self->{UserTableUser}, $Self->{UserTableUserPW}, "
425            . " valid_id, create_time, create_by, change_time, change_by)"
426            . " VALUES "
427            . " (?, ?, ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)",
428        Bind => [
429            \$Param{UserTitle}, \$Param{UserFirstname}, \$Param{UserLastname},
430            \$Param{UserLogin}, \$RandomPassword, \$Param{ValidID},
431            \$Param{ChangeUserID}, \$Param{ChangeUserID},
432        ],
433    );
434
435    # get new user id
436    my $UserLogin = lc $Param{UserLogin};
437    return if !$DBObject->Prepare(
438        SQL => "SELECT $Self->{UserTableUserID} FROM $Self->{UserTable} "
439            . " WHERE $Self->{Lower}($Self->{UserTableUser}) = ?",
440        Bind  => [ \$UserLogin ],
441        Limit => 1,
442    );
443
444    # fetch the result
445    my $UserID;
446    while ( my @Row = $DBObject->FetchrowArray() ) {
447        $UserID = $Row[0];
448    }
449
450    # check if user exists
451    if ( !$UserID ) {
452        $Kernel::OM->Get('Kernel::System::Log')->Log(
453            Priority => 'notice',
454            Message  => "Unable to create User: '$Param{UserLogin}' ($Param{ChangeUserID})!",
455        );
456        return;
457    }
458
459    # log notice
460    $Kernel::OM->Get('Kernel::System::Log')->Log(
461        Priority => 'notice',
462        Message =>
463            "User: '$Param{UserLogin}' ID: '$UserID' created successfully ($Param{ChangeUserID})!",
464    );
465
466    # set password
467    $Self->SetPassword(
468        UserLogin => $Param{UserLogin},
469        PW        => $Param{UserPw}
470    );
471
472    my @UserPreferences = grep { $_ =~ m{^User}xsi } sort keys %Param;
473
474    USERPREFERENCE:
475    for my $UserPreference (@UserPreferences) {
476
477        # UserEmail is required.
478        next USERPREFERENCE if $UserPreference eq 'UserEmail' && !$Param{UserEmail};
479
480        # Set user preferences.
481        # Native user data will not be overwriten (handeled by SetPreferences()).
482        $Self->SetPreferences(
483            UserID => $UserID,
484            Key    => $UserPreference,
485            Value  => $Param{$UserPreference} || '',
486        );
487    }
488
489    # delete cache
490    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
491        Type => $Self->{CacheType},
492    );
493
494    return $UserID;
495}
496
497=head2 UserUpdate()
498
499to update users
500
501    $UserObject->UserUpdate(
502        UserID        => 4321,
503        UserFirstname => 'Huber',
504        UserLastname  => 'Manfred',
505        UserLogin     => 'mhuber',
506        UserPw        => 'some-pass', # not required
507        UserEmail     => 'email@example.com',
508        UserMobile    => '1234567890', # not required
509        ValidID       => 1,
510        ChangeUserID  => 123,
511    );
512
513=cut
514
515sub UserUpdate {
516    my ( $Self, %Param ) = @_;
517
518    # check needed stuff
519    for (qw(UserID UserFirstname UserLastname UserLogin ValidID ChangeUserID)) {
520        if ( !$Param{$_} ) {
521            $Kernel::OM->Get('Kernel::System::Log')->Log(
522                Priority => 'error',
523                Message  => "Need $_!",
524            );
525            return;
526        }
527    }
528
529    # store old user login for later use
530    my $OldUserLogin = $Self->UserLookup(
531        UserID => $Param{UserID},
532    );
533
534    # check if a user with this login (username) already exists
535    if (
536        $Self->UserLoginExistsCheck(
537            UserLogin => $Param{UserLogin},
538            UserID    => $Param{UserID}
539        )
540        )
541    {
542        $Kernel::OM->Get('Kernel::System::Log')->Log(
543            Priority => 'error',
544            Message  => "A user with the username '$Param{UserLogin}' already exists.",
545        );
546        return;
547    }
548
549    # check email address
550    if (
551        $Param{UserEmail}
552        && !$Kernel::OM->Get('Kernel::System::CheckItem')->CheckEmail( Address => $Param{UserEmail} )
553        && grep { $_ eq $Param{ValidID} } $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet()
554        )
555    {
556        $Kernel::OM->Get('Kernel::System::Log')->Log(
557            Priority => 'error',
558            Message  => "Email address ($Param{UserEmail}) not valid ("
559                . $Kernel::OM->Get('Kernel::System::CheckItem')->CheckError() . ")!",
560        );
561        return;
562    }
563
564    # update db
565    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
566        SQL => "UPDATE $Self->{UserTable} SET title = ?, first_name = ?, last_name = ?, "
567            . " $Self->{UserTableUser} = ?, valid_id = ?, "
568            . " change_time = current_timestamp, change_by = ? "
569            . " WHERE $Self->{UserTableUserID} = ?",
570        Bind => [
571            \$Param{UserTitle}, \$Param{UserFirstname}, \$Param{UserLastname},
572            \$Param{UserLogin}, \$Param{ValidID}, \$Param{ChangeUserID}, \$Param{UserID},
573        ],
574    );
575
576    # check pw
577    if ( $Param{UserPw} ) {
578        $Self->SetPassword(
579            UserLogin => $Param{UserLogin},
580            PW        => $Param{UserPw}
581        );
582    }
583
584    my @UserPreferences = grep { $_ =~ m{^User}xsi } sort keys %Param;
585
586    USERPREFERENCE:
587    for my $UserPreference (@UserPreferences) {
588
589        # UserEmail is required.
590        next USERPREFERENCE if $UserPreference eq 'UserEmail' && !$Param{UserEmail};
591
592        # Set user preferences.
593        # Native user data will not be overwriten (handeled by SetPreferences()).
594        $Self->SetPreferences(
595            UserID => $Param{UserID},
596            Key    => $UserPreference,
597            Value  => $Param{$UserPreference} || '',
598        );
599    }
600
601    # update search profiles if the UserLogin changed
602    if ( lc $OldUserLogin ne lc $Param{UserLogin} ) {
603        $Kernel::OM->Get('Kernel::System::SearchProfile')->SearchProfileUpdateUserLogin(
604            Base         => 'TicketSearch',
605            UserLogin    => $OldUserLogin,
606            NewUserLogin => $Param{UserLogin},
607        );
608    }
609
610    $Self->_UserCacheClear( UserID => $Param{UserID} );
611
612    # TODO Not needed to delete the cache if ValidID or Name was not changed
613
614    my $CacheObject            = $Kernel::OM->Get('Kernel::System::Cache');
615    my $SystemPermissionConfig = $Kernel::OM->Get('Kernel::Config')->Get('System::Permission') || [];
616
617    for my $Type ( @{$SystemPermissionConfig}, 'rw' ) {
618
619        $CacheObject->Delete(
620            Type => 'GroupPermissionUserGet',
621            Key  => 'PermissionUserGet::' . $Param{UserID} . '::' . $Type,
622        );
623    }
624
625    $CacheObject->CleanUp(
626        Type => 'GroupPermissionGroupGet',
627    );
628
629    return 1;
630}
631
632=head2 UserSearch()
633
634to search users
635
636    my %List = $UserObject->UserSearch(
637        Search => '*some*', # also 'hans+huber' possible
638        Valid  => 1, # not required
639    );
640
641    my %List = $UserObject->UserSearch(
642        UserLogin => '*some*',
643        Limit     => 50,
644        Valid     => 1, # not required
645    );
646
647    my %List = $UserObject->UserSearch(
648        PostMasterSearch => 'email@example.com',
649        Valid            => 1, # not required
650    );
651
652Returns hash of UserID, Login pairs:
653
654    my %List = (
655        1 => 'root@locahost',
656        4 => 'admin',
657        9 => 'joe',
658    );
659
660For PostMasterSearch, it returns hash of UserID, Email pairs:
661
662    my %List = (
663        4 => 'john@example.com',
664        9 => 'joe@example.com',
665    );
666
667=cut
668
669sub UserSearch {
670    my ( $Self, %Param ) = @_;
671
672    my %Users;
673    my $Valid = $Param{Valid} // 1;
674
675    # check needed stuff
676    if ( !$Param{Search} && !$Param{UserLogin} && !$Param{PostMasterSearch} ) {
677        $Kernel::OM->Get('Kernel::System::Log')->Log(
678            Priority => 'error',
679            Message  => 'Need Search, UserLogin or PostMasterSearch!',
680        );
681        return;
682    }
683
684    # get database object
685    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
686
687    # get like escape string needed for some databases (e.g. oracle)
688    my $LikeEscapeString = $DBObject->GetDatabaseFunction('LikeEscapeString');
689
690    # build SQL string
691    my $SQL = "SELECT $Self->{UserTableUserID}, login
692                   FROM $Self->{UserTable} WHERE ";
693    my @Bind;
694
695    if ( $Param{Search} ) {
696
697        my %QueryCondition = $DBObject->QueryCondition(
698            Key      => [qw(login first_name last_name)],
699            Value    => $Param{Search},
700            BindMode => 1,
701        );
702        $SQL .= $QueryCondition{SQL} . ' ';
703        push @Bind, @{ $QueryCondition{Values} };
704    }
705    elsif ( $Param{PostMasterSearch} ) {
706
707        my %UserID = $Self->SearchPreferences(
708            Key   => 'UserEmail',
709            Value => $Param{PostMasterSearch},
710        );
711
712        for ( sort keys %UserID ) {
713            my %User = $Self->GetUserData(
714                UserID => $_,
715                Valid  => $Param{Valid},
716            );
717            if (%User) {
718                return %UserID;
719            }
720        }
721
722        return;
723    }
724    elsif ( $Param{UserLogin} ) {
725
726        my $UserLogin = lc $Param{UserLogin};
727        $SQL .= " $Self->{Lower}($Self->{UserTableUser}) LIKE ? $LikeEscapeString";
728        $UserLogin =~ s/\*/%/g;
729        $UserLogin = $DBObject->Quote( $UserLogin, 'Like' );
730        push @Bind, \$UserLogin;
731    }
732
733    # add valid option
734    if ($Valid) {
735        $SQL .= "AND valid_id IN ("
736            . join( ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet() ) . ")";
737    }
738
739    # get data
740    return if !$DBObject->Prepare(
741        SQL   => $SQL,
742        Bind  => \@Bind,
743        Limit => $Self->{UserSearchListLimit} || $Param{Limit},
744    );
745
746    # fetch the result
747    while ( my @Row = $DBObject->FetchrowArray() ) {
748        $Users{ $Row[0] } = $Row[1];
749    }
750
751    return %Users;
752}
753
754=head2 SetPassword()
755
756to set users passwords
757
758    $UserObject->SetPassword(
759        UserLogin => 'some-login',
760        PW        => 'some-new-password'
761    );
762
763=cut
764
765sub SetPassword {
766    my ( $Self, %Param ) = @_;
767
768    # check needed stuff
769    if ( !$Param{UserLogin} ) {
770        $Kernel::OM->Get('Kernel::System::Log')->Log(
771            Priority => 'error',
772            Message  => 'Need UserLogin!'
773        );
774        return;
775    }
776
777    # get old user data
778    my %User = $Self->GetUserData( User => $Param{UserLogin} );
779    if ( !$User{UserLogin} ) {
780        $Kernel::OM->Get('Kernel::System::Log')->Log(
781            Priority => 'error',
782            Message  => 'No such User!',
783        );
784        return;
785    }
786
787    my $Pw        = $Param{PW} || '';
788    my $CryptedPw = '';
789
790    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
791
792    my $CryptType = $ConfigObject->Get('AuthModule::DB::CryptType') || 'sha2';
793
794    # crypt plain (no crypt at all)
795    if ( $CryptType eq 'plain' ) {
796        $CryptedPw = $Pw;
797    }
798
799    # crypt with UNIX crypt
800    elsif ( $CryptType eq 'crypt' ) {
801
802        # encode output, needed by crypt() only non utf8 signs
803        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
804        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Param{UserLogin} );
805
806        $CryptedPw = crypt( $Pw, $Param{UserLogin} );
807    }
808
809    # crypt with md5
810    elsif ( $CryptType eq 'md5' || !$CryptType ) {
811
812        # encode output, needed by unix_md5_crypt() only non utf8 signs
813        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
814        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Param{UserLogin} );
815
816        $CryptedPw = unix_md5_crypt( $Pw, $Param{UserLogin} );
817    }
818
819    # crypt with md5 (compatible with Apache's .htpasswd files)
820    elsif ( $CryptType eq 'apr1' ) {
821
822        # encode output, needed by unix_md5_crypt() only non utf8 signs
823        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
824        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Param{UserLogin} );
825
826        $CryptedPw = apache_md5_crypt( $Pw, $Param{UserLogin} );
827    }
828
829    # crypt with sha1
830    elsif ( $CryptType eq 'sha1' ) {
831
832        my $SHAObject = Digest::SHA->new('sha1');
833        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
834        $SHAObject->add($Pw);
835        $CryptedPw = $SHAObject->hexdigest();
836    }
837
838    # crypt with sha512
839    elsif ( $CryptType eq 'sha512' ) {
840
841        my $SHAObject = Digest::SHA->new('sha512');
842        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
843        $SHAObject->add($Pw);
844        $CryptedPw = $SHAObject->hexdigest();
845    }
846
847    # bcrypt
848    elsif ( $CryptType eq 'bcrypt' ) {
849
850        if ( !$Kernel::OM->Get('Kernel::System::Main')->Require('Crypt::Eksblowfish::Bcrypt') ) {
851            $Kernel::OM->Get('Kernel::System::Log')->Log(
852                Priority => 'error',
853                Message =>
854                    "User: '$User{UserLogin}' tried to store password with bcrypt but 'Crypt::Eksblowfish::Bcrypt' is not installed!",
855            );
856            return;
857        }
858
859        my $Cost = $ConfigObject->Get('AuthModule::DB::bcryptCost') // 12;
860
861        # Don't allow values smaller than 9 for security.
862        $Cost = 9 if $Cost < 9;
863
864        # Current Crypt::Eksblowfish::Bcrypt limit is 31.
865        $Cost = 31 if $Cost > 31;
866
867        my $Salt = $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString( Length => 16 );
868
869        # remove UTF8 flag, required by Crypt::Eksblowfish::Bcrypt
870        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
871
872        # calculate password hash
873        my $Octets = Crypt::Eksblowfish::Bcrypt::bcrypt_hash(
874            {
875                key_nul => 1,
876                cost    => $Cost,
877                salt    => $Salt,
878            },
879            $Pw
880        );
881
882        # We will store cost and salt in the password string so that it can be decoded
883        #   in future even if we use a higher cost by default.
884        $CryptedPw = "BCRYPT:$Cost:$Salt:" . Crypt::Eksblowfish::Bcrypt::en_base64($Octets);
885    }
886
887    # crypt with sha256 as fallback
888    else {
889
890        my $SHAObject = Digest::SHA->new('sha256');
891
892        # encode output, needed by sha256_hex() only non utf8 signs
893        $Kernel::OM->Get('Kernel::System::Encode')->EncodeOutput( \$Pw );
894
895        $SHAObject->add($Pw);
896        $CryptedPw = $SHAObject->hexdigest();
897    }
898
899    # update db
900    my $UserLogin = lc $Param{UserLogin};
901    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
902        SQL => "UPDATE $Self->{UserTable} SET $Self->{UserTableUserPW} = ? "
903            . " WHERE $Self->{Lower}($Self->{UserTableUser}) = ?",
904        Bind => [ \$CryptedPw, \$UserLogin ],
905    );
906
907    # log notice
908    $Kernel::OM->Get('Kernel::System::Log')->Log(
909        Priority => 'notice',
910        Message  => "User: '$Param{UserLogin}' changed password successfully!",
911    );
912
913    return 1;
914}
915
916=head2 UserLookup()
917
918user login or id lookup
919
920    my $UserLogin = $UserObject->UserLookup(
921        UserID => 1,
922        Silent => 1, # optional, don't generate log entry if user was not found
923    );
924
925    my $UserID = $UserObject->UserLookup(
926        UserLogin => 'some_user_login',
927        Silent    => 1, # optional, don't generate log entry if user was not found
928    );
929
930=cut
931
932sub UserLookup {
933    my ( $Self, %Param ) = @_;
934
935    # check needed stuff
936    if ( !$Param{UserLogin} && !$Param{UserID} ) {
937        $Kernel::OM->Get('Kernel::System::Log')->Log(
938            Priority => 'error',
939            Message  => 'Need UserLogin or UserID!'
940        );
941        return;
942    }
943
944    # get database object
945    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
946
947    if ( $Param{UserLogin} ) {
948
949        # check cache
950        my $CacheKey = 'UserLookup::ID::' . $Param{UserLogin};
951        my $Cache    = $Kernel::OM->Get('Kernel::System::Cache')->Get(
952            Type => $Self->{CacheType},
953            Key  => $CacheKey,
954        );
955        return $Cache if $Cache;
956
957        # build sql query
958        my $UserLogin = lc $Param{UserLogin};
959
960        return if !$DBObject->Prepare(
961            SQL => "SELECT $Self->{UserTableUserID} FROM $Self->{UserTable} "
962                . " WHERE $Self->{Lower}($Self->{UserTableUser}) = ?",
963            Bind  => [ \$UserLogin ],
964            Limit => 1,
965        );
966
967        # fetch the result
968        my $ID;
969        while ( my @Row = $DBObject->FetchrowArray() ) {
970            $ID = $Row[0];
971        }
972
973        if ( !$ID ) {
974            if ( !$Param{Silent} ) {
975                $Kernel::OM->Get('Kernel::System::Log')->Log(
976                    Priority => 'error',
977                    Message  => "No UserID found for '$Param{UserLogin}'!",
978                );
979            }
980            return;
981        }
982
983        # set cache
984        $Kernel::OM->Get('Kernel::System::Cache')->Set(
985            Type  => $Self->{CacheType},
986            TTL   => $Self->{CacheTTL},
987            Key   => $CacheKey,
988            Value => $ID,
989        );
990
991        return $ID;
992    }
993
994    else {
995
996        # check cache
997        my $CacheKey = 'UserLookup::Login::' . $Param{UserID};
998        my $Cache    = $Kernel::OM->Get('Kernel::System::Cache')->Get(
999            Type => $Self->{CacheType},
1000            Key  => $CacheKey,
1001        );
1002        return $Cache if $Cache;
1003
1004        # build sql query
1005        return if !$DBObject->Prepare(
1006            SQL => "SELECT $Self->{UserTableUser} FROM $Self->{UserTable} "
1007                . " WHERE $Self->{UserTableUserID} = ?",
1008            Bind  => [ \$Param{UserID} ],
1009            Limit => 1,
1010        );
1011
1012        # fetch the result
1013        my $Login;
1014        while ( my @Row = $DBObject->FetchrowArray() ) {
1015            $Login = $Row[0];
1016        }
1017
1018        if ( !$Login ) {
1019            if ( !$Param{Silent} ) {
1020                $Kernel::OM->Get('Kernel::System::Log')->Log(
1021                    Priority => 'error',
1022                    Message  => "No UserLogin found for '$Param{UserID}'!",
1023                );
1024            }
1025            return;
1026        }
1027
1028        # set cache
1029        $Kernel::OM->Get('Kernel::System::Cache')->Set(
1030            Type  => $Self->{CacheType},
1031            TTL   => $Self->{CacheTTL},
1032            Key   => $CacheKey,
1033            Value => $Login,
1034        );
1035
1036        return $Login;
1037    }
1038}
1039
1040=head2 UserName()
1041
1042get user name
1043
1044    my $Name = $UserObject->UserName(
1045        User => 'some-login',
1046    );
1047
1048    or
1049
1050    my $Name = $UserObject->UserName(
1051        UserID => 123,
1052    );
1053
1054=cut
1055
1056sub UserName {
1057    my ( $Self, %Param ) = @_;
1058
1059    my %User = $Self->GetUserData(%Param);
1060
1061    return if !%User;
1062    return $User{UserFullname};
1063}
1064
1065=head2 UserList()
1066
1067return a hash with all users
1068
1069    my %List = $UserObject->UserList(
1070        Type          => 'Short', # Short|Long, default Short
1071        Valid         => 1,       # default 1
1072        NoOutOfOffice => 1,       # (optional) default 0
1073    );
1074
1075=cut
1076
1077sub UserList {
1078    my ( $Self, %Param ) = @_;
1079
1080    my $Type = $Param{Type} || 'Short';
1081
1082    # set valid option
1083    my $Valid = $Param{Valid} // 1;
1084
1085    # get config object
1086    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
1087
1088    # get configuration for the full name order
1089    my $FirstnameLastNameOrder = $ConfigObject->Get('FirstnameLastnameOrder') || 0;
1090    my $NoOutOfOffice          = $Param{NoOutOfOffice}                        || 0;
1091
1092    # check cache
1093    my $CacheKey = join '::', 'UserList', $Type, $Valid, $FirstnameLastNameOrder, $NoOutOfOffice;
1094    my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
1095        Type => $Self->{CacheType},
1096        Key  => $CacheKey,
1097    );
1098    return %{$Cache} if $Cache;
1099
1100    my $SelectStr;
1101    if ( $Type eq 'Short' ) {
1102        $SelectStr = "$ConfigObject->{DatabaseUserTableUserID}, "
1103            . " $ConfigObject->{DatabaseUserTableUser}";
1104    }
1105    else {
1106        $SelectStr = "$ConfigObject->{DatabaseUserTableUserID}, "
1107            . " last_name, first_name, "
1108            . " $ConfigObject->{DatabaseUserTableUser}";
1109    }
1110
1111    my $SQL = "SELECT $SelectStr FROM $ConfigObject->{DatabaseUserTable}";
1112
1113    # sql query
1114    if ($Valid) {
1115        $SQL
1116            .= " WHERE valid_id IN ( ${\(join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet())} )";
1117    }
1118
1119    # get database object
1120    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
1121
1122    return if !$DBObject->Prepare( SQL => $SQL );
1123
1124    # fetch the result
1125    my %UsersRaw;
1126    my %Users;
1127    while ( my @Row = $DBObject->FetchrowArray() ) {
1128        $UsersRaw{ $Row[0] } = \@Row;
1129    }
1130
1131    if ( $Type eq 'Short' ) {
1132        for my $CurrentUserID ( sort keys %UsersRaw ) {
1133            $Users{$CurrentUserID} = $UsersRaw{$CurrentUserID}->[1];
1134        }
1135    }
1136    else {
1137        for my $CurrentUserID ( sort keys %UsersRaw ) {
1138            my @Data         = @{ $UsersRaw{$CurrentUserID} };
1139            my $UserFullname = $Self->_UserFullname(
1140                UserFirstname => $Data[2],
1141                UserLastname  => $Data[1],
1142                UserLogin     => $Data[3],
1143                NameOrder     => $FirstnameLastNameOrder,
1144            );
1145
1146            $Users{$CurrentUserID} = $UserFullname;
1147        }
1148    }
1149
1150    # check vacation option
1151    if ( !$NoOutOfOffice ) {
1152
1153        USERID:
1154        for my $UserID ( sort keys %Users ) {
1155            next USERID if !$UserID;
1156
1157            my %User = $Self->GetUserData(
1158                UserID => $UserID,
1159            );
1160            if ( $User{OutOfOfficeMessage} ) {
1161                $Users{$UserID} .= ' ' . $User{OutOfOfficeMessage};
1162            }
1163        }
1164    }
1165
1166    # set cache
1167    $Kernel::OM->Get('Kernel::System::Cache')->Set(
1168        Type  => $Self->{CacheType},
1169        TTL   => $Self->{CacheTTL},
1170        Key   => $CacheKey,
1171        Value => \%Users,
1172    );
1173
1174    return %Users;
1175}
1176
1177=head2 GenerateRandomPassword()
1178
1179generate a random password
1180
1181    my $Password = $UserObject->GenerateRandomPassword();
1182
1183    or
1184
1185    my $Password = $UserObject->GenerateRandomPassword(
1186        Size => 16,
1187    );
1188
1189=cut
1190
1191sub GenerateRandomPassword {
1192    my ( $Self, %Param ) = @_;
1193
1194    # generated passwords are eight characters long by default.
1195    my $Size = $Param{Size} || 8;
1196
1197    my $Password = $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString(
1198        Length => $Size,
1199    );
1200
1201    return $Password;
1202}
1203
1204=head2 SetPreferences()
1205
1206set user preferences
1207
1208    $UserObject->SetPreferences(
1209        Key    => 'UserComment',
1210        Value  => 'some comment',
1211        UserID => 123,
1212    );
1213
1214=cut
1215
1216sub SetPreferences {
1217    my ( $Self, %Param ) = @_;
1218
1219    # check needed stuff
1220    for (qw(Key UserID)) {
1221        if ( !$Param{$_} ) {
1222            $Kernel::OM->Get('Kernel::System::Log')->Log(
1223                Priority => 'error',
1224                Message  => "Need $_!"
1225            );
1226            return;
1227        }
1228    }
1229
1230    # Don't allow overwriting of native user data.
1231    my %Blacklisted = (
1232        UserID        => 1,
1233        UserLogin     => 1,
1234        UserPw        => 1,
1235        UserFirstname => 1,
1236        UserLastname  => 1,
1237        UserFullname  => 1,
1238        UserTitle     => 1,
1239        ChangeTime    => 1,
1240        CreateTime    => 1,
1241        ValidID       => 1,
1242    );
1243
1244    return 0 if $Blacklisted{ $Param{Key} };
1245
1246    # get current setting
1247    my %User = $Self->GetUserData(
1248        UserID        => $Param{UserID},
1249        NoOutOfOffice => 1,
1250    );
1251
1252    # no updated needed
1253    return 1
1254        if defined $User{ $Param{Key} }
1255        && defined $Param{Value}
1256        && $User{ $Param{Key} } eq $Param{Value};
1257
1258    $Self->_UserCacheClear( UserID => $Param{UserID} );
1259
1260    # get user preferences config
1261    my $GeneratorModule = $Kernel::OM->Get('Kernel::Config')->Get('User::PreferencesModule')
1262        || 'Kernel::System::User::Preferences::DB';
1263
1264    # get generator preferences module
1265    my $PreferencesObject = $Kernel::OM->Get($GeneratorModule);
1266
1267    # set preferences
1268    return $PreferencesObject->SetPreferences(%Param);
1269}
1270
1271sub _UserCacheClear {
1272    my ( $Self, %Param ) = @_;
1273
1274    if ( !$Param{UserID} ) {
1275        $Kernel::OM->Get('Kernel::System::Log')->Log(
1276            Priority => 'error',
1277            Message  => "Need UserID!"
1278        );
1279        return;
1280    }
1281
1282    my $Login = $Self->UserLookup( UserID => $Param{UserID} );
1283
1284    my @CacheKeys;
1285
1286    # Delete cache for all possible FirstnameLastNameOrder settings as this might be overridden by users.
1287    for my $FirstnameLastNameOrder ( 0 .. 8 ) {
1288        for my $ActiveLevel1 ( 0 .. 1 ) {
1289            for my $ActiveLevel2 ( 0 .. 1 ) {
1290                push @CacheKeys, (
1291                    "GetUserData::User::${Login}::${ActiveLevel1}::${FirstnameLastNameOrder}::${ActiveLevel2}",
1292                    "GetUserData::UserID::$Param{UserID}::${ActiveLevel1}::${FirstnameLastNameOrder}::${ActiveLevel2}",
1293                    "UserList::Short::${ActiveLevel1}::${FirstnameLastNameOrder}::${ActiveLevel2}",
1294                    "UserList::Long::${ActiveLevel1}::${FirstnameLastNameOrder}::${ActiveLevel2}",
1295                );
1296            }
1297        }
1298        push @CacheKeys, (
1299            'UserLookup::ID::' . $Login,
1300            'UserLookup::Login::' . $Param{UserID},
1301        );
1302    }
1303
1304    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
1305
1306    for my $CacheKey (@CacheKeys) {
1307        $CacheObject->Delete(
1308            Type => $Self->{CacheType},
1309            Key  => $CacheKey,
1310        );
1311    }
1312
1313    return 1;
1314}
1315
1316=head2 GetPreferences()
1317
1318get user preferences
1319
1320    my %Preferences = $UserObject->GetPreferences(
1321        UserID => 123,
1322    );
1323
1324=cut
1325
1326sub GetPreferences {
1327    my ( $Self, %Param ) = @_;
1328
1329    # get user preferences config
1330    my $GeneratorModule = $Kernel::OM->Get('Kernel::Config')->Get('User::PreferencesModule')
1331        || 'Kernel::System::User::Preferences::DB';
1332
1333    # get generator preferences module
1334    my $PreferencesObject = $Kernel::OM->Get($GeneratorModule);
1335
1336    return $PreferencesObject->GetPreferences(%Param);
1337}
1338
1339=head2 SearchPreferences()
1340
1341search in user preferences
1342
1343    my %UserList = $UserObject->SearchPreferences(
1344        Key   => 'UserEmail',
1345        Value => 'email@example.com',   # optional, limit to a certain value/pattern
1346    );
1347
1348=cut
1349
1350sub SearchPreferences {
1351    my ( $Self, %Param ) = @_;
1352
1353    # get user preferences config
1354    my $GeneratorModule = $Kernel::OM->Get('Kernel::Config')->Get('User::PreferencesModule')
1355        || 'Kernel::System::User::Preferences::DB';
1356
1357    # get generator preferences module
1358    my $PreferencesObject = $Kernel::OM->Get($GeneratorModule);
1359
1360    return $PreferencesObject->SearchPreferences(%Param);
1361}
1362
1363=head2 TokenGenerate()
1364
1365generate a random token
1366
1367    my $Token = $UserObject->TokenGenerate(
1368        UserID => 123,
1369    );
1370
1371=cut
1372
1373sub TokenGenerate {
1374    my ( $Self, %Param ) = @_;
1375
1376    # check needed stuff
1377    if ( !$Param{UserID} ) {
1378        $Kernel::OM->Get('Kernel::System::Log')->Log(
1379            Priority => 'error',
1380            Message  => "Need UserID!"
1381        );
1382        return;
1383    }
1384    my $Token = $Kernel::OM->Get('Kernel::System::Main')->GenerateRandomString(
1385        Length => 15,
1386    );
1387
1388    # save token in preferences
1389    $Self->SetPreferences(
1390        Key    => 'UserToken',
1391        Value  => $Token,
1392        UserID => $Param{UserID},
1393    );
1394
1395    return $Token;
1396}
1397
1398=head2 TokenCheck()
1399
1400check password token
1401
1402    my $Valid = $UserObject->TokenCheck(
1403        Token  => $Token,
1404        UserID => 123,
1405    );
1406
1407=cut
1408
1409sub TokenCheck {
1410    my ( $Self, %Param ) = @_;
1411
1412    # check needed stuff
1413    if ( !$Param{Token} || !$Param{UserID} ) {
1414        $Kernel::OM->Get('Kernel::System::Log')->Log(
1415            Priority => 'error',
1416            Message  => 'Need Token and UserID!'
1417        );
1418        return;
1419    }
1420
1421    # get preferences token
1422    my %Preferences = $Self->GetPreferences(
1423        UserID => $Param{UserID},
1424    );
1425
1426    # check requested vs. stored token
1427    if ( $Preferences{UserToken} && $Preferences{UserToken} eq $Param{Token} ) {
1428
1429        # reset password token
1430        $Self->SetPreferences(
1431            Key    => 'UserToken',
1432            Value  => '',
1433            UserID => $Param{UserID},
1434        );
1435
1436        # return true if token is valid
1437        return 1;
1438    }
1439
1440    # return false if token is invalid
1441    return;
1442}
1443
1444=begin Internal:
1445
1446=head2 _UserFullname()
1447
1448Builds the user fullname based on firstname, lastname and login. The order
1449can be configured.
1450
1451    my $Fullname = $Object->_UserFullname(
1452        UserFirstname => 'Test',
1453        UserLastname  => 'Person',
1454        UserLogin     => 'tp',
1455        NameOrder     => 0,         # optional 0, 1, 2, 3, 4, 5
1456    );
1457
1458=cut
1459
1460sub _UserFullname {
1461    my ( $Self, %Param ) = @_;
1462
1463    for my $Needed (qw(UserFirstname UserLastname UserLogin)) {
1464        if ( !$Param{$Needed} ) {
1465            $Kernel::OM->Get('Kernel::System::Log')->Log(
1466                Priority => 'error',
1467                Message  => "Need $Needed!",
1468            );
1469
1470            return;
1471        }
1472    }
1473
1474    my $FirstnameLastNameOrder = $Param{NameOrder} || 0;
1475
1476    my $UserFullname;
1477    if ( $FirstnameLastNameOrder eq '0' ) {
1478        $UserFullname = $Param{UserFirstname} . ' '
1479            . $Param{UserLastname};
1480    }
1481    elsif ( $FirstnameLastNameOrder eq '1' ) {
1482        $UserFullname = $Param{UserLastname} . ', '
1483            . $Param{UserFirstname};
1484    }
1485    elsif ( $FirstnameLastNameOrder eq '2' ) {
1486        $UserFullname = $Param{UserFirstname} . ' '
1487            . $Param{UserLastname} . ' ('
1488            . $Param{UserLogin} . ')';
1489    }
1490    elsif ( $FirstnameLastNameOrder eq '3' ) {
1491        $UserFullname = $Param{UserLastname} . ', '
1492            . $Param{UserFirstname} . ' ('
1493            . $Param{UserLogin} . ')';
1494    }
1495    elsif ( $FirstnameLastNameOrder eq '4' ) {
1496        $UserFullname = '(' . $Param{UserLogin}
1497            . ') ' . $Param{UserFirstname}
1498            . ' ' . $Param{UserLastname};
1499    }
1500    elsif ( $FirstnameLastNameOrder eq '5' ) {
1501        $UserFullname = '(' . $Param{UserLogin}
1502            . ') ' . $Param{UserLastname}
1503            . ', ' . $Param{UserFirstname};
1504    }
1505    elsif ( $FirstnameLastNameOrder eq '6' ) {
1506        $UserFullname = $Param{UserLastname} . ' '
1507            . $Param{UserFirstname};
1508    }
1509    elsif ( $FirstnameLastNameOrder eq '7' ) {
1510        $UserFullname = $Param{UserLastname} . ' '
1511            . $Param{UserFirstname} . ' ('
1512            . $Param{UserLogin} . ')';
1513    }
1514    elsif ( $FirstnameLastNameOrder eq '8' ) {
1515        $UserFullname = '(' . $Param{UserLogin}
1516            . ') ' . $Param{UserLastname}
1517            . ' ' . $Param{UserFirstname};
1518    }
1519    elsif ( $FirstnameLastNameOrder eq '9' ) {
1520        $UserFullname = $Param{UserLastname} . $Param{UserFirstname};
1521    }
1522    return $UserFullname;
1523}
1524
1525=end Internal:
1526
1527=cut
1528
1529=head2 UserLoginExistsCheck()
1530
1531return 1 if another user with this login (username) already exists
1532
1533    $Exist = $UserObject->UserLoginExistsCheck(
1534        UserLogin => 'Some::UserLogin',
1535        UserID => 1, # optional
1536    );
1537
1538=cut
1539
1540sub UserLoginExistsCheck {
1541    my ( $Self, %Param ) = @_;
1542
1543    # get database object
1544    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
1545
1546    return if !$DBObject->Prepare(
1547        SQL =>
1548            "SELECT $Self->{UserTableUserID} FROM $Self->{UserTable} WHERE $Self->{UserTableUser} = ?",
1549        Bind => [ \$Param{UserLogin} ],
1550    );
1551
1552    # fetch the result
1553    my $Flag;
1554    while ( my @Row = $DBObject->FetchrowArray() ) {
1555        if ( !$Param{UserID} || $Param{UserID} ne $Row[0] ) {
1556            $Flag = 1;
1557        }
1558    }
1559    if ($Flag) {
1560        return 1;
1561    }
1562    return 0;
1563}
1564
15651;
1566
1567=head1 TERMS AND CONDITIONS
1568
1569This software is part of the OTRS project (L<https://otrs.org/>).
1570
1571This software comes with ABSOLUTELY NO WARRANTY. For details, see
1572the enclosed file COPYING for license information (GPL). If you
1573did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
1574
1575=cut
1576