Browse Source

init

master
Simon Vieille 8 months ago
commit
a75e5e12a9
No known key found for this signature in database
8 changed files with 489 additions and 0 deletions
  1. 2
    0
      .gitignore
  2. 290
    0
      index.js
  3. 17
    0
      package.json
  4. 59
    0
      src/console/input.js
  5. 37
    0
      src/console/output.js
  6. 53
    0
      src/cookie-bag.js
  7. 26
    0
      src/logger.js
  8. 5
    0
      src/preg-match.js

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
1
+login
2
+node_modules/

+ 290
- 0
index.js View File

@@ -0,0 +1,290 @@
1
+"use strict";
2
+
3
+var Crawler = require("crawler");
4
+var path = require('path');
5
+var output = new(require('./src/console/output'));
6
+var input = new(require('./src/console/input'))(process);
7
+var pregMatch = require('./src/preg-match');
8
+var logger = require('./src/logger.js');
9
+var cookieBag = new(require('./src/cookie-bag.js'));
10
+
11
+process.chdir(path.dirname(input.script));
12
+
13
+var isDebugMode = input.has('debug');
14
+
15
+if (!isDebugMode) {
16
+    process.on('uncaughtException', function() {
17
+        output.json({
18
+            error: true,
19
+            message: 'UncaughtException'
20
+        });
21
+
22
+        process.exit(1);
23
+    });
24
+}
25
+
26
+if (input.has('help')) {
27
+    output.write([
28
+        process.argv[0],
29
+        process.argv[1],
30
+        '[--crawler-debug]',
31
+        '[--crawler-info]',
32
+        '[--crawler-error]',
33
+    ].join(' '));
34
+
35
+    process.exit(1);
36
+}
37
+
38
+var handlerCrawlerError = function(response, e) {
39
+    output.json({
40
+        error: true,
41
+        message: {
42
+            body: response.body,
43
+            response: response,
44
+            error: e,
45
+        },
46
+    }, true);
47
+}
48
+
49
+var handlerCrawlerException = function(e, done) {
50
+    if (isDebugMode) {
51
+        throw e;
52
+    } else {
53
+        done();
54
+
55
+        return output.json({
56
+            error: true,
57
+            message: e
58
+        });
59
+    }
60
+}
61
+
62
+var headers = function(crawler) {
63
+    var datas = {
64
+        'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0',
65
+        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
66
+        'Accept-Language': 'fr,fr-FR;q=0.5',
67
+        'Cookie': cookieBag.toString(),
68
+    };
69
+
70
+    if (crawler.storage.hasOwnProperty('referer')) {
71
+        datas['referer'] = crawler.storage.referer;
72
+    }
73
+
74
+    return datas;
75
+}
76
+
77
+var createCrawlerOptions = function(config) {
78
+    var options = {
79
+        maxConnections: 10,
80
+        retries: 1,
81
+        logger: logger,
82
+        jQuery: false,
83
+    };
84
+
85
+    config = config || {};
86
+
87
+    for (var i in config) {
88
+        options[i] = config[i];
89
+    }
90
+
91
+    return options;
92
+}
93
+
94
+var createCrawler = function(options) {
95
+    options = createCrawlerOptions(options || {});
96
+
97
+    if (!options.hasOwnProperty('callback') && options.hasOwnProperty('onSuccess')) {
98
+        options.callback = function(error, response, done) {
99
+            if (error) {
100
+                handlerCrawlerError(response, error);
101
+            } else {
102
+                try {
103
+                    options.onSuccess(response, done);
104
+                } catch (e) {
105
+                    return handlerCrawlerException(e, done);
106
+                }
107
+            }
108
+
109
+            done();
110
+        }
111
+    }
112
+
113
+    var c = new Crawler(options);
114
+
115
+    c.storage = {};
116
+
117
+    c.runCrawl = function(url) {
118
+        c.options.headers = headers(c);
119
+
120
+        return c.queue(url);
121
+    };
122
+
123
+    return c;
124
+}
125
+
126
+try {
127
+    var UrlRefreshRequester = createCrawler({
128
+        onSuccess: function(response) {
129
+            cookieBag.handleResponse(response);
130
+
131
+            var urlRefresh = pregMatch(/"urlRefresh":"([^"]+)"/, response.body)[1];
132
+
133
+            LoginFormRequester.runCrawl(urlRefresh);
134
+        },
135
+    });
136
+
137
+    var LoginFormRequester = createCrawler({
138
+        onSuccess: function(response) {
139
+            cookieBag.handleResponse(response);
140
+
141
+            var urlPost = pregMatch(/"urlPost":"([^"]+)"/, response.body)[1];
142
+            var urlLogin = pregMatch(/"urlLogin":"([^"]+)"/, response.body)[1];
143
+
144
+            var data = {
145
+                canary: pregMatch(/"canary":"([^"]+)"/, response.body)[1],
146
+                ctx: pregMatch(/ctx=(.+)/, urlLogin)[1],
147
+                hpgrequestid: pregMatch(/"sessionId":"([^"]+)"/, response.body)[1],
148
+                flowToken: pregMatch(/"sFT":"([^"]+)"/, response.body)[1],
149
+                f1: 'Suivant',
150
+                NewUser: 1,
151
+                CookieDisclosure: 1,
152
+                fspost: 0,
153
+                IsFidoSupported: 0,
154
+                FoundMSAs: null,
155
+                i2: 1,
156
+                i13: 0,
157
+                i17: '',
158
+                i18: '',
159
+                i19: 19471,
160
+                ps: 2,
161
+                psRNGCDefaultType: '',
162
+                psRNGCEntropy: '',
163
+                psRNGCSLK: '',
164
+                PPSX: '',
165
+                FoundMSAs: '',
166
+                type: 11,
167
+                LoginOptions: 3,
168
+                Irt: '',
169
+                IrtPartition: '',
170
+                hisRegion: '',
171
+                hisScaleUnit: '',
172
+                loginfmt: process.env.MS_OFFICE365_LOGIN,
173
+                login: process.env.MS_OFFICE365_LOGIN,
174
+                passwd: process.env.MS_OFFICE365_PASSWORD,
175
+            }
176
+
177
+            LoginRequester.options.form = data;
178
+
179
+            LoginRequester.runCrawl('https://login.microsoftonline.com' + urlPost)
180
+        }
181
+    });
182
+
183
+    var LoginRequester = createCrawler({
184
+        method: 'POST',
185
+        onSuccess: function(response) {
186
+            cookieBag.handleResponse(response);
187
+
188
+            var data = {
189
+                canary: pregMatch(/"canary":"([^"]+)"/, response.body)[1],
190
+                ctx: pregMatch(/"sCtx":"([^"]+)"/, response.body)[1],
191
+                hpgrequestid: pregMatch(/"sessionId":"([^"]+)"/, response.body)[1],
192
+                flowToken: pregMatch(/"sFT":"([^"]+)"/, response.body)[1],
193
+                LoginOptions: 1,
194
+            }
195
+
196
+            KmsiRequester.options.form = data;
197
+
198
+            KmsiRequester.runCrawl('https://login.microsoftonline.com/kmsi');
199
+        }
200
+    });
201
+
202
+    var KmsiRequester = createCrawler({
203
+        method: 'POST',
204
+        jQuery: true,
205
+        onSuccess: function(response) {
206
+            cookieBag.handleResponse(response);
207
+
208
+            var data = {
209
+                code: response.$('[name="code"]').val(),
210
+                id_token: response.$('[name="id_token"]').val(),
211
+                session_state: response.$('[name="session_state"]').val(),
212
+                correlation_id: response.$('[name="correlation_id"]').val(),
213
+            };
214
+
215
+            SiteRequester.options.form = data;
216
+            SiteRequester.storage.referer = 'https://login.microsoftonline.com/kmsi';
217
+            SiteRequester.runCrawl(process.env.MS_OFFICE365_SITE + '_forms/default.aspx');
218
+        }
219
+    });
220
+
221
+    var SiteRequester = createCrawler({
222
+        method: 'POST',
223
+        jQuery: true,
224
+        onSuccess: function(response, done) {
225
+            cookieBag.handleResponse(response);
226
+
227
+            if (SiteRequester.storage.hasOwnProperty('finish')) {
228
+                output.json({
229
+                    rtFa: cookieBag.get('rtFa'),
230
+                    FedAuth: cookieBag.get('FedAuth'),
231
+                }, true);
232
+
233
+                return;
234
+            }
235
+
236
+            if (SiteRequester.storage.hasOwnProperty('kmsi')) {
237
+                delete SiteRequester.storage.kmsi;
238
+
239
+                SiteRequester.storage.finish = true;
240
+
241
+                return KmsiRequester.options.onSuccess(response);
242
+            }
243
+
244
+            if (response.headers.hasOwnProperty('location')) {
245
+                SiteRequester.options.method = 'GET';
246
+
247
+                var location = response.headers.location;
248
+
249
+                SiteRequester.runCrawl(location);
250
+            } else {
251
+                var urlPost = response.$('form').attr('action');
252
+
253
+                var data = {
254
+                    code: response.$('[name="code"]').val(),
255
+                    id_token: response.$('[name="id_token"]').val(),
256
+                    session_state: response.$('[name="session_state"]').val(),
257
+                    correlation_id: response.$('[name="correlation_id"]').val(),
258
+                };
259
+
260
+                var params = urlPost.split('?')[1].split('&');
261
+
262
+                params.forEach(function(param) {
263
+                    var name = pregMatch(/([a-zA-Z0-9_-]+)=/, param)[1];
264
+                    var value = pregMatch(/=(.+)/, param)[1];
265
+
266
+                    data[name] = decodeURIComponent(value);
267
+                });
268
+
269
+                SiteRequester.options.method = 'POST';
270
+                SiteRequester.options.form = data;
271
+                SiteRequester.storage.referer = 'https://login.microsoftonline.com/kmsi';
272
+                SiteRequester.storage.kmsi = true;
273
+                SiteRequester.runCrawl(urlPost);
274
+            }
275
+        },
276
+    });
277
+
278
+    UrlRefreshRequester.runCrawl(process.env.MS_OFFICE365_SITE);
279
+} catch (e) {
280
+    if (isDebugMode) {
281
+        throw e;
282
+    } else {
283
+        output.json({
284
+            error: true,
285
+            message: e
286
+        });
287
+    }
288
+
289
+    process.exit(1);
290
+}

+ 17
- 0
package.json View File

@@ -0,0 +1,17 @@
1
+{
2
+  "name": "sharepoint-webdav-oauth2-bot",
3
+  "version": "1.0.0",
4
+  "description": "sharepoint-webdav-oauth2-bot",
5
+  "main": "index.js",
6
+  "scripts": {
7
+    "test": "echo \"Error: no test specified\" && exit 1"
8
+  },
9
+  "author": "Simon Vieille <simon@deblan.fr>",
10
+  "license": "ISC",
11
+  "dependencies": {
12
+    "class.extend": "^0.9.2",
13
+    "crawler": "^1.1.2",
14
+    "minimist": "^1.2.0",
15
+    "trim": "0.0.1"
16
+  }
17
+}

+ 59
- 0
src/console/input.js View File

@@ -0,0 +1,59 @@
1
+"use strict";
2
+
3
+var Minimist = require('minimist');
4
+var Class = require('class.extend');
5
+
6
+var Input = Class.extend('Input', {
7
+    /**
8
+     * Constructor.
9
+     *
10
+     * @param object process
11
+     */
12
+    init: function(process) {
13
+        this.args = Minimist(process.argv.slice(2));
14
+        this.node = process.argv[0];
15
+        this.script = process.argv[1];
16
+    },
17
+
18
+    /**
19
+     * Return the value of the given name argument.
20
+     *
21
+     * @param string name
22
+     * @param mixed default
23
+     *
24
+     * @return mixed
25
+     */
26
+    get: function(name, defaultValue) {
27
+        if (this.has(name)) {
28
+            return this.args[name];
29
+        }
30
+
31
+        if (defaultValue !== undefined) {
32
+            return defaultValue;
33
+        }
34
+
35
+        return null;
36
+    },
37
+
38
+    /**
39
+     * Check the given argument name exists.
40
+     *
41
+     * @param string name
42
+     *
43
+     * @return boolean
44
+     */
45
+    has: function(name) {
46
+        return this.args.hasOwnProperty(name);
47
+    },
48
+
49
+    /**
50
+     * Return if args is empty.
51
+     *
52
+     * @return boolean
53
+     */
54
+    empty: function() {
55
+        return Object.keys(this.args).length === 1;
56
+    },
57
+});
58
+
59
+module.exports = Input;

+ 37
- 0
src/console/output.js View File

@@ -0,0 +1,37 @@
1
+"use strict";
2
+
3
+var Class = require('class.extend');
4
+
5
+var Output = Class.extend('Output', {
6
+    /**
7
+     * Convert and print data to json.
8
+     *
9
+     * @param mixed data
10
+     */
11
+    json: function(data, pretty) {
12
+        data = JSON.stringify(
13
+            data,
14
+            function(key, value) {
15
+                if (value === undefined) {
16
+                    return null;
17
+                }
18
+
19
+                return value;
20
+            },
21
+            pretty ? 2 : null
22
+        );
23
+
24
+        return this.write(data);
25
+    },
26
+
27
+    /**
28
+     * Print data.
29
+     *
30
+     * @param mixed data
31
+     */
32
+    write: function(data) {
33
+        console.log(data);
34
+    }
35
+});
36
+
37
+module.exports = Output;

+ 53
- 0
src/cookie-bag.js View File

@@ -0,0 +1,53 @@
1
+var trim = require('trim');
2
+
3
+var CookieBag = function() {
4
+    this.bag = [];
5
+}
6
+
7
+CookieBag.prototype.add = function(name, value) {
8
+    this.bag[name] = name + '=' + value;
9
+}
10
+
11
+CookieBag.prototype.get = function(name) {
12
+    var str = this.bag[name] || null;
13
+
14
+    if (!str) {
15
+        return;
16
+    }
17
+
18
+    var index = str.indexOf('=');
19
+
20
+    return str.substr(index + 1);
21
+}
22
+
23
+CookieBag.prototype.toString = function() {
24
+    var data = [];
25
+
26
+    for (var i in this.bag) {
27
+        data.push(this.bag[i]);
28
+    }
29
+
30
+    return data.join('; ');
31
+}
32
+
33
+CookieBag.prototype.handleResponse = function(response) {
34
+    var headers = response.headers || {};
35
+    var cookies = headers['set-cookie'] || [];
36
+    var that = this;
37
+
38
+    cookies.forEach(function(cookie) {
39
+        var def = cookie.split(';')[0];
40
+        var index = def.indexOf('=');
41
+
42
+        var name = def.substr(0, index);
43
+        var value = def.substr(index + 1);
44
+
45
+        that.add(name, value);
46
+    });
47
+}
48
+
49
+CookieBag.prototype.clean = function() {
50
+    this.bag = [];
51
+}
52
+
53
+module.exports = CookieBag;

+ 26
- 0
src/logger.js View File

@@ -0,0 +1,26 @@
1
+var output = new(require('./console/output'));
2
+var input = new(require('./console/input'))(process);
3
+
4
+var logCrawlerDebug = input.has('crawler-debug');
5
+var logCrawlerInfo = input.has('crawler-info');
6
+var logCrawlerError = input.has('crawler-error');
7
+
8
+var writeLog = function(level, message) {
9
+    return output.write('[' + level.toUpperCase() + '] ' + message);
10
+}
11
+
12
+module.exports = {
13
+    log: function(level, message) {
14
+        if (level === 'debug' && logCrawlerDebug) {
15
+            return writeLog(level, message);
16
+        }
17
+
18
+        if (level === 'info' && logCrawlerInfo) {
19
+            return writeLog(level, message);
20
+        }
21
+
22
+        if (level === 'error' && logCrawlerError) {
23
+            return writeLog(level, message);
24
+        }
25
+    },
26
+};

+ 5
- 0
src/preg-match.js View File

@@ -0,0 +1,5 @@
1
+var pregMatch = function(regex, content) {
2
+    return content.match(regex);
3
+}
4
+
5
+module.exports = pregMatch;

Loading…
Cancel
Save