I want to display file (which is stored on google drive) in iframe, but file should be displayed on our website's domain only. I don't want that anyone who has the URL of file can have access to file.
I am giving file permission on uploading file like:
public static Permission FilePermissions(DriveService service, String fileId)
{
Permission Permission = new Permission();
Permission.Type = "anyone";
Permission.Role = "reader";
try
{
return service.Permissions.Create(Permission, fileId).Execute();
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
return null;
}
And I am displaying file like:
<iframe id="iframe1" runat="server" width="100%" height="600px" src=""https://drive.google.com/file/d/{fileID}/preview"></iframe>
This code works but anyone can grab URL from iframe and have access to file. Is there anyway we can impose permissions to only allow access to file if it is being loaded from an iframe within specific website domain name (e.g. http://www.company.com)?
Related
I can restrict the usage of the Web App but I cannot limit the access to the images referred in the <img> tag.
Now I am developing a Web application with Google Apps Script.
This is an internal application of the corporation and I got to set the assets' access rights carefully.
For App itself, from Deploy as web app box of the script editor, I set Execute the app as Me (bar#foo.example.com) and Who has access to the app as Anyone within FOO corporation.
(Here let's say I belong to the FOO corporation. My Google account is bar#foo.example.com)
Now, when I login with the Google account of FOO, I could successfully access the Web App.
But when I didn't login, I couldn't access it.
That's what I want.
But when I set <img> tag to show the jpeg file in the Google Drive, I got to set the Share of the image file as Anyone on the internet with this link can view.
When I set <img> tag in the HTML in the Web App project and set the Share of the jpeg file as FOO Corporation -- Anyone in this group with this link can view, the <img> tag will not work (Error 403 will be returned).
I want to restrict the images' access right as well as the web app.
How can I do it?
How to reproduce
put jpeg file
put any JPEG (let's say it is baz.jpeg) file in the Google Drive
right-click and select Share
set the Get link as FOO Corporation -- Anyone in this group with this link can view
double click baz.jpeg to preview it
select ︙ => Open in new window
let's say the URL was https://drive.google.com/file/d/xxxx_object ID_xxxx/view. record the object ID (here xxxx_object ID_xxxx)
create a new untitled script project
create an index.html file and add the code:
<html>
<head>
<base target="_top">
</head>
<body>
<img src="https://drive.google.com/uc?id=xxxx_object ID_xxxx" width="30" height="30" alt="error alt text">
</body>
</html>
create Code.gs
change Code.gs as following code
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
Save it
publish it as Web App
Select menu item Publish > Deploy as web app
In Deploy as web app box, set Project version: as New
Set Execute the app as: as Me (bar#FOO.example.com)
Set Who has access the app: as Anyone within FOO corporation
Click Deploy
Click the text latest code in the message Test web app for your latest code
the result -- error
the webpage index.html is displayed
the image is not displayed. the icon of the broken picture is displayed
the alt text "error alt text" is displayed
if I right-click the icon of the broken picture and select Open in the new tab then it will open Error 403 (Forbidden) tab. The content is as follows:
Google (logo)
403. That’s an error.
We're sorry, but you do not have access to this page. That’s all we know.
change the access right of the image file
In Google Drive, find the image file baz.jpeg
right-click and select Share
set the Get link as Anyone on the internet with this link can view.
Open the tab of the Web App with the broken picture icon and reload
You see the correct image
What I want to do?
I want to set the access right of the image restricted as well as the Web app (only the user of FOO corporation can access).
How can I do it?
403 Forbidden
The /uc endpoint, when the file permission is set to "in this group", returns a 403 Forbidden response even if you are logged in the G Suite account.
Workaround
You can implement a flow of dynamically appending an HTMLImageElement with src attribute set to image data (base-64 encoded string from bytes). With this, you can restrict access to both Web App and the image and still be able to load it.
When deploying the Web App, make sure that the deployment has sufficient access to the file, for example, when the file has "Anyone in this group with this link can view" permissions:
Execute the app as: Me
Who has access to the app: Anyone within [org]
Below is a small proof of concept, including a server-side utility and a sample HTML file.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const asyncGAPIv2 = ({
funcName,
onFailure = console.error,
onSuccess,
params = []
}) => {
return new Promise((res, rej) => {
google.script.run
.withSuccessHandler(data => {
typeof onSuccess === "function" && onSuccess(data);
res(data);
})
.withFailureHandler(error => {
typeof onFailure === "function" && onFailure(error);
rej(error);
})
[funcName].apply(null, params);
});
};
const fetchAndAppendImage = async ({ parent = document.body, id }) => {
const data = await asyncGAPIv2({
funcName: "getImageFromDrive",
params: [{ id, token }]
});
const img = document.createElement("img");
img.src = data;
parent.append(img);
};
(async () => await fetchAndAppendImage({ id: "id here" }))();
</script>
</body>
</html>
You can pass the id to a server-side utility, get the file with getFileById (native authentication flow will ensure the requester will not get access to a file they do not have access to), and form an image data string by doing the following:
Extract raw bytes from the File instance by changing getBlob to getBytes.
Use the base64Encode method of the Utilities service to convert bytes to a base-64 encoded string and prepend data:image/png;base64, (Data URL scheme). If your images have another MIME-type, amend accordingly.
/**
* #summary gets an image from Google Drive
*
* #param {{
* id : string
* }}
*
* #returns {string}
*/
const getImageFromDrive = ({ id }) => {
try {
const file = DriveApp.getFileById(id);
const bytes = file.getBlob().getBytes();
const data = `data:image/png;base64,${Utilities.base64Encode(bytes)}`;
return data;
} catch (error) {
console.warn(error);
return "";
}
};
Notes
For the above to work, you must have the newer V8 runtime enabled.
It is possible that the reason is being logged in into multiple accounts, as trying to access a file via /uc endpoint with "in this group" permission while being only logged in to a G Suite account has no authorization issues.
From Oleg Valter's answer and discussions with him, as a method, I would like to propose to use Templated HTML. The origin of these methods is Oleg Valter's answer. When your script is modified, it becomes as follows.
Google Apps Script side:
From:
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
To:
function doGet() {
const id = "###"; // <--- Please set the file ID of the image file.
const html = HtmlService.createTemplateFromFile('index');
html.image = getImageFromDrive({id: id}); // <--- This function is from Oleg Valter's answer.
return html.evaluate();
}
HTML side:
From:
<img src="https://drive.google.com/uc?id=xxxx_object ID_xxxx" width="30" height="30" alt="error alt text">
To:
<img src="<?!= image ?>" width="30" height="30" alt="error alt text">
Note:
The function of getImageFromDrive by Oleg Valter's answer is used.
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
Reference:
HTML Service: Templated HTML
I've started using the GMail API and it's working fine on my local machine; it will open the Google permissions page and I can select my account. It then stores the return json token and only asks again if this token is removed.
When I publish to the server, the OAUTH page is never displayed and the application appears to timeout with a 'Thread was being aborted' exception.
My code;
try
{
using (var stream = new FileStream(HttpContext.Current.Server.MapPath("~/credentials/client_id.json"), FileMode.Open, FileAccess.Read))
{
string credPath = HttpContext.Current.Server.MapPath("~/credentials/gmail_readonly_token.json");
_credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
db.writeLog("INFO", "Gmail Credentials Saved","Credential file saved to: " + credPath);
}
// Create Gmail API service.
service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = _credential,
});
}
catch (Exception e)
{
db.writeLog("Error", "Failure when creating Gmail class", e.Message, null, _username, null);
}
Is there something I need to change within the 'client_id.json' (formally client_secret.json) file? The only thing I have altered is the redirect_uris line.
Any other suggestions would be welcome, the only other question I could find that is similar is here but there is no answer.
Thanks,
Danny.
The first one worked because you followed the intended use case, which is client-side. But, to implement authorization on the server, follow the Implementing Server-Side AUthorization guide.
I'm using the following when trying to open a local file:
some document
When I click the above in a browser, it opens Finder to the folder. But does not open the file. Should I be doing something else to have the file open in Numbers?
You cannot open local files on the client. This would be a huge security risk.
You can link to files on your server (like you did) or you can ask the client for a file using <input type="file">
You can only open some types of files in browsers, like html css js and mp4, otherwise the browser will want to download it. Also remember that browsers replace spaces with %20. I recommend right clicking the file and opening it with chrome then copy that link and using it.
You can open files that are local as long as it is a file that is on the file that is trying to open another file is local.
Your issue is likely the space in the document name. Try this instead:
some document
The %20 will be read by your browser as a space.
Update
The other answer points out something I missed. The .numbers extension will not be able to be opened directly by your browser. Additionally the other answer describes the security risk this could create.
The File API in HTML 5 now allows you to work with local files directly from JS (after basic user interaction in selecting the file(s), for security).
From the Mozilla File API docs:
"The File interface provides information about files and allows JavaScript in a web page to access their content.
File objects are generally retrieved from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation's DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement."
For more info and code examples, see the sample demo linked from the same article.
This might not be what you're trying to do, but someone out there may find it helpful:
If you want to share a link (by email for example) to a network file you can do so like this:
file:///Volumes/SomeNetworkFolder/Path/To/file.html
This however also requires that the recipient connects to the network folder in finder --- in menu bar,
Go > Connect to Server
enter server address (e.g. file.yourdomain.com - "SomeNetworkFolder" will be inside this directory) and click Connect. Now the link above should work.
Here is the alternative way to download local file by client side and server side effort:
<a onclick='fileClick(this)' href="file://C:/path/to/file/file.html"/>
js:
function fileClick(a) {
var linkTag = a.href;
var substring = "file:///";
if (linkTag.includes(substring)) {
var url = '/v/downloadLocalfile?path=' +
encodeURIComponent(linkTag);
fileOpen(url);
}
else {
window.open(linkTag, '_blank');
}
}
function fileOpen(url) {
$.ajax({
url: url,
complete: function (jqxhr, txt_status) {
console.log("Complete: [ " + txt_status + " ] " + jqxhr);
if (txt_status == 'success') {
window.open(url, '_self');
}
else {
alert("File not found[404]!");
}
// }
}
});
}
Server side[java]:
#GetMapping("/v/downloadLocalfile")
public void downloadLocalfile(#RequestParam String path, HttpServletResponse
response) throws IOException, JRException {
try {
String nPath = path.replace("file:///", "").trim();
File file = new File(nPath);
String fileName = file.getName();
response.setHeader("Content-Disposition", "attachment;filename=" +
fileName);
if (file.exists()) {
FileInputStream in = new FileInputStream(file);
response.setStatus(200);
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int numBytesRead;
while ((numBytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, numBytesRead);
}
// out.flush();
in.close();
out.close();
}
else {
response.setStatus(404);
}
} catch (Exception ex) {
logger.error(ex.getLocalizedMessage());
}
return;
}
You can expose your entire file system in your browser by using an http server.
caddy2 server
caddy file-server --listen :2022 --browse --root /
serves the root file system at http://localhost:2022/
python3 built-in server
python3 -m http.server
serves current dir on http://localhost:8000/
python2 built-in server
python3 -m SimpleHTTPServer
serves current dir on http://localhost:8000/
This s
Since I am unable to capture browser window close event using the GoogleWebAuthorizationBroker.AuthorizeAsync API, I followed this link (http://www.daimto.com/google-api-and-oath2/) to create an embedded browser and authenticate the user. I am unable to continue further to use the access token to upload a file in google drive. Is there any example available to continue from the above link to upload/download a file from Google Drive.
Regards,
Amrut
From the same author, there is a documentation how to upload/ download files to Google Drive.
Like with most of the Google APIs you need to be authenticated in order to connect to them. To do that you must first register your application on Google Developer console. Under APIs be sure to enable the Google Drive API and Google Drive SDK, as always don’t forget to add a product name and email address on the consent screen form.
Make sure your project is at least set to .net 4.0.
Add the following NuGet Package
PM> Install-Package Google.Apis.Drive.v2
In order to download a file we need to know its file resorce the only way to get the file id is from the Files.List() command we used earlier.
public static Boolean downloadFile(DriveService _service, File _fileResource, string _saveTo)
{
if (!String.IsNullOrEmpty(_fileResource.DownloadUrl))
{
try
{
var x = _service.HttpClient.GetByteArrayAsync(_fileResource.DownloadUrl );
byte[] arrBytes = x.Result;
System.IO.File.WriteAllBytes(_saveTo, arrBytes);
return true;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
return false;
}
}
else
{
// The file doesn't have any content stored on Drive.
return false;
}
}
Using _service.HttpClient.GetByteArrayAsync we can pass it the download url of the file we would like to download. Once the file is download its a simple matter of wright the file to the disk.
Remember from creating a directory in order to upload a file you have to be able to tell Google what its mime-type is. I have a little method here that try’s to figure that out. Just send it the file name. Note: When uploading a file to Google Drive if the name of the file is the same name as a file that is already there. Google Drive just uploads it anyway, the file that was there is not updated you just end up with two files with the same name. It only checks based on the fileId not based upon the file name. If you want to Update a file you need to use the Update command we will check that later.
public static File uploadFile(DriveService _service, string _uploadFile, string _parent) {
if (System.IO.File.Exists(_uploadFile))
{
File body = new File();
body.Title = System.IO.Path.GetFileName(_uploadFile);
body.Description = "File uploaded by Diamto Drive Sample";
body.MimeType = GetMimeType(_uploadFile);
body.Parents = new List() { new ParentReference() { Id = _parent } };
// File's content.
byte[] byteArray = System.IO.File.ReadAllBytes(_uploadFile);
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
try
{
FilesResource.InsertMediaUpload request = _service.Files.Insert(body, stream, GetMimeType(_uploadFile));
request.Upload();
return request.ResponseBody;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
return null;
}
}
else {
Console.WriteLine("File does not exist: " + _uploadFile);
return null;
}
}
I'm trying to link to a local file. I've set href as follows:
Link Anchor
In Firefox, when I right click and "open link in new tab", nothing happens.
When I right click and "copy link location", then manually open a new tab and paste the copied link, it works fine. So it seems my file:// syntax is fine. I've also tried it with 3 slashes like file:/// but it's the same result.
What am I doing wrong?
By definition, file: URLs are system-dependent, and they have little use. A URL as in your example works when used locally, i.e. the linking page itself is in the user’s computer. But browsers generally refuse to follow file: links on a page that it has fetched with the HTTP protocol, so that the page's own URL is an http: URL. When you click on such a link, nothing happens. The purpose is presumably security: to prevent a remote page from accessing files in the visitor’s computer. (I think this feature was first implemented in Mozilla, then copied to other browsers.)
So if you work with HTML documents in your computer, the file: URLs should work, though there are system-dependent issues in their syntax (how you write path names and file names in such a URL).
If you really need to work with an HTML document on your computers and another HTML document on a web server, the way to make links work is to use the local file as primary and, if needed, use client-side scripting to fetch the document from the server,
Organize your files in hierarchical directories and then just use relative paths.
Demo:
HTML (index.html)
<a href='inner/file.html'>link</a>
Directory structure:
base/
base/index.html
base/inner/file.html
....
The href value inside the base tag will become your reference point for all your relative paths and thus override your current directory path value otherwise - the '~' is the root of your site
<head>
<base href="~/" />
</head>
This can happen when you are running IIS and you run the html page through it, then the Local file system will not be accessible.
To make your link work locally the run the calling html page directly from file browser not visual studio F5 or IIS simply click it to open from the file system, and make sure you are using the link like this:
Intro
../htmlfilename with .html
User can do this
This will solve your problem of redirection to anypage for local files.
Try swapping your colon : for a bar |. that should do it
Link Anchor
The right way of setting a href=“” when it's a local file.
It will not make any issue when code or file is online.
FAQ
Hope it will help you.
Here is the alternative way to download local file by client side and server side effort:
<a onclick='fileClick(this)' href="file://C:/path/to/file/file.html"/>
Js:
function fileClick(a) {
var linkTag = a.href;
var substring = "file:///";
if (linkTag.includes(substring)) {
var url = '/cnm/document/v/downloadLocalfile?path=' + encodeURIComponent(linkTag);
fileOpen(url);
}
else {
window.open(linkTag, '_blank');
}
}
function fileOpen(url) {
$.ajax({
url: url,
complete: function (jqxhr, txt_status) {
console.log("Complete: [ " + txt_status + " ] " + jqxhr);
if (txt_status == 'success') {
window.open(url, '_self');
}
else {
alert("File not found[404]!");
}
// }
}
});
}
Server side[java]:
#GetMapping("/v/downloadLocalfile")
public void downloadLocalfile(#RequestParam String path, HttpServletResponse
response) throws IOException, JRException {
try {
String nPath = path.replace("file:///", "").trim();
File file = new File(nPath);
String fileName = file.getName();
response.setHeader("Content-Disposition", "attachment;filename=" +
fileName);
if (file.exists()) {
FileInputStream in = new FileInputStream(file);
response.setStatus(200);
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int numBytesRead;
while ((numBytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, numBytesRead);
}
// out.flush();
in.close();
out.close();
}
else {
response.setStatus(404);
}
} catch (Exception ex) {
logger.error(ex.getLocalizedMessage());
}
return;
}