download sys_attachment by public

CyrillW
Tera Contributor

Good Morning Guys!

At the moment, i make a Widget and this is on a public site (no authication).

 

In this Widget i want to make downloading files from sys_attachment.

first of all, i used the url /sys_attchment?sys_id=<SYSID>, but if i am not logged in, i dont can download the file.

After this, i try to make a restapi.. but this doesnt have permission to the sys_attachments...

After this, i try with a base64.. but it doesnt work..

Here is my Code:
Server Script:

(function() {
    data.attachments = [];

    var recordSysId = 'd3b1461f1b345550d9d142e6bb4bcb14';
    var gr = new GlideRecord('sys_attachment');
    gr.addQuery('table_sys_id', recordSysId);
    gr.orderByDesc('sys_created_on');
    gr.query();

    var sa = new GlideSysAttachment();
    while (gr.next()) {

        var bytes = sa.getBytes(gr);   // Datei holen
        var base64 = GlideStringUtil.base64Encode(bytes);

        data.attachments.push({
            name: gr.file_name + '',
            sys_id: gr.sys_id + '',
            base64: base64
        });
    }
})();

Client Controller:
api.controller = function() {
    var c = this;
    c.attachments = c.data.attachments || [];

   $scope.download = function(att) {

        var byteChars = atob(att.base64);
        var byteNumbers = new Array(byteChars.length);

        for (var i = 0; i < byteChars.length; i++) {
            byteNumbers[i] = byteChars.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);
        var blob = new Blob([byteArray], { type: att.mime });

        var url = URL.createObjectURL(blob);

        var a = document.createElement('a');
        a.href = url;
        a.download = att.name;
        a.click();
        URL.revokeObjectURL(url);
    };

};


on the site, i can see the files,
CyrillW_0-1764230762800.png

 

But, i doesn't can download it..
 
There any Ideas? i prefer a easy workaround.
Thanks and regards


1 ACCEPTED SOLUTION

CyrillW
Tera Contributor

 

Hey there,
I've now found a solution and would like to share it with you.
In case you encounter the same problem.

See for yourself:


Server script:

(function() {
    data.attachments = [];

    var gr = new GlideRecord('sys_attachment');
    // add your query
    gr.query();

    var sa = new GlideSysAttachment();
    while (gr.next()) {
        try {
            var bytes = sa.getBytes(gr);
            
            if (bytes && bytes.length > 0) {
                var base64 = GlideStringUtil.base64Encode(bytes);
                
                data.attachments.push({
                    name: gr.getValue('file_name'),
                    sys_id: gr.getValue('sys_id'),
                    content_type: gr.getValue('content_type'),
                    size: gr.getValue('size_bytes'),
                    base64: base64
                });
            }
        } catch(ex) {
            gs.error('Error in File ' + ex.message);
        }
    }
})();

 

And this is my Client controller:

api.controller = function($scope, spUtil) {
    var c = this;
    c.attachments = c.data.attachments || [];
 
    c.download = function(att) {
        try {
            var byteChars = atob(att.base64);
            var byteNumbers = new Array(byteChars.length);
 
            for (var i = 0; i < byteChars.length; i++) {
                byteNumbers[i] = byteChars.charCodeAt(i);
            }
 
            var byteArray = new Uint8Array(byteNumbers);
            var blob = new Blob([byteArray], { type: att.content_type });
 
            var url = URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = att.name;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        } catch(e) {
            alert('Error by downloading: ' + e.message);
        }
    };
}

But thanks, for the help.

View solution in original post

9 REPLIES 9

@CyrillW if everything else is down, why not to use your ServiceNow credentials like always?

 

I don't know how to help you because I don't really understand the requirement. sorry...

_____
No AI was used in the writing of this post. Pure #GlideFather only

@GlideFather 

The only thing I need is a way to display or download files using a widget if they are stored in sys_attachment – or any other method.
Important: It must work without login, just like accessing google.com.
Nothing else really matters. These are my requirements – the why or how is completely irrelevant.

Tomas Lozano
Tera Expert

Hi @CyrillW

 

Just for you to understand, conceptionally, public role and sys_attachment is a big no no in ServiceNow, so you'll be struggling to find an out of the box way to interact with the sys_attachment table directly without opening yourself up to an attack.

 

There's the sys_property glide.attachment.role where you can add the 'public' role. That won't work on all portals tho.

 

If this doesn't work for you, what I would suggest is looking to leverage the $scope object to store the attachments before submit. On Submit, you can create them in sys_attachment and map them to the target record.

 

Lastly, I would recommend you look to secure the form and the submit to minimise risk of unwanted requests being created. I would recommend leveraging Authentication.. any... or maybe a check on get/set clientData based on a server side logic(API Reference).

 

Good luck!

Tomas

Hey @Tomas Lozano 

First of all, thank you for your explanation.

I am aware that this would be a very serious security vulnerability. However, I don't want to upload anything, I want to download something.
That's why this solution won't work.

Nevertheless, thank you very much for your help.

CyrillW
Tera Contributor

 

Hey there,
I've now found a solution and would like to share it with you.
In case you encounter the same problem.

See for yourself:


Server script:

(function() {
    data.attachments = [];

    var gr = new GlideRecord('sys_attachment');
    // add your query
    gr.query();

    var sa = new GlideSysAttachment();
    while (gr.next()) {
        try {
            var bytes = sa.getBytes(gr);
            
            if (bytes && bytes.length > 0) {
                var base64 = GlideStringUtil.base64Encode(bytes);
                
                data.attachments.push({
                    name: gr.getValue('file_name'),
                    sys_id: gr.getValue('sys_id'),
                    content_type: gr.getValue('content_type'),
                    size: gr.getValue('size_bytes'),
                    base64: base64
                });
            }
        } catch(ex) {
            gs.error('Error in File ' + ex.message);
        }
    }
})();

 

And this is my Client controller:

api.controller = function($scope, spUtil) {
    var c = this;
    c.attachments = c.data.attachments || [];
 
    c.download = function(att) {
        try {
            var byteChars = atob(att.base64);
            var byteNumbers = new Array(byteChars.length);
 
            for (var i = 0; i < byteChars.length; i++) {
                byteNumbers[i] = byteChars.charCodeAt(i);
            }
 
            var byteArray = new Uint8Array(byteNumbers);
            var blob = new Blob([byteArray], { type: att.content_type });
 
            var url = URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = att.name;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        } catch(e) {
            alert('Error by downloading: ' + e.message);
        }
    };
}

But thanks, for the help.