@take2identity/verosint 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/package.json +35 -0
- package/postinstall.js +280 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Take 2 Identity, Inc <support@verosint.com> (https://verosint.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# verosint
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[![Go Report Card][reportcard]](https://goreportcard.com/report/gitlab.com/443id/public/verosint)
|
|
5
|
+
|
|
6
|
+
`verosint` is an open source tool that enables command line access to Verosint identity security endpoints.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Use the `verosint` tool to:
|
|
11
|
+
|
|
12
|
+
* Evaluate risk scores or create a rule set for a single set of identifiers
|
|
13
|
+
(email, IP, and/or phone number).
|
|
14
|
+
* Determine risk by running a batch evaluation for identifiers using a
|
|
15
|
+
[CSV](https://en.wikipedia.org/wiki/Comma-separated_values),
|
|
16
|
+
[LDIF](https://www.rfc-editor.org/rfc/rfc2849.html) files.
|
|
17
|
+
* Send events to the SignalPrint endpoint to create a map of identities and access history.
|
|
18
|
+
* Generate an [LDAP Schema](https://www.rfc-editor.org/rfc/rfc4512#page-22)
|
|
19
|
+
file to store risk scoring and/or rules evaluation atttributes in user
|
|
20
|
+
entries.
|
|
21
|
+
|
|
22
|
+
Download the binary from:
|
|
23
|
+
[releases page](https://gitlab.com/443id/public/verosint/-/releases).
|
|
24
|
+
Binaries for macOS, Windows, and Linux are available.
|
|
25
|
+
|
|
26
|
+
### Alternative Installation methods
|
|
27
|
+
|
|
28
|
+
#### Node Package Manager (NPM)
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
npm i -g @take2identity/verosint
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Check permissions before installing the CLI tool. If installation
|
|
35
|
+
fails, run the command with "sudo" (MacOS/Linux), or run the command
|
|
36
|
+
inside a terminal using administrator privileges (Windows).
|
|
37
|
+
|
|
38
|
+
#### Using `go install`
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
go install gitlab.com/443id/public/verosint@latest
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Executing with a Docker container
|
|
45
|
+
|
|
46
|
+
Use the following command to run the CLI inside a Docker container
|
|
47
|
+
without having to install the tool on your system. This method
|
|
48
|
+
also enables running the CLI inside in a containerized
|
|
49
|
+
CI/CD pipeline.
|
|
50
|
+
|
|
51
|
+
```shell
|
|
52
|
+
docker run --rm registry.gitlab.com/443id/public/verosint
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Configuration for the CLI is stored in the `.verosint.yaml` file in the
|
|
56
|
+
home directory. To make the configuration accessible to the Docker
|
|
57
|
+
container, create a volume map for the configuration file. For example:
|
|
58
|
+
|
|
59
|
+
```shell
|
|
60
|
+
docker run --rm -v "$HOME/.verosint.yaml:/root/.verosint.yaml" registry.gitlab.com/443id/public/verosint
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
After installing the CLI, you can execute it using `verosint`.
|
|
66
|
+
|
|
67
|
+
Interacting with the Verosint APIs require an API key. You can pass the API
|
|
68
|
+
key using the `--apiKey` argument. It is recommended to store the API
|
|
69
|
+
key inside a configuration file or an environment variable to prevent
|
|
70
|
+
exposure.
|
|
71
|
+
|
|
72
|
+
You can get command help using the `--help` argument:
|
|
73
|
+
|
|
74
|
+
```shell
|
|
75
|
+
verosint --help
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Note that each subcommand has its own specific help. For example, to
|
|
79
|
+
print the help for the `evaluate rules-batch` subcommand, run:
|
|
80
|
+
|
|
81
|
+
```shell
|
|
82
|
+
verosint evaluate rules-batch --help
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Configuration File
|
|
86
|
+
|
|
87
|
+
The tool can read settings from a configuration file in a
|
|
88
|
+
[YAML](https://yaml.org/) format.
|
|
89
|
+
For example, you can store the API key as well as rules inside the file.
|
|
90
|
+
|
|
91
|
+
By default, the tool looks for the configuration file `.verosint.yaml` in
|
|
92
|
+
your home directory. You can override this setting with the
|
|
93
|
+
`--configFile` flag.
|
|
94
|
+
|
|
95
|
+
An example configuration file:
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
apiKey: <API key goes here>
|
|
99
|
+
rules:
|
|
100
|
+
- name: IP - Tor/Bot/VPN
|
|
101
|
+
outcomes:
|
|
102
|
+
- DENY
|
|
103
|
+
query: 'signals.ip.bot || signals.ip.tor || signals.ip.vpn'
|
|
104
|
+
reason: This IP address is a known bot, active Tor node, or a VPN
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### API key as an Environment Variable
|
|
108
|
+
|
|
109
|
+
You can also set the `VEROSINT_APIKEY` environment variable to hold the value
|
|
110
|
+
of the API key:
|
|
111
|
+
|
|
112
|
+
```shell
|
|
113
|
+
export VEROSINT_APIKEY="API key goes here"
|
|
114
|
+
verosint evaluate risk ip:172.66.43.186
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Evaluating Risk or Rules
|
|
118
|
+
|
|
119
|
+
#### Single Set of Identifiers
|
|
120
|
+
|
|
121
|
+
The CLI accepts the following identifiers.
|
|
122
|
+
|
|
123
|
+
| Name | Description |
|
|
124
|
+
| ---- | ----------- |
|
|
125
|
+
| ip | IP address (either IPv4 or IPv6) |
|
|
126
|
+
| email | Email address |
|
|
127
|
+
| phone | Phone number in the [international phone number format](https://en.wikipedia.org/wiki/E.164) |
|
|
128
|
+
|
|
129
|
+
When providing a set of identifiers, the API expects one or more
|
|
130
|
+
identifiers (IP address, email, or phone number) and only one value per
|
|
131
|
+
identifier.
|
|
132
|
+
|
|
133
|
+
IPv6 addresses may compress zeros for a shorter form and use
|
|
134
|
+
representations as described in [RFC 5952](https://www.rfc-editor.org/rfc/rfc5952).
|
|
135
|
+
|
|
136
|
+
Responses are provided in the JSON format on the standard output.
|
|
137
|
+
|
|
138
|
+
##### Examples
|
|
139
|
+
|
|
140
|
+
###### 1. Evaluate an IPv4 address for risk
|
|
141
|
+
|
|
142
|
+
```shell
|
|
143
|
+
verosint evaluate risk ip:172.66.43.186
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
###### 2. Evaluate an IPv6 address, a phone number and an email address for risk
|
|
147
|
+
|
|
148
|
+
```shell
|
|
149
|
+
verosint evaluate risk ip:2607:fb91:1296:c7dc:a0c4:25a9:ac7a:4384 email:user@example.com phone:15123944240
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
###### 3. Evaluate an IPv4 for address against a rule set already defined in the configuration
|
|
153
|
+
|
|
154
|
+
To obtain the UUID of the rule set, visit the
|
|
155
|
+
[Rules](https://app.verosint.com/rules) configuration and copy the UUID of
|
|
156
|
+
the rule set you would like to evaluate.
|
|
157
|
+
|
|
158
|
+

|
|
159
|
+
|
|
160
|
+
```shell
|
|
161
|
+
verosint evalute rule ip:104.255.6.45 --ruleSetUuid 4f5ab21b-984c-455e-b889-b6b0272a4567
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
###### 4. Evaluate an Email address against a rule set provided using a local file
|
|
165
|
+
|
|
166
|
+
You can export a rule set defined in the [Rules](https://app.verosint.com/rules)
|
|
167
|
+
configuration into a local file as shown below.
|
|
168
|
+
|
|
169
|
+

|
|
170
|
+
|
|
171
|
+
This example evaluates the `babs@jensen.com` email address against the rule defined
|
|
172
|
+
in the `mfarule.json` file.
|
|
173
|
+
|
|
174
|
+
```shell
|
|
175
|
+
verosint evaluate rule email:babs@jensen.com --rulesSetFile mfarule.json
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
##### 5. Check if an IP address is within 100 kilometers of Austin, TX using a local rule set
|
|
179
|
+
|
|
180
|
+
You can place rules inside a local configuration file. The following is
|
|
181
|
+
an example file that uses the `isWithin` function, which enables you to
|
|
182
|
+
create rules for [geo-fencing](https://en.wikipedia.org/wiki/Geo-fence)
|
|
183
|
+
purposes:
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
apiKey: <API key goes here>
|
|
187
|
+
rules:
|
|
188
|
+
- name: IP not in Austin
|
|
189
|
+
outcomes:
|
|
190
|
+
- DENY
|
|
191
|
+
query: '!signals.ip.geo.isWithin(30.3079827, -97.895826, 100)'
|
|
192
|
+
reason: This IP is not within 100 kilometers of Austin, Texas
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The following example runs the same rule saved in the default
|
|
196
|
+
configuration file against the `104.16.44.99` IP address:
|
|
197
|
+
|
|
198
|
+
```shell
|
|
199
|
+
verosint evaluate rules ip:104.16.44.99
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Batch Evaluation
|
|
203
|
+
|
|
204
|
+
Use input files to evaluate multiple sets of identifiers for risk or
|
|
205
|
+
against rules. Batch commands can use input and output files in CSV or
|
|
206
|
+
LDIF format, and can produce a report file formatted as JSON.
|
|
207
|
+
|
|
208
|
+
Processing time may take much longer with larger files, than for a
|
|
209
|
+
single set of identifiers.
|
|
210
|
+
|
|
211
|
+
##### Batch Evaluation Examples
|
|
212
|
+
|
|
213
|
+
###### 1. Executing Batch Risk Evaluation using CSV input/output format
|
|
214
|
+
|
|
215
|
+
The input file does not have to be fully populated for all identifiers.
|
|
216
|
+
For example, the following CSV-formatted input file is valid without a
|
|
217
|
+
phone number present on the last record:
|
|
218
|
+
|
|
219
|
+
```csv
|
|
220
|
+
ip,email,phone
|
|
221
|
+
104.16.44.99,babs@jensen.com,15123944240
|
|
222
|
+
2607:fb91:1296:c7dc:a0c4:25a9:ac7a:4384,alison@example.com,
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
To evaluate the same file and save it as `myRecords.csv`, run the
|
|
226
|
+
following command:
|
|
227
|
+
|
|
228
|
+
```shell
|
|
229
|
+
verosint evaluate risk-batch --inputFile myRecords.csv \
|
|
230
|
+
--outputFile riskOutput.csv --reportFile riskReport.json
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
###### 2. Executing Batch Rules Evaluation with column index and LDIF output
|
|
234
|
+
|
|
235
|
+
If using an input file that has multiple identifiers, you can provide a
|
|
236
|
+
column index (where 0 refers to the first column) to indicate where the
|
|
237
|
+
value of a particular identifier is present. This content is saved in
|
|
238
|
+
`mapped.csv`.
|
|
239
|
+
|
|
240
|
+
```csv
|
|
241
|
+
ipaddress,mail,telephone
|
|
242
|
+
104.16.44.99,babs@jensen.com,15123944240
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Using the rules present in the default configuration file, the
|
|
246
|
+
following command generates LDIF-formatted output and JSON report
|
|
247
|
+
files:
|
|
248
|
+
|
|
249
|
+
```shell
|
|
250
|
+
verosint evaluate rules-batch ip:0 email:1 phone:2 \
|
|
251
|
+
--inputFile mapped.csv \
|
|
252
|
+
--outputFile output.ldif --outputType ldif \
|
|
253
|
+
--reportFile report.json
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Submitting SignalPrint Events
|
|
257
|
+
|
|
258
|
+
You can submit events to the SignalPrint endpoint using the
|
|
259
|
+
`signalprint send-events` subcommand. This subcommand works like the
|
|
260
|
+
batch commands for risk and rules. An event timestamp, IP address, and
|
|
261
|
+
user agent is required for each event. You can optionally add an
|
|
262
|
+
accountId, email, and/or phone with each event.
|
|
263
|
+
|
|
264
|
+
The timestamp is expected to be in the
|
|
265
|
+
[RFC3339](https://www.rfc-editor.org/rfc/rfc3339) format. For example:
|
|
266
|
+
`2019-01-22T03:56:17+03:30`.
|
|
267
|
+
|
|
268
|
+
##### Examples for Signal Print
|
|
269
|
+
|
|
270
|
+
Using the following event information saved in `events.csv`,
|
|
271
|
+
|
|
272
|
+
```csv
|
|
273
|
+
timestamp,ip,accountId,email,phone,userAgent
|
|
274
|
+
2019-01-22T03:56:17-05:00,104.16.44.99,babs_jensen,babs@jensen.com,15123944240,"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
you can send the event to SignalPrint with:
|
|
278
|
+
|
|
279
|
+
```csv
|
|
280
|
+
verosint signalprint send-events --inputFile events.csv
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Generating LDAP Schema
|
|
284
|
+
|
|
285
|
+
This command prints an LDAP schema that allows you to enrich existing
|
|
286
|
+
user entries in your directory with risk or rule evaluation data from
|
|
287
|
+
the API. The enterprise number (59592) in the object identifiers (OIDs)
|
|
288
|
+
is registered with the
|
|
289
|
+
[Internet Assigned Numbers Authority](https://www.iana.org/assignments/enterprise-numbers/).
|
|
290
|
+
This guarantees that the schema will not collide with your existing
|
|
291
|
+
schema elements.
|
|
292
|
+
|
|
293
|
+
Note that generating the LDAP schema does *not* require an API key. To
|
|
294
|
+
generate a schema, run the following command:
|
|
295
|
+
|
|
296
|
+
```shell
|
|
297
|
+
verosint generate schema
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Development
|
|
301
|
+
|
|
302
|
+
## Prerequisites
|
|
303
|
+
|
|
304
|
+
### Go
|
|
305
|
+
|
|
306
|
+
Install the 1.20 version of [Go](https://golang.dev).
|
|
307
|
+
|
|
308
|
+
### Task
|
|
309
|
+
|
|
310
|
+
The [task](https://taskfile.dev/installation/) utility is required to
|
|
311
|
+
execute the various build related tasks.
|
|
312
|
+
|
|
313
|
+
### Testing
|
|
314
|
+
|
|
315
|
+
Unit tests can be executed using the `task test:unit` command. Our goal
|
|
316
|
+
is to maintain a minimum of 70% coverage.
|
|
317
|
+
|
|
318
|
+
## Issues
|
|
319
|
+
|
|
320
|
+
Report issues at [Verosint Support](mailto:support@verosint.com)
|
|
321
|
+
|
|
322
|
+
[reportcard]: https://goreportcard.com/badge/gitlab.com/443id/public/verosint
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@take2identity/verosint",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Official CLI to interact with Verosint API",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"postinstall": "node postinstall.js install",
|
|
8
|
+
"preuninstall": "node postinstall.js uninstall"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://gitlab.com/443id/public/verosint.git"
|
|
13
|
+
},
|
|
14
|
+
"author": "Verosint",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://gitlab.com/443id/public/verosint/-/issues"
|
|
18
|
+
},
|
|
19
|
+
"goBinary": {
|
|
20
|
+
"name": "verosint",
|
|
21
|
+
"path": "./bin",
|
|
22
|
+
"url": "https://gitlab.com/443id/public/verosint/-/releases/v{{version}}/downloads/verosint_"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"postinstall.js"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://gitlab.com/443id/public/verosint#readme",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"mkdirp": "^2.1.5",
|
|
31
|
+
"request": "^2.88.2",
|
|
32
|
+
"tar": "^6.1.13",
|
|
33
|
+
"unzipper": "0.10.11"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2023 Take 2 Identity, Inc
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const { join } = require('path');
|
|
8
|
+
const { exec } = require('child_process');
|
|
9
|
+
const { existsSync, chmodSync, copyFileSync, unlinkSync, createWriteStream, readFileSync } = require('fs');
|
|
10
|
+
const mkdirp = require('mkdirp');
|
|
11
|
+
const request = require('request');
|
|
12
|
+
const tar = require('tar');
|
|
13
|
+
const zlib = require('zlib');
|
|
14
|
+
const unzipper = require('unzipper');
|
|
15
|
+
|
|
16
|
+
// Mapping from Node's `process.arch` to Our binary arch standard
|
|
17
|
+
const ARCH_MAPPING = {
|
|
18
|
+
"x64": "x86_64",
|
|
19
|
+
"x32": "i386",
|
|
20
|
+
"arm64": "arm64",
|
|
21
|
+
"amd64": "x86_64"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Mapping between Node's `process.platform` to Our binary platform standard
|
|
25
|
+
const PLATFORM_MAPPING = {
|
|
26
|
+
"darwin": "MacOS",
|
|
27
|
+
"linux": "Linux",
|
|
28
|
+
"win32": "Windows",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function getInstallationPath(callback) {
|
|
32
|
+
|
|
33
|
+
// `npm bin` will output the path where binary files should be installed
|
|
34
|
+
exec('npm bin', (err, stdout, stderr) => {
|
|
35
|
+
let dir = null;
|
|
36
|
+
if (err || stderr || !stdout || stdout.length === 0) {
|
|
37
|
+
|
|
38
|
+
// We couldn't infer path from `npm bin`. Let's try to get it from
|
|
39
|
+
// Environment variables set by NPM when it runs.
|
|
40
|
+
// npm_config_prefix points to NPM's installation directory where `bin` folder is available
|
|
41
|
+
// Ex: /Users/foo/.nvm/versions/node/v4.3.0
|
|
42
|
+
const env = process.env;
|
|
43
|
+
|
|
44
|
+
if (env && env.npm_config_prefix) {
|
|
45
|
+
dir = join(env.npm_config_prefix, 'bin');
|
|
46
|
+
} else {
|
|
47
|
+
return callback(new Error('Error finding binary installation directory'));
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
dir = stdout.trim();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dir = dir.replace(/node_modules.*[\/\\]\.bin/, join('node_modules', '.bin'));
|
|
54
|
+
mkdirp.sync(dir);
|
|
55
|
+
callback(null, dir);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function verifyAndPlaceBinary(binName, binPath, callback) {
|
|
60
|
+
if (!existsSync(join(binPath, binName))) {
|
|
61
|
+
return callback(`Downloaded binary does not contain the binary specified in configuration - ${binName}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getInstallationPath((err, installationPath) => {
|
|
65
|
+
if (err) {
|
|
66
|
+
return callback(err);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Move the binary file and make sure it is executable
|
|
70
|
+
copyFileSync(join(binPath, binName), join(installationPath, binName));
|
|
71
|
+
unlinkSync(join(binPath, binName));
|
|
72
|
+
chmodSync(join(installationPath, binName), '755');
|
|
73
|
+
|
|
74
|
+
console.log('Placed binary on', join(installationPath, binName));
|
|
75
|
+
|
|
76
|
+
callback(null);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function validateConfiguration(packageJson) {
|
|
81
|
+
|
|
82
|
+
if (!packageJson.version) {
|
|
83
|
+
return "'version' property must be specified";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!packageJson.goBinary || typeof(packageJson.goBinary) !== "object") {
|
|
87
|
+
return "'goBinary' property must be defined and be an object";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!packageJson.goBinary.name) {
|
|
91
|
+
return "'name' property is necessary";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!packageJson.goBinary.path) {
|
|
95
|
+
return "'path' property is necessary";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!packageJson.goBinary.url) {
|
|
99
|
+
return "'url' property is required";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function parsePackageJson() {
|
|
104
|
+
if (!(process.arch in ARCH_MAPPING)) {
|
|
105
|
+
console.error("Installation is not supported for this architecture: " + process.arch);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!(process.platform in PLATFORM_MAPPING)) {
|
|
110
|
+
console.error("Installation is not supported for this platform: " + process.platform);
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const packageJsonPath = join(".", "package.json");
|
|
115
|
+
if (!existsSync(packageJsonPath)) {
|
|
116
|
+
console.error("Unable to find package.json. " +
|
|
117
|
+
"Please run this script at root of the package you want to be installed");
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let packageJson = JSON.parse(readFileSync(packageJsonPath));
|
|
122
|
+
let error = validateConfiguration(packageJson);
|
|
123
|
+
if (error && error.length > 0) {
|
|
124
|
+
console.error("Invalid package.json: " + error);
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// We have validated the config. It exists in all its glory
|
|
129
|
+
let binName = packageJson.goBinary.name;
|
|
130
|
+
let binPath = packageJson.goBinary.path;
|
|
131
|
+
let url = packageJson.goBinary.url;
|
|
132
|
+
let version = packageJson.version;
|
|
133
|
+
|
|
134
|
+
// Binary name on Windows has .exe suffix
|
|
135
|
+
if (process.platform === "win32") {
|
|
136
|
+
binName += ".exe";
|
|
137
|
+
url = url.replace(/{{win_ext}}/g, '.exe');
|
|
138
|
+
} else {
|
|
139
|
+
url = url.replace(/{{win_ext}}/g, '');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
url = url.replace(/{{version}}/g, version);
|
|
143
|
+
|
|
144
|
+
switch(PLATFORM_MAPPING[process.platform]) {
|
|
145
|
+
case "MacOS":
|
|
146
|
+
url = url + "MacOS_all.tar.gz";
|
|
147
|
+
break;
|
|
148
|
+
case "Linux":
|
|
149
|
+
url = url + "Linux_" + ARCH_MAPPING[process.arch] + ".tar.gz";
|
|
150
|
+
break;
|
|
151
|
+
case "Windows":
|
|
152
|
+
url = url + "Windows_" + ARCH_MAPPING[process.arch] + ".zip";
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
console.error(`You have an unsupported platform (${process.platform}) or architecture (${process.arch})`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
binName: binName,
|
|
161
|
+
binPath: binPath,
|
|
162
|
+
url: url,
|
|
163
|
+
version: version
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function unzip({ opts, req, onSuccess, onError }) {
|
|
168
|
+
const unzip = unzipper.Extract({ path: opts.binPath });
|
|
169
|
+
unzip.on('error', onError);
|
|
170
|
+
unzip.on('close', onSuccess);
|
|
171
|
+
req.pipe(unzip);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function untar({ opts, req, onSuccess, onError }) {
|
|
175
|
+
const ungz = zlib.createGunzip();
|
|
176
|
+
const untar = tar.Extract({ path: opts.binPath });
|
|
177
|
+
ungz.on('error', onError);
|
|
178
|
+
untar.on('error', onError);
|
|
179
|
+
untar.on('end', onSuccess);
|
|
180
|
+
req.pipe(ungz).pipe(untar);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Move strategy for binary resources without compression.
|
|
185
|
+
*/
|
|
186
|
+
function move({ opts, req, onSuccess, onError }) {
|
|
187
|
+
const stream = createWriteStream(join(opts.binPath, opts.binName));
|
|
188
|
+
stream.on('error', onError);
|
|
189
|
+
stream.on('close', onSuccess);
|
|
190
|
+
req.pipe(stream);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function getStrategy({ url }) {
|
|
194
|
+
if (url.endsWith('.tar.gz')) {
|
|
195
|
+
return untar;
|
|
196
|
+
} else if (url.endsWith('.zip')) {
|
|
197
|
+
return unzip;
|
|
198
|
+
} else {
|
|
199
|
+
return move;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Reads the configuration from application's package.json,
|
|
205
|
+
* validates properties, downloads the binary, untars, and stores at
|
|
206
|
+
* ./bin in the package's root. NPM already has support to install binary files
|
|
207
|
+
* specific locations when invoked with "npm install -g"
|
|
208
|
+
*
|
|
209
|
+
* See: https://docs.npmjs.com/files/package.json#bin
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
function install(callback) {
|
|
213
|
+
|
|
214
|
+
const opts = parsePackageJson();
|
|
215
|
+
if (!opts) return callback('Invalid inputs');
|
|
216
|
+
|
|
217
|
+
mkdirp.sync(opts.binPath);
|
|
218
|
+
|
|
219
|
+
console.log('Downloading from URL: ' + opts.url);
|
|
220
|
+
|
|
221
|
+
const req = request({ uri: opts.url });
|
|
222
|
+
req.on('error', () => callback('Error downloading from URL: ' + opts.url));
|
|
223
|
+
req.on('response', (res) => {
|
|
224
|
+
if (res.statusCode !== 200) return callback('Error downloading binary. HTTP Status Code: ' + res.statusCode);
|
|
225
|
+
|
|
226
|
+
const strategy = getStrategy(opts);
|
|
227
|
+
|
|
228
|
+
strategy({
|
|
229
|
+
opts,
|
|
230
|
+
req,
|
|
231
|
+
onSuccess: () => verifyAndPlaceBinary(opts.binName, opts.binPath, callback),
|
|
232
|
+
onError: callback
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
function uninstall(callback) {
|
|
239
|
+
const { binName } = parsePackageJson();
|
|
240
|
+
getInstallationPath((err, installationPath) => {
|
|
241
|
+
if (err) {
|
|
242
|
+
return callback(err);
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
unlinkSync(join(installationPath, binName));
|
|
246
|
+
} catch(ex) {
|
|
247
|
+
// Ignore errors when deleting the file.
|
|
248
|
+
}
|
|
249
|
+
return callback(null);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
// Parse command line arguments and call the right method
|
|
256
|
+
let actions = {
|
|
257
|
+
"install": install,
|
|
258
|
+
"uninstall": uninstall
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
let argv = process.argv;
|
|
262
|
+
if (argv && argv.length > 2) {
|
|
263
|
+
let cmd = process.argv[2];
|
|
264
|
+
if (!actions[cmd]) {
|
|
265
|
+
console.log("Invalid command to postinstall script. `install` and `uninstall` are the only supported commands");
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
actions[cmd](function(err) {
|
|
270
|
+
if (err) {
|
|
271
|
+
console.error(err);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
} else {
|
|
274
|
+
process.exit(0);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
} else {
|
|
278
|
+
console.log('No command supplied. `install` and `uninstall` are the only supported commands');
|
|
279
|
+
exit(1);
|
|
280
|
+
}
|