1## OpenXPKI::Server::Notification::Connector
2## Notifier that sends the compiled message using a Connector
3
4package OpenXPKI::Server::Notification::Connector;
5
6use strict;
7use warnings;
8use English;
9
10use Data::Dumper;
11
12use JSON;
13use YAML::Loader;
14use DateTime;
15use OpenXPKI::Server::Context qw( CTX );
16use OpenXPKI::Exception;
17use OpenXPKI::Debug;
18
19use Moose;
20
21extends 'OpenXPKI::Server::Notification::Base';
22
23has 'backend' => (
24    is => 'ro',
25    isa => 'Object',
26    reader => '_backend',
27    builder => '_init_backend',
28    lazy => 1,
29);
30
31has '_json' => (
32    is => 'ro',
33    isa => 'JSON',
34    default => sub { return JSON->new(); },
35    lazy => 1,
36);
37
38sub _init_backend {
39
40    my $self = shift;
41
42    ##! 8: 'creating transport'
43    my $cfg = CTX('config')->get_hash( $self->config() . '.backend' );
44
45    my $class = $cfg->{connector};
46    delete $cfg->{class}; # this is the notify package name
47    delete $cfg->{connector}; # this is the connector package name
48
49    eval "use $class;1" or OpenXPKI::Exception->throw(
50        message => 'Unable to load connector backend class',
51        params  => {
52            class => $class,
53            error => $@,
54        });
55
56    my $conn;
57    eval{ $conn = $class->new(%{$cfg}); };
58    if ($EVAL_ERROR || !$conn) {
59        OpenXPKI::Exception->throw(
60        message => 'Unable to initialize connector backend class',
61        params  => {
62            class => $class,
63            error => $@,
64        });
65    }
66
67    return $conn;
68}
69
70=head1 Functions
71=head2 notify
72see @OpenXPKI::Server::Notification::Base
73=cut
74sub notify {
75
76    ##! 1: 'start'
77
78    my $self = shift;
79    my $args = shift;
80
81    my $msg = $args->{MESSAGE};
82    my $token = $args->{TOKEN};
83    my $template_vars = $args->{VARS};
84
85    my $msgconfig = $self->config().'.message.'.$msg;
86
87    # Test if there is an entry for this kind of message
88    my @handles = CTX('config')->get_keys( $msgconfig );
89
90    ##! 8: 'Starting message ' . $msg
91
92    ##! 16: 'Found handles ' . Dumper @handles
93
94    ##! 32: 'Template vars: ' . Dumper $template_vars
95
96    if (!@handles) {
97        CTX('log')->system()->debug("No notifcations to send for $msgconfig");
98        return 0;
99    }
100
101    # Walk through the handles
102    QUEUE_HANDLE:
103    foreach my $handle (@handles) {
104
105        my $pi = $token->{$handle};
106
107        ##! 16: 'Starting handle '.$handle.', PI: ' . Dumper $pi
108
109        # We do the eval per handle
110        eval {
111            my @path = CTX('config')->get_scalar_as_list( "$msgconfig.$handle.path" );
112
113            # template defines a YAML file that is rendered using TT
114            my $template_file = CTX('config')->get( "$msgconfig.$handle.template" );
115
116            my $data;
117            if ($template_file) {
118                ##! 32: "Using template file $template_file"
119                my $yaml = $self->_render_template_file( $template_file.'.yaml', $template_vars );
120                ##! 64: $yaml
121                $data = YAML::Loader->new->load($yaml);
122            } elsif (my $content = CTX('config')->get_hash( "$msgconfig.$handle.content" )) {
123                my %vars = %{$template_vars};
124                ##! 32: "Using content hash with key " . join(", ", keys %($content))
125                ##! 64: $content
126                # we use template toolkit on any scalar value that contains a % sign
127                foreach my $key (keys %{$content}) {
128                    my $value = $content->{$key} || '';
129                    if (!ref $value && ($value =~ m{%}))  {
130                        $value = $self->_render_template( $value, \%vars );
131                    }
132                    $data->{$key} = $value;
133                }
134            }
135
136            if (!$data) {
137                CTX('log')->system()->warn("Unable to generate message for $handle - no data");
138                next QUEUE_HANDLE;
139            }
140
141            ##! 32: $data
142            ##! 64: "Notify to path " . join(".", @path)
143            ##! 64: $data
144            CTX('log')->system()->trace(sprintf("Notify to %s with payload %s",
145                join(".", @path), Dumper $data)) if (CTX('log')->system()->is_trace());
146
147            my $json = $self->_json()->encode($data);
148            die "Unable to encode to json" unless($json);
149            $self->_backend()->set(\@path, $json);
150
151        };
152        if ($EVAL_ERROR) {
153            CTX('log')->system()->error("Notify for $msgconfig/$handle failed with $EVAL_ERROR");
154        }
155    } # end handle
156
157    return $token;
158
159}
160
161sub _cleanup {
162
163}
164
1651;
166
167=head1 Name
168
169OpenXPKI::Server::Notification::Connector - Notification via Connector
170
171=head1 Description
172
173This class implements a notifier that sends out notifications via the "set"
174method of a Connector backend. The payload can be created in different ways.
175
176For now this backend supports creation of a data structure directly as a hash
177from the config or by rendering a YAML file using Template Toolkit and parsing
178the yaml afterwards.
179
180The only supported output format for now is JSON.
181
182=head1 Configuration
183
184# Sample configuration using a JSON based REST API via HTTP
185
186backend:
187    class: OpenXPKI::Server::Notification::Connector
188    connector: Connector::Proxy::HTTP
189    LOCATION: https://api.acme.org/v2/
190    content_type: application/json
191    http_method: POST
192    header:
193        Authorization: MyAuthToken
194
195# template settings
196template:
197    dir:   /usr/local/etc/openxpki/template/alerts/
198
199message:
200    cert_expiry:
201        default:
202            path: alert
203            # the template file must end on .yaml and generate a hash
204            template: cert_expiry
205
206    cert_issued:
207        default:
208            path: info
209            content:
210                title: A new certificate for [% cert_subject %] has been created
211                message: |
212                    A new certificate for [% cert_subject %] has been created.
213                    You can find more details on the PKI WebUI.
214                priority: info
215