Skip to content

Commit e38260f

Browse files
committed
enh(apps::monitoring::zscaler::zdx::api): adapt to OneAPI authentication
Refs: CTOR-2251
1 parent 08b03e1 commit e38260f

File tree

6 files changed

+153
-183
lines changed

6 files changed

+153
-183
lines changed

src/apps/monitoring/zscaler/zdx/api/custom/api.pm

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use centreon::plugins::misc qw(json_decode is_empty value_of is_excluded);
3030

3131
sub new {
3232
my ($class, %options) = @_;
33-
my $self = {};
33+
my $self = {};
3434
bless $self, $class;
3535

3636
if (!defined($options{output})) {
@@ -41,23 +41,24 @@ sub new {
4141
$options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument.");
4242
$options{output}->option_exit();
4343
}
44-
44+
4545
if (!defined($options{noptions})) {
4646
$options{options}->add_options(arguments => {
47-
'key-id:s' => { name => 'key_id', default => '' },
48-
'key-secret:s' => { name => 'key_secret', default => '' },
49-
'api-path:s' => { name => 'api_path', default => '/v1' },
50-
'hostname:s' => { name => 'hostname', default => 'api.zdxcloud.net' },
51-
'port:s' => { name => 'port', default => 443 },
52-
'proto:s' => { name => 'proto', default => 'https' },
53-
'timeout:s' => { name => 'timeout', default => 10 }
47+
'auth-url:s' => { name => 'auth_url', default => '' },
48+
'client-id:s' => { name => 'client_id', default => '' },
49+
'client-secret:s' => { name => 'client_secret', default => '' },
50+
'api-path:s' => { name => 'api_path', default => '/zdx/v1' },
51+
'hostname:s' => { name => 'hostname', default => 'api.zsapi.net' },
52+
'port:s' => { name => 'port', default => 443 },
53+
'proto:s' => { name => 'proto', default => 'https' },
54+
'timeout:s' => { name => 'timeout', default => 10 }
5455
});
5556
}
5657
$options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1);
5758

5859
$self->{output} = $options{output};
59-
$self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl');
60-
$self->{cache} = centreon::plugins::statefile->new(%options);
60+
$self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl');
61+
$self->{cache} = centreon::plugins::statefile->new(%options);
6162

6263
return $self;
6364
}
@@ -70,14 +71,13 @@ sub get_token {
7071
$self->settings();
7172

7273
my $has_cache_file = $self->{cache}->read(
73-
statefile => 'zdx_token_' . md5_hex(
74-
$self->{hostname}
75-
. ':' . $self->{port}
76-
. '_' . $self->{key_id})
74+
statefile => 'zdx_token_' . md5_hex(
75+
$self->{auth_url}
76+
. '_' . $self->{client_id})
7777
);
7878
# return token stored in cache if exists after checking it is still valid
7979
if ($has_cache_file) {
80-
my $expiration = $self->{cache}->get(name => 'expiration');
80+
my $expiration = $self->{cache}->get(name => 'expiration');
8181
if (defined($expiration) && $expiration > time() + 60) {
8282
$self->{token} = $self->{cache}->get(name => 'token');
8383
$self->{token_type} = $self->{cache}->get(name => 'token_type');
@@ -89,17 +89,22 @@ sub get_token {
8989
# if we do not have a token or if it has to be renewed
9090
my $content = $self->{http}->request(
9191
method => 'POST',
92-
url_path => '/v1/oauth/token',
93-
query_form_post => '{"key_id": "' . $self->{key_id} . '", "key_secret": "' . $self->{key_secret} . '"}',
94-
92+
full_url => $self->{auth_url},
93+
header => [ 'Content-Type: application/x-www-form-urlencoded' ],
94+
query_form_post => "grant_type=client_credentials&client_id=" . $self->{client_id}
95+
. "&client_secret=" . $self->{client_secret},
9596
);
9697
my $decoded_content = json_decode($content, output => $self->{output});
9798

98-
$self->{output}->option_exit(short_msg => "No token found in '$content'") unless ($decoded_content->{token});
99-
$self->{token} = $decoded_content->{token};
100-
$self->{token_type} = $decoded_content->{token_type};
99+
$self->{output}->option_exit(short_msg => "No token found in '$content'")
100+
unless ($decoded_content->{access_token} || $decoded_content->{token});
101+
# store in $self for further requests
102+
$self->{token} = $decoded_content->{access_token} // $decoded_content->{token};
103+
$self->{token_type} = $decoded_content->{token_type} // 'Bearer';
104+
# store in cache for further executions
101105
$self->{http}->add_header(key => 'Authorization', value => $self->{token_type} . ' ' . $self->{token});
102-
$self->{cache}->write(data => { token => $decoded_content->{token}, token_type => $decoded_content->{token_type}, expiration => ($decoded_content->{expires_in} + time()) });
106+
$self->{cache}->write(data => { token => $self->{token}, token_type => $self->{token_type}, expiration => ($decoded_content->{expires_in} + time()) });
107+
103108
return $self->{token};
104109
}
105110

@@ -120,7 +125,7 @@ sub build_location_filters {
120125
# if location filters are provided, get the list of location ids
121126
my $locations = $self->get_locations(%options);
122127
# $locations points to an array of {id, name}
123-
return { loc => [map { $_->{id}} @$locations] };
128+
return { loc => [ map {$_->{id}} @$locations ] };
124129
}
125130

126131
sub get_unique_app {
@@ -143,10 +148,10 @@ sub get_unique_app {
143148
sub get_unique_app_metrics {
144149
my ($self, %options) = @_;
145150

146-
my $to = time();
147-
my $get_params = { %{ $self->{get_params}} };
151+
my $to = time();
152+
my $get_params = { %{$self->{get_params}} };
148153
$get_params->{from} = $to - 60 * ($options{max_metrics_age} // 20);
149-
$get_params->{to} = $to;
154+
$get_params->{to} = $to;
150155

151156
my $content = $self->{http}->request(
152157
method => 'GET',
@@ -232,8 +237,8 @@ sub get_locations {
232237

233238
# get all locations and check which ones match the filters
234239
my $locations_json = $self->{http}->request(
235-
method => 'GET',
236-
url_path => $self->{option_results}->{api_path} . '/administration/locations/'
240+
method => 'GET',
241+
url_path => $self->{option_results}->{api_path} . '/administration/locations/'
237242
);
238243
my $locations = json_decode($locations_json, output => $self->{output});
239244

@@ -246,8 +251,8 @@ sub get_locations {
246251
$options{exclude_location_name});
247252

248253
push @result, {
249-
id => $loc->{id},
250-
name => $loc->{name},
254+
id => $loc->{id},
255+
name => $loc->{name},
251256
};
252257
}
253258

@@ -259,10 +264,11 @@ sub set_defaults {}
259264
sub check_options {
260265
my ($self, %options) = @_;
261266

262-
$self->{$_} = $self->{option_results}->{$_} foreach qw(hostname port proto api_path timeout key_id key_secret );
267+
$self->{$_} = $self->{option_results}->{$_} foreach qw(hostname port proto timeout auth_url api_path client_id client_secret);
268+
$self->{output}->option_exit(short_msg => "Mandatory option 'auth-url' is missing for OneAPI authentication.") if ($self->{auth_url} eq '');
263269

264-
foreach (qw(key_id key_secret)) {
265-
$self->{output}->option_exit(short_msg => "Mandatory option '$_' is missing.") if ($self->{$_} eq '');
270+
foreach (qw(client_id client_secret)) {
271+
$self->{output}->option_exit(short_msg => "Mandatory option '$_' is missing for OneAPI authentication.") if ($self->{$_} eq '');
266272
$self->{output}->option_exit(short_msg => "Option '$_' contains illegal characters.") if ($self->{$_} =~ /([\b\f\n\r\t\"\\]+)/);
267273
}
268274

@@ -274,9 +280,7 @@ sub check_options {
274280
sub settings {
275281
my ($self, %options) = @_;
276282

277-
#$self->build_options_for_httplib();
278283
$self->{http}->add_header(key => 'Accept', value => 'application/json');
279-
$self->{http}->add_header(key => 'Content-Type', value => 'application/json');
280284
$self->{http}->set_options(%{$self->{option_results}});
281285
}
282286

@@ -298,29 +302,37 @@ Zscaler Digital Experience (ZDX) Rest API
298302
299303
=over 8
300304
305+
=item B<--auth-url>
306+
307+
Authentication URL to get a token from (mandatory).
308+
309+
Depends on your Zscaler customer name.
310+
311+
Example: C<https://company-name.zslogin.net/oauth2/v1/token>.
312+
301313
=item B<--hostname>
302314
303-
ZDX API hostname (default: C<api.zdxcloud.net>)
315+
API URL (default: C<api.zsapi.net>). You should not need to change it.
304316
305317
=item B<--port>
306318
307-
API port (default: 443)
319+
API port (default: 443).
308320
309321
=item B<--proto>
310322
311-
Specify http if needed (default: 'https')
323+
Specify http if needed (default: 'https').
312324
313325
=item B<--api-path>
314326
315-
API URL path (default: '/api')
327+
API URL path (default: '/zdx/v1')
316328
317-
=item B<--key-id>
329+
=item B<--client-id>
318330
319-
Key ID (see L<here|https://help.zscaler.com/zdx/managing-zdx-api-keys> for more details).
331+
Client ID for OneAPI authentication (see L<here|https://help.zscaler.com/zidentity/understanding-oneapi-authentication> for more details).
320332
321-
=item B<--key-secret>
333+
=item B<--client-secret>
322334
323-
Key secret (see L<here|https://help.zscaler.com/zdx/managing-zdx-api-keys> for more details).
335+
Client secret for OneAPI authentication (see L<here|https://help.zscaler.com/zidentity/understanding-oneapi-authentication> for more details).
324336
325337
=item B<--timeout>
326338

tests/apps/monitoring/zscaler/zdx/api/application.robot

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ Test Timeout 120s
1111
*** Variables ***
1212
${MOCKOON_JSON} ${CURDIR}${/}mockoon.json
1313
${CMD} ${CENTREON_PLUGINS}
14-
... --plugin=apps::monitoring::zscaler::zdx::api::plugin
15-
... --mode=application
16-
... --hostname=${HOSTNAME}
17-
... --port=${APIPORT}
18-
... --proto=http
19-
... --key-id=1
20-
... --key-secret=1
14+
... --plugin=apps::monitoring::zscaler::zdx::api::plugin
15+
... --mode=application
16+
... --hostname=${HOSTNAME}
17+
... --port=${APIPORT}
18+
... --proto=http
19+
... --client-id=1
20+
... --client-secret=1
21+
... --auth-url=http://127.0.0.1:${APIPORT}/oauth2/v1/token
2122

2223

2324
*** Test Cases ***

tests/apps/monitoring/zscaler/zdx/api/discovery.robot

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,23 @@ Test Timeout 120s
99

1010

1111
*** Variables ***
12-
${MOCKOON_JSON} ${CURDIR}${/}mockoon.json
13-
${CMD} ${CENTREON_PLUGINS}
14-
... --plugin=apps::monitoring::zscaler::zdx::api::plugin
15-
... --mode=discovery
16-
... --hostname=${HOSTNAME}
17-
... --port=${APIPORT}
18-
... --proto=http
19-
... --key-id=1
20-
... --key-secret=1
21-
${EXPECTED_RESULT} SEPARATOR=
22-
... {"discovered_items":2,"end_time":1765545358,"results":[{"hostname": "127.0.0.1", "id": 1, "name": \
23-
... "SharePoint Online", "score": 73.1244323342416, "total_users": 50}, {"hostname": "127.0.0.1", \
24-
... "id": 3, "name": "Outlook Online", "score": 81.551724137931, "total_users": 67}],"duration":0, \
25-
... "start_time":1765545358}
12+
${MOCKOON_JSON} ${CURDIR}${/}mockoon.json
13+
${CMD} ${CENTREON_PLUGINS}
14+
... --plugin=apps::monitoring::zscaler::zdx::api::plugin
15+
... --mode=discovery
16+
... --hostname=${HOSTNAME}
17+
... --port=${APIPORT}
18+
... --proto=http
19+
... --client-id=1
20+
... --client-secret=1
21+
... --auth-url=http://127.0.0.1:${APIPORT}/oauth2/v1/token
22+
23+
${EXPECTED_RESULT}
24+
... SEPARATOR=
25+
... {"discovered_items":2,"end_time":1765545358,"results":[{"hostname": "127.0.0.1", "id": 1, "name": \
26+
... "SharePoint Online", "score": 73.1244323342416, "total_users": 50}, {"hostname": "127.0.0.1", \
27+
... "id": 3, "name": "Outlook Online", "score": 81.551724137931, "total_users": 67}],"duration":0, \
28+
... "start_time":1765545358}
2629

2730

2831
*** Test Cases ***

tests/apps/monitoring/zscaler/zdx/api/list-locations.robot

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ Test Timeout 120s
1111
*** Variables ***
1212
${MOCKOON_JSON} ${CURDIR}${/}mockoon.json
1313
${CMD} ${CENTREON_PLUGINS}
14-
... --plugin=apps::monitoring::zscaler::zdx::api::plugin
15-
... --mode=list-locations
16-
... --hostname=${HOSTNAME}
17-
... --port=${APIPORT}
18-
... --proto=http
19-
... --key-id=1
20-
... --key-secret=1
14+
... --plugin=apps::monitoring::zscaler::zdx::api::plugin
15+
... --mode=list-locations
16+
... --hostname=${HOSTNAME}
17+
... --port=${APIPORT}
18+
... --proto=http
19+
... --client-id=1
20+
... --client-secret=1
21+
... --auth-url=http://127.0.0.1:${APIPORT}/oauth2/v1/token
2122

2223

2324
*** Test Cases ***

0 commit comments

Comments
 (0)