b54ecc11fd88ce4f45ab95b0e9ffb163bfee2edd
[lhc/web/wiklou.git] / js2 / mwEmbed / libMwApi / mw.proxy.js
1 /*
2 *
3 * Api proxy system
4 *
5 * Built to support cross domain uploading, and api actions for a approved set of domains.
6 * mwProxy enables a system for fluid contributions across wikis domains for which your logged in.
7 *
8 * The framework support a request approval system for per-user domain approval
9 * and a central blacklisting of domains controlled by site or system administrators.
10 *
11 * Flow outline below:
12 *
13 * Domain A (lets say en.wiki)
14 * invokes add-media-wizard and wants to upload to domain B ( commons.wiki )
15 *
16 * Domain A loads iframe to domain B ? with request param to to insert from Domain A
17 * Domain B checks list of approved domains for (Domain A) & checks the user is logged in ( and if the same account name )
18 * if user is not logged in
19 * a _link_ to Domain B to new window login page is given
20 * if user has not approved domain and (Domain A) is not pre-approved
21 * a new page link is generated with a approve domain request
22 * if user approves domain it goes into their User:{username}/apiProxyDomains.js config
23 * If Domain A is approved we then:
24 * loads a "push" and "pull" iframe back to Domain A
25 (Domain B can change the #hash values of these children thereby proxy the data)
26 * Domain A now gets the iframe "loaded" callback a does a initial echo to confirm cross domain proxy
27 * echo sends "echo" to push and (Domain A) js passes the echo onto the "pull"
28 * Domain A now sends api requests to the iframe "push" child and gets results from the iframe "pull"
29 * api actions happen with status updates and everything and we can reuse existing api interface code
30 *
31 * if the browser supports it we can pass msgs with the postMessage API
32 * http://ejohn.org/blog/cross-window-messaging/
33 *
34 * @@todo it would be nice if this supported multiple proxy targets (ie to a bright widgets future)
35 *
36 */
37
38 loadGM({
39 "mwe-setting-up-proxy" : "Setting up proxy",
40 "mwe-re-try" : "Retry Api request",
41 "mwe-re-trying": "Retrying api request",
42 "mwe-cancel" : "Cancel",
43 "mwe-proxy-not-ready": "Proxy is not configured",
44 "mwe-please-login" : "Please <a target=\"_new\" href=\"$1\">login</a> on ($2) and or enable mwEmbed and retry the request",
45 "mwe-remember-loging": "As a general security reminder. Only login to web sites when your address bar displays that site's address"
46 });
47
48 (function( $ ) {
49 /**
50 * Base API Proxy object
51 *
52 */
53 $.proxy = {};
54
55 /**
56 * The client setup function:
57 */
58 $.proxy.client = function( pConf, conf ){
59 var _this = this;
60 //do setup:
61 if( pConf.server_frame )
62 $.proxy.server_frame = pConf.server_frame;
63
64 if( pConf.client_frame_path ){
65 $.proxy.client_frame_path = pConf.client_frame_path;
66 }else{
67 //guess the client frame path:
68 $.proxy.client_frame_path = wgScriptPath +'/js2/mwEmbed/libMwApi/NestedCallbackIframe.html';
69 }
70
71 if( parseUri( $.proxy.server_frame).host == parseUri( document.URL ).host ){
72 js_log("Error: why are you trying to proxy yourself? " );
73 return false;
74 }
75 return true;
76 }
77 //set the frameProxy Flag:
78 var frameProxyOk = false;
79 /* setup a iframe request hash */
80 $.proxy.doFrameProxy = function( reqObj ){
81 var hashPack = {
82 'cd': parseUri( document.URL ).host,
83 'cfp': $.proxy.client_frame_path,
84 'req': reqObj
85 }
86 js_log( "Do frame proxy request on src: \n" + $.proxy.server_frame + "\n" +
87 JSON.stringify( reqObj ) );
88 //we can't update src's so we have to remove and add all the time :(
89 //@@todo we should support frame msg system
90 $j('#frame_proxy').remove();
91 $j('body').append('<iframe style="display:none" id="frame_proxy" name="frame_proxy" ' +
92 'src="' + $.proxy.server_frame +
93 '#' + escape( JSON.stringify( hashPack ) ) +
94 '"></iframe>' );
95
96 //add an onLoad hook:
97 $j('#frame_proxy').get(0).onload = function(){
98 //add a 5 second timeout for setting up the nested child callback (after page load)
99 setTimeout(function(){
100 if( !frameProxyOk ){
101 //we timmed out no api proxy (should make sure the user is "logged in")
102 js_log("Error:: api proxy timeout are we logged in? mwEmbed is on?");
103 $.proxy.proxyNotReadyDialog();
104 }
105 }, 5000);
106 }
107 }
108 var lastApiReq = {};
109 $.proxy.proxyNotReadyDialog = function(){
110 var btn = {};
111 btn[ gM('mwe-re-try') ] = function(){
112 $j.addLoaderDialog( gM('mwe-re-trying') );
113 $.proxy.doFrameProxy( lastApiReq );
114 }
115 btn[ gM('mwe-cancel') ] = function(){
116 $j.closeLoaderDialog();
117 }
118 var pUri = parseUri( $.proxy.server_frame);
119 //this is sort of a temporary hack if we change the MediaWiki:ApiProxy key
120 //we will have to deal with that here as well:
121 var login_url = pUri.protocol +'://'+ pUri.host +
122 pUri.path.replace( 'MediaWiki:ApiProxy', 'Special:UserLogin');
123 $j.addDialog( gM('mwe-proxy-not-ready'), gM('mwe-please-login', [ login_url, pUri.host] ) +
124 '<p style="font-size:small">' + gM('mwe-remember-loging') + '</p>',
125 btn
126 )
127 }
128 /* the do_api_request with callback: */
129 $.proxy.doRequest = function( reqObj, callback){
130 js_log("doRequest:: " + JSON.stringify( reqObj ) );
131 lastApiReq = reqObj;
132 //setup the callback:
133 $.proxy.callback = callback;
134 //do the proxy req:
135 $.proxy.doFrameProxy( reqObj );
136 }
137 /**
138 * The nested iframe action that passes its msg back up to the top instance
139 */
140 $.proxy.nested = function( hashResult ){
141 //close the loader if present:
142 $j.closeLoaderDialog();
143 js_log( '$.proxy.nested callback :: ' + unescape( hashResult ) );
144 frameProxyOk = true;
145 //try to parse the hash result:
146 try{
147 var rObj = JSON.parse( unescape( hashResult ) );
148 }catch (e) {
149 js_log("Error could not parse hashResult");
150 }
151 //special callback to frameProxyOk flag (not an api result, don't call callback)
152 if( rObj.state == 'ok')
153 return ;
154
155 //if all good pass it to the callback:
156 $.proxy.callback( rObj );
157 }
158 /**
159 * The serverApiProxy handles the actual proxy
160 * and child frames pointing to the parent "blank" frames
161 *
162 * This is (Domain B) in the above described setup
163 */
164 $.proxy.server = function( pConf, callback){
165 /* clear the body of any html */
166 $j('body').html( 'proxy setup' );
167
168 //read the anchor action from the requesting url
169 var jmsg = unescape( parseUri( document.URL ).anchor );
170 try{
171 var aObj = JSON.parse( jmsg );
172 }catch ( e ){
173 js_log("ProxyServer:: could not parse anchor");
174 }
175 if( !aObj.cd ){
176 js_log("Error: no client domain provided ");
177 return false;
178 }
179
180 js_log("Setup server on: " + parseUri( document.URL ).host +
181 ' client from: ' + aObj.cd +
182 ' to nested target: ' + aObj.cfp );
183
184 // make sure we are logged in
185 // (its a normal mediaWiki page so all site vars should be defined)
186 if( !wgUserName ){
187 js_log('error Not logged in');
188 return false;
189 }
190
191 var domain = aObj.cd;
192 var nested_frame_src = 'http://' + aObj.cd + aObj.cfp;
193 //check the master whitelist
194 for(var i in pConf.master_whitelist){
195 if( domain == pConf.master_whitelist[ i ] ){
196 //do the request:
197 return doNestedProxy( aObj.req );
198 }
199 }
200 //check master blacklist
201 for(var i in pConf.master_blacklist ){
202 if( domain == pConf.master_blacklist ){
203 js_log('domain: ' + domain + ' is blacklisted');
204 return false;
205 }
206 }
207 //@@todo grab the users whitelist for our current domain
208 /*var local_api = wgScriptPath + '/index' + wgScriptExtension + '?title=' +
209 'User:' + wgUserName + '/apiProxyDomainList.js' +
210 '&action=raw&smaxage=0&gen=js';
211 $j.get( local_api, function( data ){
212 debugger;
213 });*/
214
215 //if still not found:
216 js_log("domain " + domain + " not approved");
217
218 //offer the user the ability to "approve" requested domain save to their user page
219
220 function doNestedProxy( reqObj ){
221 js_log("doNestedProxy to: " + nested_frame_src);
222 //output iframe prior to running api request
223 //(so we can fail faster / cache the nested page / simotaniusly load things)
224 doNestedFrame ( 'nested_ok' , {'state':'ok'});
225
226 var outputhash = escape( JSON.stringify( reqObj ) );
227 //add some api stuff:
228 reqObj['format'] = 'json';
229 //process the api request
230 $j.post(wgScriptPath + '/api' + wgScriptExtension,
231 reqObj,
232 function( data ){
233 //js_log("Proxy GOT Res: " + data );
234 //put it into the nested frame hash string:
235 doNestedFrame( 'nested_push', JSON.parse( data ) );
236 }
237 );
238 }
239 //add the doNestedFrame iframe:
240 function doNestedFrame( nestname, resultObj ){
241 $j('#nested_push').remove();
242 //setup the nested proxy that points back to top domain:
243 $j('body').append( '<iframe id="nested_push" name="nested_push" ' +
244 'src="'+ nested_frame_src +
245 '#' + escape( JSON.stringify( resultObj ) ) +
246 '"></iframe>' );
247 }
248 }
249
250 })(window.$mw);