UNPKG

5.12 kBJavaScriptView Raw
1'use strict';
2
3var qs = require('qs');
4let _ = require('underscore');
5let cheerio = require('cheerio-without-node-native');
6let IsomorphicFetch = require('real-isomorphic-fetch');
7
8const config = require('./const.js');
9var status = require('./status.js');
10
11const 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
15const getRandomServer = () => {
16 const servers = ['idaps1.uzh.ch', 'idaps2.uzh.ch'];
17 return servers[Math.floor(Math.random() * servers.length)];
18};
19
20let 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
35let 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
55let 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 /* eslint-disable camelcase */
64 j_username: username,
65 j_password: password,
66 _eventId_proceed: '',
67 _shib_idp_revokeConsent: 'true'
68 /* eslint-enable camelcase */
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
80let 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
119let 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; // eslint-disable-line no-unused-vars
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
150exports.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};