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::GenericInterface::Webservice;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(:all);
15
16our @ObjectDependencies = (
17    'Kernel::Config',
18    'Kernel::System::Cache',
19    'Kernel::System::DB',
20    'Kernel::System::GenericInterface::DebugLog',
21    'Kernel::System::GenericInterface::WebserviceHistory',
22    'Kernel::System::Log',
23    'Kernel::System::Main',
24    'Kernel::System::Valid',
25    'Kernel::System::YAML',
26);
27
28=head1 NAME
29
30Kernel::System::GenericInterface::Webservice
31
32=head1 DESCRIPTION
33
34Web service configuration backend.
35
36=head1 PUBLIC INTERFACE
37
38=head2 new()
39
40Don't use the constructor directly, use the ObjectManager instead:
41
42    my $WebserviceObject = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice');
43
44=cut
45
46sub new {
47    my ( $Webservice, %Param ) = @_;
48
49    # allocate new hash for object
50    my $Self = {};
51    bless( $Self, $Webservice );
52
53    return $Self;
54}
55
56=head2 WebserviceAdd()
57
58add new Webservices
59
60returns id of new web service if successful or undef otherwise
61
62    my $ID = $WebserviceObject->WebserviceAdd(
63        Name    => 'some name',
64        Config  => $ConfigHashRef,
65        ValidID => 1,
66        UserID  => 123,
67    );
68
69=cut
70
71sub WebserviceAdd {
72    my ( $Self, %Param ) = @_;
73
74    # check needed stuff
75    for my $Key (qw(Name Config ValidID UserID)) {
76        if ( !$Param{$Key} ) {
77            $Kernel::OM->Get('Kernel::System::Log')->Log(
78                Priority => 'error',
79                Message  => "Need $Key!"
80            );
81            return;
82        }
83    }
84
85    # check config
86    if ( !IsHashRefWithData( $Param{Config} ) ) {
87        $Kernel::OM->Get('Kernel::System::Log')->Log(
88            Priority => 'error',
89            Message  => "Web service Config should be a non empty hash reference!",
90        );
91        return;
92    }
93
94    # check config internals
95    if ( !IsHashRefWithData( $Param{Config}->{Debugger} ) ) {
96        $Kernel::OM->Get('Kernel::System::Log')->Log(
97            Priority => 'error',
98            Message  => "Web service Config Debugger should be a non empty hash reference!",
99        );
100        return;
101    }
102    if ( !IsStringWithData( $Param{Config}->{Debugger}->{DebugThreshold} ) ) {
103        $Kernel::OM->Get('Kernel::System::Log')->Log(
104            Priority => 'error',
105            Message  => "Web service Config Debugger DebugThreshold should be a non empty string!",
106        );
107        return;
108    }
109    if ( !defined $Param{Config}->{Provider} && !defined $Param{Config}->{Requester} ) {
110        $Kernel::OM->Get('Kernel::System::Log')->Log(
111            Priority => 'error',
112            Message  => "Web service Config Provider or Requester should be defined!",
113        );
114        return;
115    }
116    for my $CommunicationType (qw(Provider Requester)) {
117        if ( defined $Param{Config}->{$CommunicationType} ) {
118            if ( !IsHashRefWithData( $Param{Config}->{$CommunicationType} ) ) {
119                $Kernel::OM->Get('Kernel::System::Log')->Log(
120                    Priority => 'error',
121                    Message  => "Web service Config $CommunicationType should be a non empty hash"
122                        . " reference!",
123                );
124                return;
125            }
126            if ( !IsHashRefWithData( $Param{Config}->{$CommunicationType}->{Transport} ) ) {
127                $Kernel::OM->Get('Kernel::System::Log')->Log(
128                    Priority => 'error',
129                    Message  => "Web service Config $CommunicationType Transport should be a"
130                        . " non empty hash reference!",
131                );
132                return;
133            }
134        }
135    }
136
137    # Check if web service is using an old configuration type and upgrade if necessary.
138    $Self->_WebserviceConfigUpgrade(%Param);
139
140    # dump config as string
141    my $Config = $Kernel::OM->Get('Kernel::System::YAML')->Dump( Data => $Param{Config} );
142
143    # get database object
144    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
145
146    # sql
147    return if !$DBObject->Do(
148        SQL =>
149            'INSERT INTO gi_webservice_config (name, config, valid_id, '
150            . ' create_time, create_by, change_time, change_by)'
151            . ' VALUES (?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
152        Bind => [
153            \$Param{Name}, \$Config, \$Param{ValidID},
154            \$Param{UserID}, \$Param{UserID},
155        ],
156    );
157
158    return if !$DBObject->Prepare(
159        SQL  => 'SELECT id FROM gi_webservice_config WHERE name = ?',
160        Bind => [ \$Param{Name} ],
161    );
162
163    my $ID;
164    while ( my @Row = $DBObject->FetchrowArray() ) {
165        $ID = $Row[0];
166    }
167
168    # delete cache
169    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
170        Type => 'Webservice',
171    );
172
173    # get web service history object
174    my $WebserviceHistoryObject = $Kernel::OM->Get('Kernel::System::GenericInterface::WebserviceHistory');
175
176    # add history
177    return if !$WebserviceHistoryObject->WebserviceHistoryAdd(
178        WebserviceID => $ID,
179        Config       => $Param{Config},
180        UserID       => $Param{UserID},
181    );
182
183    return $ID;
184}
185
186=head2 WebserviceGet()
187
188get Webservices attributes
189
190    my $Webservice = $WebserviceObject->WebserviceGet(
191        ID   => 123,            # ID or Name must be provided
192        Name => 'MyWebservice',
193    );
194
195Returns:
196
197    $Webservice = {
198        ID         => 123,
199        Name       => 'some name',
200        Config     => $ConfigHashRef,
201        ValidID    => 123,
202        CreateTime => '2011-02-08 15:08:00',
203        ChangeTime => '2011-02-08 15:08:00',
204    };
205
206=cut
207
208sub WebserviceGet {
209    my ( $Self, %Param ) = @_;
210
211    # check needed stuff
212    if ( !$Param{ID} && !$Param{Name} ) {
213        $Kernel::OM->Get('Kernel::System::Log')->Log(
214            Priority => 'error',
215            Message  => 'Need ID or Name!'
216        );
217        return;
218    }
219
220    # get cache object
221    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
222
223    # check cache
224    my $CacheKey;
225    if ( $Param{ID} ) {
226        $CacheKey = 'WebserviceGet::ID::' . $Param{ID};
227    }
228    else {
229        $CacheKey = 'WebserviceGet::Name::' . $Param{Name};
230
231    }
232    my $Cache = $CacheObject->Get(
233        Type => 'Webservice',
234        Key  => $CacheKey,
235    );
236    return $Cache if $Cache;
237
238    # get database object
239    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
240
241    # sql
242    if ( $Param{ID} ) {
243        return if !$DBObject->Prepare(
244            SQL => 'SELECT id, name, config, valid_id, create_time, change_time '
245                . 'FROM gi_webservice_config WHERE id = ?',
246            Bind  => [ \$Param{ID} ],
247            Limit => 1,
248        );
249    }
250    else {
251        return if !$DBObject->Prepare(
252            SQL => 'SELECT id, name, config, valid_id, create_time, change_time '
253                . 'FROM gi_webservice_config WHERE name = ?',
254            Bind  => [ \$Param{Name} ],
255            Limit => 1,
256        );
257    }
258
259    # get yaml object
260    my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
261
262    my %Data;
263    while ( my @Data = $DBObject->FetchrowArray() ) {
264
265        my $Config = $YAMLObject->Load( Data => $Data[2] );
266
267        %Data = (
268            ID         => $Data[0],
269            Name       => $Data[1],
270            Config     => $Config,
271            ValidID    => $Data[3],
272            CreateTime => $Data[4],
273            ChangeTime => $Data[5],
274        );
275    }
276
277    # get the cache TTL (in seconds)
278    my $CacheTTL = int(
279        $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::WebserviceConfig::CacheTTL')
280            || 3600
281    );
282
283    # set cache
284    $CacheObject->Set(
285        Type  => 'Webservice',
286        Key   => $CacheKey,
287        Value => \%Data,
288        TTL   => $CacheTTL,
289    );
290
291    return \%Data;
292}
293
294=head2 WebserviceUpdate()
295
296update web service attributes
297
298returns 1 if successful or undef otherwise
299
300    my $Success = $WebserviceObject->WebserviceUpdate(
301        ID      => 123,
302        Name    => 'some name',
303        Config  => $ConfigHashRef,
304        ValidID => 1,
305        UserID  => 123,
306    );
307
308=cut
309
310sub WebserviceUpdate {
311    my ( $Self, %Param ) = @_;
312
313    # check needed stuff
314    for my $Key (qw(ID Name Config ValidID UserID)) {
315        if ( !$Param{$Key} ) {
316            $Kernel::OM->Get('Kernel::System::Log')->Log(
317                Priority => 'error',
318                Message  => "Need $Key!"
319            );
320            return;
321        }
322    }
323
324    # check config
325    if ( !IsHashRefWithData( $Param{Config} ) ) {
326        $Kernel::OM->Get('Kernel::System::Log')->Log(
327            Priority => 'error',
328            Message  => "Web service Config should be a non empty hash reference!",
329        );
330        return;
331    }
332
333    # check config internals
334    if ( !IsHashRefWithData( $Param{Config}->{Debugger} ) ) {
335        $Kernel::OM->Get('Kernel::System::Log')->Log(
336            Priority => 'error',
337            Message  => "Web service Config Debugger should be a non empty hash reference!",
338        );
339        return;
340    }
341    if ( !IsStringWithData( $Param{Config}->{Debugger}->{DebugThreshold} ) ) {
342        $Kernel::OM->Get('Kernel::System::Log')->Log(
343            Priority => 'error',
344            Message  => "Web service Config Debugger DebugThreshold should be a non empty string!",
345        );
346        return;
347    }
348    if ( !defined $Param{Config}->{Provider} && !defined $Param{Config}->{Requester} ) {
349        $Kernel::OM->Get('Kernel::System::Log')->Log(
350            Priority => 'error',
351            Message  => "Web service Config Provider or Requester should be defined!",
352        );
353        return;
354    }
355    for my $CommunicationType (qw(Provider Requester)) {
356        if ( defined $Param{Config}->{$CommunicationType} ) {
357            if ( !IsHashRefWithData( $Param{Config}->{$CommunicationType} ) ) {
358                $Kernel::OM->Get('Kernel::System::Log')->Log(
359                    Priority => 'error',
360                    Message  => "Web service Config $CommunicationType should be a non empty hash"
361                        . " reference!",
362                );
363                return;
364            }
365            if ( !IsHashRefWithData( $Param{Config}->{$CommunicationType}->{Transport} ) ) {
366                $Kernel::OM->Get('Kernel::System::Log')->Log(
367                    Priority => 'error',
368                    Message  => "Web service Config $CommunicationType Transport should be a"
369                        . " non empty hash reference!",
370                );
371                return;
372            }
373        }
374    }
375
376    # Check if web service is using an old configuration type and upgrade if necessary.
377    $Self->_WebserviceConfigUpgrade(%Param);
378
379    # dump config as string
380    my $Config = $Kernel::OM->Get('Kernel::System::YAML')->Dump( Data => $Param{Config} );
381
382    # get database object
383    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
384
385    # check if config and valid_id is the same
386    return if !$DBObject->Prepare(
387        SQL   => 'SELECT config, valid_id, name FROM gi_webservice_config WHERE id = ?',
388        Bind  => [ \$Param{ID} ],
389        Limit => 1,
390    );
391
392    my $ConfigCurrent;
393    my $ValidIDCurrent;
394    my $NameCurrent;
395    while ( my @Data = $DBObject->FetchrowArray() ) {
396        $ConfigCurrent  = $Data[0];
397        $ValidIDCurrent = $Data[1];
398        $NameCurrent    = $Data[2];
399    }
400
401    return 1 if $ValidIDCurrent eq $Param{ValidID}
402        && $Config eq $ConfigCurrent
403        && $NameCurrent eq $Param{Name};
404
405    # sql
406    return if !$DBObject->Do(
407        SQL => 'UPDATE gi_webservice_config SET name = ?, config = ?, '
408            . ' valid_id = ?, change_time = current_timestamp, '
409            . ' change_by = ? WHERE id = ?',
410        Bind => [
411            \$Param{Name}, \$Config, \$Param{ValidID}, \$Param{UserID},
412            \$Param{ID},
413        ],
414    );
415
416    # delete cache
417    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
418        Type => 'Webservice',
419    );
420
421    # get web service history object
422    my $WebserviceHistoryObject = $Kernel::OM->Get('Kernel::System::GenericInterface::WebserviceHistory');
423
424    # add history
425    return if !$WebserviceHistoryObject->WebserviceHistoryAdd(
426        WebserviceID => $Param{ID},
427        Config       => $Param{Config},
428        UserID       => $Param{UserID},
429    );
430
431    return 1;
432}
433
434=head2 WebserviceDelete()
435
436delete a Webservice
437
438returns 1 if successful or undef otherwise
439
440    my $Success = $WebserviceObject->WebserviceDelete(
441        ID      => 123,
442        UserID  => 123,
443    );
444
445=cut
446
447sub WebserviceDelete {
448    my ( $Self, %Param ) = @_;
449
450    # check needed stuff
451    for my $Key (qw(ID UserID)) {
452        if ( !$Param{$Key} ) {
453            $Kernel::OM->Get('Kernel::System::Log')->Log(
454                Priority => 'error',
455                Message  => "Need $Key!"
456            );
457            return;
458        }
459    }
460
461    # check if exists
462    my $Webservice = $Self->WebserviceGet(
463        ID => $Param{ID},
464    );
465    return if !IsHashRefWithData($Webservice);
466
467    # get web service history object
468    my $WebserviceHistoryObject = $Kernel::OM->Get('Kernel::System::GenericInterface::WebserviceHistory');
469
470    # delete history
471    return if !$WebserviceHistoryObject->WebserviceHistoryDelete(
472        WebserviceID => $Param{ID},
473        UserID       => $Param{UserID},
474    );
475
476    # get debug log object
477    my $DebugLogObject = $Kernel::OM->Get('Kernel::System::GenericInterface::DebugLog');
478
479    # delete debugging data for web service
480    return if !$DebugLogObject->LogDelete(
481        WebserviceID   => $Param{ID},
482        NoErrorIfEmpty => 1,
483    );
484
485    # delete web service
486    return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
487        SQL  => 'DELETE FROM gi_webservice_config WHERE id = ?',
488        Bind => [ \$Param{ID} ],
489    );
490
491    # delete cache
492    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
493        Type => 'Webservice',
494    );
495
496    return 1;
497}
498
499=head2 WebserviceList()
500
501get web service list
502
503    my $List = $WebserviceObject->WebserviceList();
504
505    or
506
507    my $List = $WebserviceObject->WebserviceList(
508        Valid => 0, # optional, defaults to 1
509    );
510
511=cut
512
513sub WebserviceList {
514    my ( $Self, %Param ) = @_;
515
516    # get cache object
517    my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
518
519    # check Valid param
520    my $Valid = ( IsStringWithData( $Param{Valid} ) && $Param{Valid} eq 0 ) ? 0 : 1;
521
522    # check cache
523    my $CacheKey = 'WebserviceList::Valid::' . $Valid;
524    my $Cache    = $CacheObject->Get(
525        Type => 'Webservice',
526        Key  => $CacheKey,
527    );
528    return $Cache if ref $Cache;
529
530    my $SQL = 'SELECT id, name FROM gi_webservice_config';
531
532    if ($Valid) {
533
534        # get valid object
535        my $ValidObject = $Kernel::OM->Get('Kernel::System::Valid');
536
537        $SQL .= ' WHERE valid_id IN (' . join ', ', $ValidObject->ValidIDsGet() . ')';
538    }
539
540    # get database object
541    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
542
543    return if !$DBObject->Prepare( SQL => $SQL );
544
545    my %Data;
546    while ( my @Row = $DBObject->FetchrowArray() ) {
547        $Data{ $Row[0] } = $Row[1];
548    }
549
550    # get the cache TTL (in seconds)
551    my $CacheTTL = int(
552        $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::WebserviceConfig::CacheTTL')
553            || 3600
554    );
555
556    # set cache
557    $CacheObject->Set(
558        Type  => 'Webservice',
559        Key   => $CacheKey,
560        Value => \%Data,
561        TTL   => $CacheTTL,
562    );
563
564    return \%Data;
565}
566
567=begin Internal:
568
569=head2 _WebserviceConfigUpgrade()
570
571Update version if webservice config (e.g. for API changes).
572
573    my $Config = $WebserviceObject->_WebserviceConfigUpgrade( Config => $Config );
574
575=cut
576
577sub _WebserviceConfigUpgrade {
578    my ( $Self, %Param ) = @_;
579
580    return if !IsHashRefWithData( $Param{Config} );
581
582    # Updates of SOAP and REST transport in OTRS 6:
583    #   Authentication, SSL and Proxy option changes, introduction of timeout param.
584    # Upgrade is considered necessary if the new (and now mandatory) parameter 'Timeout' isn't set.
585    if (
586        IsHashRefWithData( $Param{Config}->{Requester} )    # prevent creation of dummy elements
587        && IsStringWithData( $Param{Config}->{Requester}->{Transport}->{Type} )
588        && (
589            $Param{Config}->{Requester}->{Transport}->{Type} eq 'HTTP::REST'
590            || $Param{Config}->{Requester}->{Transport}->{Type} eq 'HTTP::SOAP'
591        )
592        && IsHashRefWithData( $Param{Config}->{Requester}->{Transport}->{Config} )
593        && !IsStringWithData( $Param{Config}->{Requester}->{Transport}->{Config}->{Timeout} )
594        )
595    {
596        my $RequesterTransportConfig = $Param{Config}->{Requester}->{Transport}->{Config};
597        my $RequesterTransportType   = $Param{Config}->{Requester}->{Transport}->{Type};
598
599        # set default timeout
600        if ( $RequesterTransportType eq 'HTTP::SOAP' ) {
601            $RequesterTransportConfig->{Timeout} = 60;
602        }
603        else {
604            $RequesterTransportConfig->{Timeout} = 300;
605        }
606
607        # set default SOAPAction scheme for SOAP
608        if (
609            $RequesterTransportType eq 'HTTP::SOAP'
610            && IsStringWithData( $RequesterTransportConfig->{SOAPAction} )
611            && $RequesterTransportConfig->{SOAPAction} eq 'Yes'
612            )
613        {
614            $RequesterTransportConfig->{SOAPActionScheme} = 'NameSpaceSeparatorOperation';
615        }
616
617        # convert auth settings
618        my $Authentication = delete $RequesterTransportConfig->{Authentication};
619        if (
620            IsHashRefWithData($Authentication)
621            && $Authentication->{Type}
622            && $Authentication->{Type} eq 'BasicAuth'
623            )
624        {
625            $RequesterTransportConfig->{Authentication} = {
626                AuthType          => $Authentication->{Type},
627                BasicAuthUser     => $Authentication->{User},
628                BasicAuthPassword => $Authentication->{Password},
629            };
630        }
631
632        # convert ssl settings
633        my $SSL  = delete $RequesterTransportConfig->{SSL};
634        my $X509 = delete $RequesterTransportConfig->{X509};
635        if (
636            $RequesterTransportType eq 'HTTP::SOAP'
637            && IsHashRefWithData($SSL)
638            && $SSL->{UseSSL}
639            && $SSL->{UseSSL} eq 'Yes'
640            )
641        {
642            $RequesterTransportConfig->{SSL} = {
643                UseSSL         => 'Yes',
644                SSLPassword    => $SSL->{SSLP12Password},
645                SSLCertificate => $SSL->{SSLP12Certificate},
646                SSLCADir       => $SSL->{SSLCADir},
647                SSLCAFile      => $SSL->{SSLCAFile},
648            };
649        }
650        elsif (
651            IsHashRefWithData($X509)
652            && $X509->{UseX509}
653            && $X509->{UseX509} eq 'Yes'
654            )
655        {
656            $RequesterTransportConfig->{SSL} = {
657                UseSSL         => 'Yes',
658                SSLKey         => $X509->{X509KeyFile},
659                SSLCertificate => $X509->{X509CertFile},
660                SSLCAFile      => $X509->{X509CAFile},
661            };
662        }
663        else {
664            $RequesterTransportConfig->{SSL}->{UseSSL} = 'No';
665        }
666
667        # convert proxy settings
668        if (
669            IsHashRefWithData($SSL)
670            && $SSL->{SSLProxy}
671            )
672        {
673            $RequesterTransportConfig->{Proxy} = {
674                UseProxy      => 'Yes',
675                ProxyHost     => $SSL->{SSLProxy},
676                ProxyUser     => $SSL->{SSLProxyUser},
677                ProxyPassword => $SSL->{SSLProxyPassword},
678                ProxyExclude  => 'No',
679            };
680        }
681        else {
682            $RequesterTransportConfig->{Proxy}->{UseProxy} = 'No';
683        }
684
685        # set updated config
686        $Param{Config}->{Requester}->{Transport}->{Config} = $RequesterTransportConfig;
687    }
688
689    return 1;
690}
691
6921;
693
694=end Internal:
695
696=head1 TERMS AND CONDITIONS
697
698This software is part of the OTRS project (L<https://otrs.org/>).
699
700This software comes with ABSOLUTELY NO WARRANTY. For details, see
701the enclosed file COPYING for license information (GPL). If you
702did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
703
704=cut
705