How to secure Vert.x 3.3 Eventbus Bridge with Keycloak - javascript

I'm currently writing a simple prototype Vert.x 3.3 application (Latest Github build using Jitpack.io) that tries to secure http endpoints and the eventbus with Keycloak.
I have two verticals and static index page with a button to send messages. Everything works fine unsecured but I don't know how to secure the SockJS eventbus bridge with Keycloak. Securing the http endpoint works fine.
Since Vert.x 3.3 is not officially released yet I haven't been able to find very much information.
http://vertx.io/blog/vertx-3-and-keycloak-tutorial/ only covered securing the http endpoint and the 3.21 documentation on requiring authorization is specifically for using the BasicAuthHandler which I'm not sure how to modify to work with Keycloak:
http://vertx.io/docs/vertx-web/java/#_requiring_authorisation_for_messages
Currently I have the following code:
public class VertxEventBusTest extends AbstractVerticle {
#Override
public void start(Future<Void> startFuture) throws Exception {
System.out.println("Primary Verticle Started");
Router router = Router.router(vertx);
HttpServer server = vertx.createHttpServer().requestHandler(router::accept);
OAuth2Auth oAuth2Auth = OAuth2Auth.createKeycloak(vertx, OAuth2FlowType.AUTH_CODE, getKeycloakJson());
OAuth2AuthHandler oAuth2AuthHandler = OAuth2AuthHandler.create(oAuth2Auth, "http://localhost:8091");
oAuth2AuthHandler.setupCallback(router.get("/callback"));
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
BridgeOptions bridgeOptions = new BridgeOptions();
bridgeOptions.addInboundPermitted(new PermittedOptions().setAddress("click"));
bridgeOptions.addOutboundPermitted(new PermittedOptions().setAddress("click"));
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options).bridge(bridgeOptions);
router.route("/").handler(oAuth2AuthHandler);
router.route("/eventbus/*").handler(oAuth2AuthHandler);
router.route("/eventbus/*").handler(sockJSHandler);
vertx.eventBus().<JsonObject>consumer("click", msg -> System.out.println("Msg Received on Verticle1: " + msg.body()));
router.route().handler(StaticHandler.create().setWebRoot("webroot"));
server.listen(8091, result -> {
if (result.succeeded()) {
startFuture.complete();
} else {
startFuture.fail(result.cause());
}
});
}
private JsonObject getKeycloakJson() {
return new JsonObject("{\n" +
" \"realm\": \"Prototype\",\n" +
" \"realm-public-key\": \"<public key>",\n" +
" \"auth-server-url\": \"http://localhost:8180/auth\",\n" +
" \"ssl-required\": \"external\",\n" +
" \"resource\": \"prototype-eventbus\",\n" +
" \"credentials\": {\n" +
" \"secret\": \"<secret>\"\n" +
" }\n" +
"}");
}
}
My Static Html is:
<head>
<meta charset="UTF-8">
<script src='sockjs-0.3.4.js'></script>
<script src='vertx-eventbus.js'></script>
<script src='index-js.js'></script>
<title>VERT.X Test</title>
</head>
<body onload="load()">
<!-- JavaScript includes. -->
<button onclick="zclicker()">Test Click</button>
</body>
</html>
Javascript:
var eb = new EventBus('http://localhost:8091/eventbus');
function load() {
eb.onopen = function () {
eb.registerHandler('click', function (data) {})
};
}
function zclicker() {
eb.publish('click', {
'clicked': true
});
}
When running the vertical there are no errors but chrome developer tools shows the following error after login through keycloak:
XMLHttpRequest cannot load http://localhost:8180/auth/realms/Prototype/protocol/openid-connect/auth?re…direct_uri%3D%2Feventbus%2Finfo&state=&client_id=prototype-eventbus&scope=. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8091' is therefore not allowed access.

That looks like a CORS issue.
By the looks of your error you might just be able to add the following header in the response:
Access-Control-Allow-Origin: *
or
Access-Control-Allow-Origin: http://localhost:8091
depending on how much you care about security.
That's usually sufficient for simple GET for text/html, but if you want to handle other content types (eg JSON) or PUT / or DELETE methods, then you'll have to add more headers to allow them.
Some resources:
http://enable-cors.org/server.html
http://www.html5rocks.com/en/tutorials/cors/

Related

How to pass an Access Token from .NET (code behind) to Javascript

I would like to "authorize" via the .NET framework using my own API keys, secret and Refresh Token, and then pass the Access Token to JavaScript so I can upload a video directly to YouTube with a progress indicator.
I have working code via the .NET API that will accomplish the upload directly to my channel [without authorization], but you get no progress indication (and it can take quite some time) and the file must be uploaded to my server first, then to YouTube server.
Is a server side access code different from a client side access code? If not:
How can I obtain the access code server side? (get the string)
***How can I pass it to Google APIs via JavaScript? (not how to write <%= access code %> but where to pass it in?)
I am well aware of the security risk in exposing the access token, but these do expire right? (as a bonus, how can I shorten the expire time). This is also being done inside a password protected web page, and you don't get the 'client secret' or 'refresh token'
***update - I think I found where to pass in the token, in the MediaUploader object.
var uploader = new MediaUploader({
baseUrl: 'https://www.googleapis.com/upload/youtube/v3/videos',
file: selectedFile,
token: token, // ***RIGHT HERE***
metadata: metadata,
params: params,
onError: function(data) {
Okay, so after weeks of smashing my way through the APIs, .NET, and JavaScript documentation I have built the following solution....
As per the YouTube V3 Developer documentation get your Keys all set up. (choose OAuth, Web Application, and enter URIs for both your Javascript and Redirect code)
Next use the OAuth Playground to obtain your Refresh Code
Once you have your client_id, client_secret, and refresh_token you are ready to rock!
This code makes a simple HTTP/REST call via the code behind to obtain an access_token which is good for 3600 seconds (the default). It then passes this string to the JavaScript code for use ****WARNING****
This does not use any of the .NET or JavaScript Libraries with the exception of one file, the cors_upload.js available on GitHub
Allas, the code
Default.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ApisCallTest.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta charset="utf-8" />
<title>YouTube API Uploads via CORS</title>
<style>
#disclaimer {
font-size: 0.75em;
color: #aeaeae;
max-width: 350px;
}
.during-upload { display: none; }
label { display: block; }
input[type="text"],
textarea,
progress {
font-size: 16px;
width: 15em;
margin-bottom: 1em;
padding: 0.5em;
font-family: "Open Sans", sans-serif;
}
</style>
</head>
<body>
<div>
<input input type="file" id="file" class="button" accept="video/*" />
<button id="button">Upload Video</button>
</div>
<div class="during-upload">
<p><span id="percent-transferred"></span>% done (<span id="bytes-transferred"></span>/<span id="total-bytes"></span> bytes)</p>
<progress id="upload-progress" max="1" value="0"></progress>
</div>
<p id="disclaimer">By uploading a video, you certify that you own all rights to the content or that you are authorized by the owner to make the content publicly available on YouTube, and that it otherwise complies with the YouTube Terms of Service located at http://www.youtube.com/t/terms</p>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="cors_upload.js"></script>
<script>
$('#button').on("click", goForUpload);
function goForUpload() {
if ($('#file').get(0).files[0]) {
$('#button').attr('disabled', true);
var uploadStartTime = 0;
var metadata = {
snippet: {
title: 'PUT YOU TITLE HERE',
description: 'THIS IS YOUR VIDEO DESCRIPTION',
categoryId: '22'
},
status: {
privacyStatus: 'unlisted'
}
};
var uploader = new MediaUploader({
baseUrl: 'https://www.googleapis.com/upload/youtube/v3/videos',
file: $('#file').get(0).files[0],
token: '<%= access_token %>',
metadata: metadata,
params: {
part: Object.keys(metadata).join(',')
},
onError: function (data) {
var message = data;
// Assuming the error is raised by the YouTube API, data will be
// a JSON string with error.message set. That may not be the
// only time onError will be raised, though.
try {
var errorResponse = JSON.parse(data);
message = errorResponse.error.message;
} finally {
alert(message);
}
},
onProgress: function (data) {
var currentTime = Date.now();
var bytesUploaded = data.loaded;
var totalBytes = data.total;
// The times are in millis, so we need to divide by 1000 to get seconds.
var bytesPerSecond = bytesUploaded / ((currentTime - this.uploadStartTime) / 1000);
var estimatedSecondsRemaining = (totalBytes - bytesUploaded) / bytesPerSecond;
var percentageComplete = (bytesUploaded * 100) / totalBytes;
$('#upload-progress').attr({
value: bytesUploaded,
max: totalBytes
});
$('#percent-transferred').text(percentageComplete);
$('#bytes-transferred').text(bytesUploaded);
$('#total-bytes').text(totalBytes);
$('.during-upload').show();
},
onComplete: function (data) {
var uploadResponse = JSON.parse(data);
alert('all done, you can store this id: ' + uploadResponse.id)
}
});
uploadStartTime = Date.now();
uploader.upload();
}
}
</script>
</body>
</html>
and Default.aspx.cs
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace ApisCallTest
{
public partial class WebForm1 : System.Web.UI.Page
{
private static readonly HttpClient client = new HttpClient();
public string access_token;
protected void Page_Load(object sender, EventArgs e)
{
var values = new Dictionary<string, string>
{
{ "client_id", "REPLACE_ME" },
{ "client_secret", "REPLACE_ME" },
{ "refresh_token", "REPLACE_ME" },
{ "grant_type", "refresh_token" }
};
var content = new FormUrlEncodedContent(values);
var response = client.PostAsync("https://www.googleapis.com/oauth2/v4/token", content);
string json = response.Result.Content.ReadAsStringAsync().Result;
dynamic obj = JObject.Parse(json);
access_token = obj.access_token;
}
}
}
The result... From a password protected web page I can have a user upload a video to MY channel, unlisted, and store the video ID to later embed that video back on my website.
as for the ****WARNING****, this is somewhat of a security concern as you are exposing (very directly) your PERSONAL access key to the "public". It only lasts for 1 hour, but gives whatever "scope" of access you are using to "anyone" It would be a good idea to try and obstuficate the key somewhat, and at the very least, don't do this on a publicly available page.

Power BI Embedded - Access Token

So I have some graphs in Power BI which I want to share with my clients.
I'm making a custom page here on my server and trying to embed those graphs using Power BI Embedded setup.
I'm following this link https://learn.microsoft.com/en-us/power-bi/developer/get-azuread-access-token
However, how do I get an access token via javascript API?
Generate EmbedToken is basically a REST API call. you can use NodeJs or AJAX to issue this request and get your EmbedToken.
For AAD auth, I can maybe refer you to ADAL.js: https://github.com/AzureAD/azure-activedirectory-library-for-js
which can help with auth against AAD
I don't think that's possible in Javascript at the moment. I tried to create access tokens in Javascript not a long time ago but failed to find a way to do that.
I ended up doing a bit of server side code (something like this https://learn.microsoft.com/en-us/power-bi/developer/walkthrough-push-data-get-token) and printed the access code to a hidden div. Then I grabbed the token with Javascript and continued with Javascript from there (created the embed token and embedded the report itself).
It might be possible to do a sort of Javascript solution with a proxy, but that's out of my expertise (the proxy has the server side code).
The only pure Javascript solution I'm aware of is the Publish to web -solution (https://learn.microsoft.com/en-us/power-bi/service-publish-to-web), but it's got some limitations and security issues.
I have already embed the Power BI reports into my web application. And also, i have faced problems while embedding report into my application but finally i embed the reports. Below is the code that will help you to get the Access token.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></title>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.12/js/adal.min.js"></script>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
window.config = {
instance: 'https://login.microsoftonline.com/',
tenant: 'common', //COMMON OR YOUR TENANT ID
clientId: '49df1bc7-db68-4fb4-91c0-6d93f770d1a4', //This is your client ID
redirectUri: 'https://login.live.com/oauth20_desktop.srf', //This is your redirect URI
callback: userSignedIn,
popUp: true
};
var ADAL = new AuthenticationContext(config);
function signIn() {
ADAL.login();
}
function userSignedIn(err, token) {
console.log('userSignedIn called');
if (!err) {
showWelcomeMessage();
ADAL.acquireToken("https://analysis.windows.net/powerbi/api", function(error, token) {
// Handle ADAL Error
if (error || !token) {
printErrorMessage('ADAL Error Occurred: ' + error);
return;
}
// Get TodoList Data
$.ajax({
type: "GET",
url: "https://api.powerbi.com/v1.0/myorg/datasets",
headers: {
'Authorization': 'Bearer ' + token,
},
}).done(function(data) {
console.log(data);
// Update the UI
$loading.hide();
}).fail(function() {
printErrorMessage('Error getting todo list data')
}).always(function() {
// Register Handlers for Buttons in Data Table
registerDataClickHandlers();
});
});
} else {
console.error("error: " + err);
}
}
function getDataSets() {
var trythis = "Bearer " + token;
var request = new XMLHttpRequest();
request.open('GET', 'https://api.powerbi.com/v1.0/myorg/datasets'); request.setRequestHeader('Authorization', trythis);
request.onreadystatechange = function() {
if (this.readyState === 4) {
console.log('Status:', this.status);
console.log('Body:', this.responseText);
}
};
request.send();
}
function showWelcomeMessage() {
var user = ADAL.getCachedUser();
var divWelcome = document.getElementById('WelcomeMessage');
divWelcome.innerHTML = "Welcome " + user.profile.name;
}
</script>
</head>
<body>
<button id="SignIn" onclick="signIn()">Sign In</button>
<h4 id="WelcomeMessage"></h4>
</body>
</html>
For more information you can go through the link that i will provide here.
Link : https://community.powerbi.com/t5/Developer/get-Access-token-using-js/m-p/352093#M10472

how to call webservices using ajax in asp.net mvc 5

I am working on a asp.net mvc 5 and I want to use web api in it and I use a jquery library and below code.but I didnot get proper answer .it returns always fail but I can see the output in the browser.please help me
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="js/jquery-1.12.4.js"></script>
<script>
function ftoken() {
$.ajax({
url: 'http://charter724.ir/webservice/get_city_a.php?id=367&key=EmlyCD8JOWX22cL8IuNBju5K5',
// data: { },
type: 'GET',
dataType: 'json',
success: function (data) {
alert('success')
// alert(data.data)
//if (country_id != "" && zone_code != "" && duration != "" && birthday != "") {
// fPrice(data.token);
//}
//$.each(data, function (idx, result) {
// alert(result.data);
//})
},
error: function (x, y, z) {
alert('fail')
//alert(x.responseText);
//alert(z);
}
});
}
</script>
</head>
<body>
<input type="submit" name="name" onclick="ftoken();" value="Click " />
</body>
</html>
I can see the output in the browser
You will not be able to make an AJAX call to this URL because of the Same Origin Policy(Yes you can copy and paste the URL in the browser and get a response, but you cannot access it from javascript).
Same origin policy states that AJAX calls will only be allowed if they are coming from the same domain, you are doing something like this:
making an AJAX call from http://localhost... to http://charter724... - this is not allowed
If you inspect the browser console you will see an error similar to the one below:
XMLHttpRequest cannot load http://charter724.ir/webservice/get_city_a.php?id=367&key=EmlyCD8JOWX22cL8IuNBju5K5. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:10585' is therefore not allowed access
The only way around this is for you to contact the company exposing the charter724 service and ask them to enable CORS(Cross Origin Resource Sharing) and allow all requests coming from your domain to access their service calls.
Alternatively you can access the service call using HttpClient and get the JSON response.If you really wanted to you could even expose your own set of services which talk to the external service and access your service calls from java script:
public async System.Threading.Tasks.Task<ActionResult> Index()
{
string url = "http://charter724.ir/webservice/get_city_a.php?id=367&key=EmlyCD8JOWX22cL8IuNBju5K5";
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return View();
}

How to embed yammer private messages on my website?

I know how to embed a feed which has a certain ID. I already did it. Now I'd like to implement the following functionality: If a user receives a private message, it will appear on an embedded feed. The best option in my opinion would be to embed the whole "chat window", but I didn't find a single code sample on the web. How can I do that?
You cannot really embed private messages like you can with feeds, because Yammer's REST APIs (incl. private messages) require authentication via OAuth 2.0. That means you have to create a Yammer API application which will ask your users to log in and allow you to access their messages. The overall concept of that described in their documentation here and here.
Yammer provides several SDKs you can use, one of them is the Javascript SDK. I pieced togehter a simple example of how you can ask users to log in and then it will display their private messages. Mind you, this is a very simple solution, I just tested it on a single one-to-one conversation.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" data-app-id="YOUR-APP-CLIENT-ID" src="https://c64.assets-yammer.com/assets/platform_js_sdk.js"></script>
</head>
<body>
<span id="yammer-login"></span>
<div id="messages"></div>
<script>
yam.connect.loginButton('#yammer-login', function (resp) {
if (resp.authResponse) {
document.getElementById('yammer-login').innerHTML = 'Welcome to Yammer!';
}
});
var msgdiv = document.querySelector("#messages");
yam.getLoginStatus(
function(response) {
if (response.authResponse) {
console.log("logged in");
var myId = response.authResponse.user_id;
yam.platform.request({
url: "messages/private.json",
method: "GET",
success: function (response) {
console.log("The request was successful.");
var usernames = {};
response.references.forEach(function(ref){
if(ref.type === "user") {
usernames[ref.id] = ref.full_name;
}
});
response.messages.forEach(function(message){
var msg = document.createElement("span");
msg.innerHTML = usernames[message.sender_id] + ": " + message.body.parsed + "<br/>";
msgdiv.appendChild(msg);
})
},
error: function (response) {
console.log("There was an error with the request.");
console.dir(private);
}
});
}
else {
console.log("not logged in")
}
}
);
</script>
</body>
</html>
The response from the messages/private.json API endpoint is a JSON file that you can go through. It includes information about the message and the users involved in the conversation.

Share cookies with other domains

I know that it's possible to allow other domains to read our domain cookie as long as they're sub domains of the same parent domain.
For example, intranet.abc.com and extranet.abc.com can allow cookies to be read by each other by specifying the domain property to .abc.com
Now, I'm really in need that I can allow other domains to read my domain cookie (they are not sub domains of the same domain). I have searched a lot of discussions on the internet => all say "NO" due to security issues. I'm not sure if I missed a solution out there because I don't see any security issues in this case. My server clearly ALLOWS this cookie to be read by an XYZ.COM domain because the cookie does not contain any sensitive information and XYZ.COM domain is my trusted domain,
In my opinion, there should be a way to specify a list of other domains that are allowed to read a particular cookie in our domain, just like CORS, the server can decide if the information should be available to some trusted domains.
Please tell me if it's possible without using a workaround and if so, how to do it?
If it's not possible, I really would like to know why.
Some information about what I'm implementing:
I'm implementing a file download and on client side I need to detect whether the download is complete by periodically checking for a download token in the cookie using an interval in javascript.
The logic of the current system I'm working on at the moment may store the files in 2 different servers. If the file is missing in the current server, it will download file in another server (another domain)
Thank you very much.
You can read off-domain cookies by opening an iframe to specially instrumented page on the other domain and using the window.postMessage API to communicate between windows. HTML5 only, obviously.
Simplifying the postMessage API somewhat for brevity, consult MDN developer pages for full details.
https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage
<iframe id="ifrm" src="http://other.domain.com/getCookie.html"></iframe>
<script>
var iframe = document.getElementById('ifrm');
window.addEventListener('message', function (e) {
if (e.source === iframe.contentWindow && e.origin === 'other.domain.com') {
var cookie = e.data;
//do something with cookie
}
});
//wait for the iframe to load...maybe ping it first...then
iframe.contentWindow.postMessage('give me the cookie:cookie name', 'other.domain.com');
</script>
/* in getCookie.html */
<script>
window.addEventListener('message', function (e) {
if (e.origin === 'your.domain.com') {
var soughtCookie = /give me the cookie\:(.*)/.exec(e.data)[1];
// read the cookie
var cookie = getCookieFn(soughtCookie)
e.source.postMessage(cookie.toString(), 'your.domain.com');
}
}, false);
</script>
you could have a backend web service which shares the contents of the cookie with the 3rd party, but then your server would have to hold the cookie value in session and have a session id that is some how shared with the other website.
Can also special page and redirection so that the cookie value is read and passed to your domain as a form submit.
Lets say your domain is yours.com and on page yours.com/page1 you set some cookie value.
Now xyz.com , another domain wants that value. xyz.com/somePage, redirects to yours.com/spl (along with parameter of the page to send user to say xyz.com/somePage2), Now yours.com/spl gets the cookie via JavaScript and then redirects to xyz.com/somePage2 passing the cookie value as a POST or a GET parameter.
Full working sample at http://sel2in.com/pages/prog/html/acrossSites/make.php (with a simple web service)
AJAX not example wont work but can do it with iframes.
Code :
coki.js (goes on the first site that wants to expose cookies)
function setCookie(cname,cvalue, daysExpire)
{
var d = new Date();
d.setTime(d.getTime()+(daysExpire * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + " ; path=/ ;"
}
function getCookie(cname)
{
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++)
{
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
wsa.php (goes on site 1). To make it more secure can check the calling page/ container URL and use a dynamic secret key.
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php
error_reporting(E_WARNING);
$d = $_REQUEST['s'];
if($d != "secret565"){
echo "Bad secret bye";
return;
}
$n = $_REQUEST['n'];
if($n == ""){
echo "No cookie name, bye";
return;
}
?>
<script src=coki.js>
</script>
<script >
n = '<?php echo "$n"?>'
v = getCookie(n)
//alert("For " + n + ", got :" + v + ".")
window.parent.gotVal(n, v)
</script>
getc.html
Goes on site 2, gets the value of cookie C1 or other cookie from site 1 via wsa.php, using an iframe. wsa.php reads the secret auth key and cookie name from its parameters, then calls a javascript function in containing page to pass back values
<form name=f1 action=ws.php method=post>
<h1>Get cookie from Javascript sample </h1>
http://sel2in.com/pages/prog/html/acrossSites/
<table>
<tr><td>Url from <td/><td> <input name=u1 value='wsa.php' size=100><td/></tr>
<tr><td>Cookie Name <td/><td> <input name=n value='C1'><td/></tr>
<tr><td>Secret <td/><td> <input name=s value='secret565'><td/></tr>
<tr><td><input type=button value='Go' onclick='s1do()' > <td/><td><td/></tr>
</table>
</form>
<div id = result>result here</div>
<div id = cc1>container</div>
v 2 c
<script>
function gotVal(n, v){
document.getElementById("result").innerHTML = "For " + n + ", got :" + v + "."
}
function s1do(){
document.getElementById("cc1").innerHTML = ""
n1 = document.f1.n.value
s1 = document.f1.s.value
url = document.f1.u1.value
qry = "s=" + escape(s1) + "&n=" + escape(n1)
s = "<iframe border=0 height =1 width=1 src=\"" + url + "?" + qry + "\" ></iframe>"
document.getElementById("cc1").innerHTML = s
}
</script>

Categories

Resources