Skip to content

Commit 2d5a9ff

Browse files
authored
Merge pull request #14 from NETWAYS/oauth2
Add OAuth2 Client Credentials as auth method
2 parents 37f43f1 + 7ee68bf commit 2d5a9ff

File tree

3 files changed

+120
-31
lines changed

3 files changed

+120
-31
lines changed

doc/03-Configuration.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,29 @@ Per default, no filter limitation is applied to the query. The whole table will
2727

2828
To filter, you will need to follow the [official filter syntax from ServiceNow](https://www.servicenow.com/docs/bundle/yokohama-platform-user-interface/page/use/using-lists/concept/c_EncodedQueryStrings.html) An example could be `nameSTARTSWITHlnux`.
2929

30-
### Authentification methods
30+
### Authentication methods
3131

32-
Currently supported authentification methods:
32+
Currently supported authentication methods:
3333

3434
HTTP Basic Auth:
3535

36-
**ServiceNow API Username:** User to authenticate against the ServiceNow API.
36+
- **ServiceNow API Username:** User to authenticate against the ServiceNow API.
3737

3838
This user needs to have read access to the table you want to import.
3939

40-
**ServiceNow API Password:** Password to authenticate against the ServiceNow API.
40+
- **ServiceNow API Password:** Password to authenticate against the ServiceNow API.
4141

4242
API Token:
4343

44-
**ServiceNow API Token:** Bearer token to authenticate against the ServiceNow API. Used in the header key `x-sn-apikey`.
44+
- **ServiceNow API Token:** Bearer token to authenticate against the ServiceNow API. Used in the header key `x-sn-apikey`.
45+
46+
OAuth2 Client Credentials:
47+
48+
- **ServiceNow Client ID:** ID for the credentials
49+
- **ServiceNow Client Secret:** Secret for the credentials
50+
- **ServiceNow Scopes:** Scopes for the access token
51+
52+
This method uses the ServiceNow `/oauth_token.do` endpoint to request an access token.
53+
Currently, the access token is not stored during multiple requests.
54+
55+
Note, this requires the System Property: `glide.oauth.inbound.client.credential.grant_type.enabled`

library/Servicenowimport/Api/Servicenow.php

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,36 @@ public function __construct(string $baseUri, bool $tlsVerify = true, int $timeou
4141
if ($method === 'BEARER') {
4242
$c['headers']['x-sn-apikey'] = $auth['token'];
4343
}
44+
45+
if ($method === 'OAUTH') {
46+
// Note: We currently don't store the token between requests
47+
$accessToken = $this->fetchAccessToken($baseUri, $auth);
48+
$c['headers']['Authorization'] = 'Bearer ' . $accessToken;
49+
}
4450
} catch (\Exception $e) {
4551
throw new RuntimeException(sprintf("Failed to set authentication method %s", $e->getMessage()));
4652
}
4753

48-
try {
49-
$proxyAddress = parse_url($auth['proxy_address']);
50-
$proxyHost = $proxyAddress['host'];
51-
$proxyScheme = 'https';
52-
$proxyPort = 443;
53-
54-
if ($proxyType === 'HTTP') {
55-
$proxyScheme = $proxyAddress['scheme'] ?? 'https';
56-
$defaultPort = $proxyScheme === 'http' ? 80 : 443;
57-
$proxyPort = $proxyAddress['port'] ?? $defaultPort;
58-
} elseif ($proxyType === 'SOCKS5') {
59-
$proxyScheme = 'socks5';
60-
$proxyPort = $proxyAddress['port'] ?? 1028;
61-
}
54+
if ($proxyType !== '') {
55+
try {
56+
$proxyAddress = parse_url($auth['proxy_address']);
57+
$proxyHost = $proxyAddress['host'];
58+
$proxyScheme = 'https';
59+
$proxyPort = 443;
6260

63-
$c['proxy'] = sprintf('%s://%s:%s@%s:%d', $proxyScheme, $auth['proxy_user'], $auth['proxy_password'], $proxyHost, $proxyPort);
64-
} catch (\Exception $e) {
65-
throw new RuntimeException(sprintf("Failed to set proxy method %s", $e->getMessage()));
61+
if ($proxyType === 'HTTP') {
62+
$proxyScheme = $proxyAddress['scheme'] ?? 'https';
63+
$defaultPort = $proxyScheme === 'http' ? 80 : 443;
64+
$proxyPort = $proxyAddress['port'] ?? $defaultPort;
65+
} elseif ($proxyType === 'SOCKS5') {
66+
$proxyScheme = 'socks5';
67+
$proxyPort = $proxyAddress['port'] ?? 1028;
68+
}
69+
70+
$c['proxy'] = sprintf('%s://%s:%s@%s:%d', $proxyScheme, $auth['proxy_user'], $auth['proxy_password'], $proxyHost, $proxyPort);
71+
} catch (\Exception $e) {
72+
throw new RuntimeException(sprintf("Failed to set proxy method %s", $e->getMessage()));
73+
}
6674
}
6775

6876
$this->client = new Client($c);
@@ -84,4 +92,35 @@ public function request(string $endpoint, array $params = [], string $method = "
8492
throw new RuntimeException(sprintf("Request failed: %s", $e->getMessage()));
8593
}
8694
}
95+
96+
/**
97+
* fetchAccessToken gets a new access token from the authorization server.
98+
*/
99+
private function fetchAccessToken(string $baseUri, array $oauthConfig): string
100+
{
101+
$c = new Client(['base_uri' => $baseUri]);
102+
103+
$params = [
104+
'grant_type' => 'client_credentials',
105+
'client_id' => $oauthConfig['client_id'],
106+
'client_secret' => $oauthConfig['client_secret'],
107+
];
108+
109+
if (array_key_exists('scope', $oauthConfig)) {
110+
$params['scope'] = $oauthConfig['scope'];
111+
}
112+
113+
try {
114+
$response = $c->post('/oauth_token.do', ['form_params' => $params]);
115+
$data = json_decode($response->getBody(), true);
116+
} catch (\Exception $e) {
117+
throw new RuntimeException(sprintf("Request failed: %s", $e->getMessage()));
118+
}
119+
120+
if (array_key_exists('access_token', $data)) {
121+
return $data['access_token'];
122+
}
123+
124+
throw new RuntimeException(sprintf("No access token in response", $data));
125+
}
87126
}

library/Servicenowimport/ProvidedHook/Director/ImportSource.php

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ protected static function addAuthOptions(QuickForm $form)
2828
'text',
2929
'servicenow_username',
3030
[
31-
'label' => 'ServiceNow API username',
31+
'label' => 'API username',
3232
'required' => true,
3333
'description' => 'Username to authenticate at the ServiceNow API',
3434
]
@@ -38,7 +38,7 @@ protected static function addAuthOptions(QuickForm $form)
3838
'password',
3939
'servicenow_password',
4040
[
41-
'label' => 'ServiceNow API password',
41+
'label' => 'API password',
4242
'required' => true,
4343
'renderPassword' => true,
4444
'description' => 'Password to authenticate at the ServiceNow API',
@@ -51,13 +51,46 @@ protected static function addAuthOptions(QuickForm $form)
5151
'password',
5252
'servicenow_token',
5353
[
54-
'label' => 'ServiceNow API token',
54+
'label' => 'API token',
5555
'required' => true,
5656
'renderPassword' => true,
5757
'description' => 'Token to authenticate at the ServiceNow API',
5858
]
5959
);
6060
}
61+
62+
if ($method === 'OAUTH') {
63+
$form->addElement(
64+
'text',
65+
'servicenow_oauth_client_id',
66+
[
67+
'label' => 'OAuth Client ID',
68+
'required' => true,
69+
'description' => 'Client ID for the credentials',
70+
]
71+
);
72+
73+
$form->addElement(
74+
'password',
75+
'servicenow_oauth_client_secret',
76+
[
77+
'label' => 'OAuth Client Secret',
78+
'required' => true,
79+
'renderPassword' => true,
80+
'description' => 'Credentials for the client ID',
81+
]
82+
);
83+
84+
$form->addElement(
85+
'text',
86+
'servicenow_oauth_scope',
87+
[
88+
'label' => 'OAuth scopes',
89+
'required' => false,
90+
'description' => 'Scopes for the access token',
91+
]
92+
);
93+
}
6194
}
6295

6396
/**
@@ -72,6 +105,7 @@ public static function addSettingsFormFields(QuickForm $form)
72105
'servicenow_url',
73106
[
74107
'label' => 'ServiceNow API URL',
108+
'placeholder' => 'https://example.service-now.com',
75109
'required' => true,
76110
'description' => 'ServiceNow API URL. Full-qualified URL including protocol and domain (e.g. https://example.service-now.com)',
77111
]
@@ -82,6 +116,7 @@ public static function addSettingsFormFields(QuickForm $form)
82116
'servicenow_endpoint',
83117
[
84118
'label' => 'ServiceNow CMDB table endpoint',
119+
'placeholder' => 'api/now/table/cmdb_ci_computer',
85120
'required' => true,
86121
'description' => 'API endpoint to fetch objects from (e.g.: api/now/table/cmdb_ci_computer)',
87122
]
@@ -91,11 +126,12 @@ public static function addSettingsFormFields(QuickForm $form)
91126
'select',
92127
'servicenow_authmethod',
93128
[
94-
'label' => 'ServiceNow API authentification method',
95-
'description' => 'Authentification method for the ServiceNow API',
129+
'label' => 'API authentication method',
130+
'description' => 'Authentication method to use for the API',
96131
'multiOptions' => [
97132
'BASIC' => 'Basic Auth',
98133
'BEARER' => 'API Token',
134+
'OAUTH' => 'ServiceNow OAuth2 Client Credentials',
99135
],
100136
'class' => 'autosubmit',
101137
'required' => true,
@@ -108,7 +144,8 @@ public static function addSettingsFormFields(QuickForm $form)
108144
'text',
109145
'servicenow_timeout',
110146
[
111-
'label' => 'ServiceNow API timeout',
147+
'label' => 'API timeout',
148+
'placeholder' => '20',
112149
'required' => false,
113150
'description' => 'Timeout in seconds used to query data from ServiceNow. Default is 20.',
114151
]
@@ -119,6 +156,7 @@ public static function addSettingsFormFields(QuickForm $form)
119156
'servicenow_columns',
120157
[
121158
'label' => 'ServiceNow columns',
159+
'placeholder' => 'name,ip_address',
122160
'required' => false,
123161
'description' => 'Comma separated list of columns to fetch. Leave empty to fetch all columns.',
124162
]
@@ -197,12 +235,15 @@ private function getRecordsFromTable(): array
197235
// Set endpoint
198236
$endpoint = sprintf('%s?sysparm_display_value=true', $this->getSetting('servicenow_endpoint'));
199237

200-
201238
$auth = [
202239
'method' => $this->getSetting('servicenow_authmethod'),
203240
'token' => $this->getSetting('servicenow_token'),
204241
'username' => $this->getSetting('servicenow_username'),
205242
'password' => $this->getSetting('servicenow_password'),
243+
'token_url' => $this->getSetting('servicenow_oauth_token_url'),
244+
'client_id' => $this->getSetting('servicenow_oauth_client_id'),
245+
'client_secret' => $this->getSetting('servicenow_oauth_client_secret'),
246+
'scope' => $this->getSetting('servicenow_oauth_scope'),
206247
'proxy_type' => $this->getSetting('proxy_type'),
207248
'proxy_address' => $this->getSetting('proxy'),
208249
'proxy_user' => $this->getSetting('proxy_user'),
@@ -226,8 +267,6 @@ private function getRecordsFromTable(): array
226267
]
227268
);
228269

229-
230-
231270
return json_decode($result)->result;
232271
}
233272

0 commit comments

Comments
 (0)