1 | 'use strict';
|
2 |
|
3 | var qs = require('qs');
|
4 | let _ = require('underscore');
|
5 | let cheerio = require('cheerio-without-node-native');
|
6 | let IsomorphicFetch = require('real-isomorphic-fetch');
|
7 |
|
8 | const config = require('./const.js');
|
9 | var status = require('./status.js');
|
10 |
|
11 | const makeShibbolethUrl = server => {
|
12 | return `https://${server}/Shibboleth.sso/DS?providerId=https%3A%2F%2Faai-idp.uzh.ch%2Fidp%2Fshibboleth&target=https%3A%2F%2F${server}%2Fuzh%2Fworld%2Fcm%2Fstudium%2Fzcm_svmb1a%2Fmb101.do`;
|
13 | };
|
14 |
|
15 | const getRandomServer = () => {
|
16 | const servers = ['idaps1.uzh.ch', 'idaps2.uzh.ch'];
|
17 | return servers[Math.floor(Math.random() * servers.length)];
|
18 | };
|
19 |
|
20 | let zerothRequest = function (fetch) {
|
21 | return new Promise(function (resolve) {
|
22 | fetch('http://idagreen.uzh.ch/re/')
|
23 | .then(response => response.text())
|
24 | .then(body => {
|
25 | const match = body.match(/var\sserver\s=\s\"([0-9a-zA-Z\.]+)"/);
|
26 | if (match) {
|
27 | return resolve(makeShibbolethUrl(match[1]));
|
28 | }
|
29 | return resolve(makeShibbolethUrl(getRandomServer()));
|
30 | })
|
31 | .catch(() => resolve(makeShibbolethUrl(getRandomServer())));
|
32 | });
|
33 | };
|
34 |
|
35 | let firstRequest = function (url, fetch) {
|
36 | return new Promise(function (resolve, reject) {
|
37 | fetch(url, {
|
38 | credentials: 'include'
|
39 | })
|
40 | .then(response => response.text())
|
41 | .then(body => {
|
42 | if (body.indexOf('You don\'t have permission to access the requested object') > -1) {
|
43 | return reject(new Error('Anfrage wurde blockiert'));
|
44 | }
|
45 | var url = body.match(/action="(.*?)"/);
|
46 | if (!url) {
|
47 | throw new Error(`URL has no action: ${url}`);
|
48 | }
|
49 | resolve(url[1]);
|
50 | })
|
51 | .catch(reject);
|
52 | });
|
53 | };
|
54 |
|
55 | let secondRequest = function (fetch, username, password, url) {
|
56 | return new Promise(function (resolve, reject) {
|
57 | fetch('https://aai-idp.uzh.ch' + url, {
|
58 | headers: {
|
59 | 'User-Agent': config.USER_AGENT,
|
60 | 'Content-Type': 'application/x-www-form-urlencoded'
|
61 | },
|
62 | body: qs.stringify({
|
63 |
|
64 | j_username: username,
|
65 | j_password: password,
|
66 | _eventId_proceed: '',
|
67 | _shib_idp_revokeConsent: 'true'
|
68 |
|
69 | }),
|
70 | method: 'POST',
|
71 | redirect: 'manual',
|
72 | credentials: 'include'
|
73 | })
|
74 | .then(response => response.text())
|
75 | .then(resolve)
|
76 | .catch(reject);
|
77 | });
|
78 | };
|
79 |
|
80 | let thirdRequest = function (body, fetch) {
|
81 | return new Promise(function (resolve, reject) {
|
82 | let _$ = cheerio.load(body);
|
83 | let _action = _$('form')[0].attribs.action;
|
84 | let _data = {};
|
85 | _.each(_$('form input'), function (_input) {
|
86 | if (!_input.attribs.name) {
|
87 | return;
|
88 | }
|
89 | if (_input.attribs.name === '_eventId_AttributeReleaseRejected') {
|
90 | return;
|
91 | }
|
92 | if (_input.attribs.value === '_shib_idp_rememberConsent') {
|
93 | return;
|
94 | }
|
95 | if (_.isString(_data[_input.attribs.name])) {
|
96 | _data[_input.attribs.name] = [_data[_input.attribs.name], _input.attribs.value];
|
97 | } else if (_.isArray(_data[_input.attribs.name])) {
|
98 | _data[_input.attribs.name].push(_input.attribs.value);
|
99 | } else {
|
100 | _data[_input.attribs.name] = _input.attribs.value;
|
101 | }
|
102 | });
|
103 | fetch('https://aai-idp.uzh.ch' + _action, {
|
104 | body: qs.stringify(_data, {indices: false}),
|
105 | headers: {
|
106 | 'User-Agent': config.USER_AGENT,
|
107 | 'Content-Type': 'application/x-www-form-urlencoded'
|
108 | },
|
109 | method: 'POST',
|
110 | redirect: 'manual',
|
111 | credentials: 'include'
|
112 | })
|
113 | .then(response => response.text())
|
114 | .then(resolve)
|
115 | .catch(reject);
|
116 | });
|
117 | };
|
118 |
|
119 | let fourthRequest = function (body, fetch) {
|
120 | let $ = cheerio.load(body);
|
121 | return new Promise(function (resolve, reject) {
|
122 | var data = {};
|
123 | _.map($('form input'), function (input) {
|
124 | if (!input.attribs.name) {
|
125 | return;
|
126 | }
|
127 | data[input.attribs.name] = input.attribs.value;
|
128 | });
|
129 | try {
|
130 | let a = $('form')[0].attribs.action;
|
131 | } catch (err) {
|
132 | return reject(new Error('Der UZH-Server hat eine unbekannte Antwort gegeben. Du kannst uns unter info@bestande.ch kontaktieren.'));
|
133 | }
|
134 | fetch($('form')[0].attribs.action, {
|
135 | method: 'POST',
|
136 | body: qs.stringify(data),
|
137 | headers: {
|
138 | 'User-Agent': config.USER_AGENT,
|
139 | 'Content-Type': 'application/x-www-form-urlencoded'
|
140 | },
|
141 | redirect: 'manual',
|
142 | credentials: 'include'
|
143 | })
|
144 | .then(response => response.text())
|
145 | .then(resolve)
|
146 | .catch(reject);
|
147 | });
|
148 | };
|
149 |
|
150 | exports.get = (username, password, fetch, feedback) => {
|
151 | fetch = new IsomorphicFetch(fetch);
|
152 | return new Promise(function (resolve, reject) {
|
153 | feedback('CONTACT_UZH_CH');
|
154 | zerothRequest(fetch)
|
155 | .then(url => firstRequest(url, fetch))
|
156 | .then(function (url) {
|
157 | feedback('LOGGING_IN');
|
158 | return secondRequest(fetch, username, password, url);
|
159 | })
|
160 | .then(function (body) {
|
161 | feedback('LOGGED_IN');
|
162 | if (status.loginFailed(body)) {
|
163 | return reject(new Error('USERNAME_PW_WRONG'));
|
164 | }
|
165 | if (status.usernameUnknown(body)) {
|
166 | return reject(new Error('USERNAME_UNKNOWN'));
|
167 | }
|
168 | if (status.isStale(body)) {
|
169 | return reject(new Error('STALE_REQUEST'));
|
170 | }
|
171 | return thirdRequest(body, fetch);
|
172 | })
|
173 | .then(function (html) {
|
174 | feedback('LOADING_MODULES');
|
175 | return fourthRequest(html, fetch);
|
176 | })
|
177 | .then(html => resolve({success: true, html}))
|
178 | .catch(reject);
|
179 | });
|
180 | };
|