Jump to content

emby endpoint: cross origin security question


chef

Recommended Posts

chef

Hi,

 

This is somewhat of an advanced question.

I'm going to post some code below, please don't judge me with some of my class names. The code itself is kind of walking a fine line.

 

-------------

Note:

I have web app which allows me to poll an rss feed and display the contents on a sub domain which is part of my site.

 

This is made possible by having a back-end C# console app which requests the RSS feed contents and then write them in an XML file,  which is included in my sites folder structure. 

 

I then use simple JQuery/ajax method to parse the XML file,  and using Material CSS,  I display the contents, having the UI mimic emby.

 

This allows me to generate and read XML information from an RSS feed without breaking any cross origin security measures and keep my site safe.

 

This is working like a dream!

--------------

 

Now comes the fun part, but also where I am having some difficulty. 

 

In order to interact with the links provided in XML,  I thought I would create an emby plugin endpoint, which would handle information POSTed by the  ajax functions form my web page

 

I have used emby endpoints in the past with alexa custom skills and other custom plugins, but this time I have 'run aground'.

 

Perhaps one of you advanced web coders will see the issue where I am missing it.

 

ajax POST method from my js file:

    $('#download').on('click', function () {
        renderToastPopup("Sending info to Server");
        for (var i = 0; i < linkList.length; i++) {
            setTimeout(function () {
                $.ajax({
                    url: "http://192.168.2.12:8096/emby/Submit/Torrent",
                    method: 'POST',
                    contentType: "application/json",
                    data: linkList[i],
                    crossDomain: true,
                    dataType: 'jsonp'
                }).then(function (data) {
                    console.log(data.responseText);
                });
            }, 3000);
            console.log(linkList[i])
        }

    });

Then the Endpoint code in the Emby Plugin:

using System;
using System.Net;
using System.Text;
using HtmlAgilityPack;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;

namespace Torrent
{
    [Route("/Submit/Torrent", "POST", Summary = "Torrent End Point")]
    public class Torrent : IReturnVoid
    {
        public string Url { get; set; }
    }

    class TorrentEntryPoint : IService
    {
        private readonly ILogger logger;

      
       
        public TorrentEntryPoint(ILogManager logManager)
        {
            logger = logManager.GetLogger(GetType().Name);
           
        }

        private static string Base64Encode(string plainText)
        {
            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            return Convert.ToBase64String(plainTextBytes);
        }

        private static string GetAuthToken()
        {
            //TODO: USE THE BUILT IN EMBY HTTP CLIENT HERE INSTEAD OF USING WebClient.
            using (var client = new WebClient())
            {
                client.Headers["Content-Type"] = "text/plain";
                client.Headers["Authorization"] = "Basic " + Base64Encode("USRNAME:PASSWORD");
                var html = new HtmlDocument();
                html.LoadHtml(Encoding.UTF8.GetString(
                    client.DownloadData("http://192.168.2.18:4608/gui/token.html")));
                var info = html.DocumentNode.ChildNodes[0].InnerText;
                
                return info;
            }
        }


        public object Post(Torrent torrent)
        {
            logger.Info("TORRENT - " + GetAuthToken());
            using (var client = new WebClient())
            {
                client.Headers["Content-Type"] = "plain/text";
                client.Headers["Authorization"] = "Basic " + Base64Encode("USERNAME:PASSWORD");

                string sUrl = "http://192.168.2.18:4608/gui/?token=" + GetAuthToken() + "&action=add-url&s=" + torrent.Url;

                var responsebody = client.OpenWrite(sUrl);
                logger.Info("TORRENT - " + responsebody);
                return "success: " + responsebody;
            }           
        }
    }
}


Because I'm using JSON with padding I thought this would stop cross origin security issues, which I believe it has, however I get 'NET::ERR' with this request.

 

Perhaps I need to create some headers specifically for the request? 

 

I just had it with it not working after a couple days and now I'm asking for help.

I thought about going to StackOverflow, but because it is an emby endpoint, I thought to try here first.

 

If this code should be removed please let me know.

 

Many thanks.

Edited by chef
Link to comment
Share on other sites

Base64Encode("USRNAME:PASSWORD");

 

Possibly this? Is username an alias? Or does the misspelling not matter as you hardcoded this?

 

Sent from my Nexus 7 using Tapatalk

Edited by speechles
Link to comment
Share on other sites

chef

I used an alias for purposes of posting here.

 

I can get the functions to run okay when testing them in a console app talking to that particular program.

Link to comment
Share on other sites

chef

Figured it out.

 

Two things had to heppen:

 

1. This is the ajax call to the Emby endpoint:

 //Download Button - Adds torrent URL to emby Endpoint Plugin -> Adds to uTorrent
    $('#download').on('click', function () {
        renderToastPopup("Sending info to Server");
        for (var i = 0; i < linkList.length; i++) {
            
                $.ajax({
                    url: "http://192.168.2.12:8096/emby/Submit/Torrent",
                    method: 'POST',
                    contentType: "application/json",
                    data: linkList[i],
                    crossDomain: true,
                    dataType: 'jsonp'
                }).done(function (data) {
                    console.log(data);
                });
            
            console.log(linkList[i])
        }

    });

2. When sending data to an emby endpoint it has to look like this:

{ "KEY" : "VALUE" }

So the link list in the ajax call is created like this:

            var link = this.dataset.link;
            var item = { "Url" : encodeURIComponent(link) }
            linkList.push(item);

Where the link is grabbed form a 'data-' object in the html button, which was placed there thorugh Javascript from the RSS Feed XML.

html += '<li class="collection-item blue-text avatar" id="' + title + '" data-link="' + link + '">';

Now back in the C# Emby plugin Code:

using System;
using System.Net;
using System.Text;
using HtmlAgilityPack;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;

namespace Torrent
{
    [Route("/Submit/Torrent", "GET", Summary = "Torrent End Point")]
    public class Torrent : IReturnVoid
    {
        public string Url { get; set; }
    }

    class TorrentEntryPoint : IService
    {
        private readonly ILogger logger;

      
       
        public TorrentEntryPoint(ILogManager logManager)
        {
            logger = logManager.GetLogger(GetType().Name);
           
        }

        private static string Base64Encode(string plainText)
        {
            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            return Convert.ToBase64String(plainTextBytes);
        }

        private static string GetAuthToken()
        {
            using (var client = new WebClient())
            {
                client.Headers["Content-Type"] = "text/plain";
                client.Headers["Authorization"] = "Basic " + Base64Encode("USER:PASS&");
                var html = new HtmlDocument();
                html.LoadHtml(Encoding.UTF8.GetString(
                    client.DownloadData("http://192.168.2.18:4608/gui/token.html")));
                var info = html.DocumentNode.ChildNodes[0].InnerText;
                
                return info;
            }
        }


        public object Get(Torrent torrent)
        {
            logger.Info("TORRENT - " + GetAuthToken());
            try
            {
                using (var client = new WebClient())
                {
                    client.Headers["Content-Type"] = "plain/text";
                    client.Headers["Authorization"] = "Basic " + Base64Encode("USER:PASS&");

                    string sUrl = "http://192.168.2.18:4608/gui/?token=" + GetAuthToken() + "&action=add-url&s=" +
                                  torrent.Url;
                    logger.Info("SENDING TO UTORRENT: " + sUrl);
                    client.DownloadData(sUrl);
                    logger.Info(torrent.Url);
                    
                }
            }
            catch (Exception ex)
            {
                logger.Info(ex.ToString());
            }
            return "{\"" + torrent.Url + "\"}";
        }
    }
}


Bam! full control over every single thing that the server is doing. From gathering to organization, all though a sub domain with strong encryption like emby.

 

Did I hear someone say "That's bad ass", because it is.

Edited by chef
  • Like 1
Link to comment
Share on other sites

nxenos83

@@chef make sure to secure your endpoints.  In the above code [server ip]/emby/submit/torrent is wide open. 

 

If you add authenticated below the Route definition, you will need an access token to reach the end point

 [Route("/Submit/Torrent", "GET", Summary = "Torrent End Point")]
 [Authenticated(Roles = "Admin")]

You will then need to update your calling code to first obtain an access code. Use the javascrip API to do this. If the code is being executed from a page in Emby dashboard, use:

ApiClient.ajax({});

and get the url using this

ApiClient.getUrl("submit/torrent")

If the webpage is hosted outside of the dashboard, you will need to implement the ApiClient and get your access token.

  • Like 1
Link to comment
Share on other sites

chef

@@chef make sure to secure your endpoints. In the above code [server ip]/emby/submit/torrent is wide open.

 

If you add authenticated below the Route definition, you will need an access token to reach the end point

 [Route("/Submit/Torrent", "GET", Summary = "Torrent End Point")]
 [Authenticated(Roles = "Admin")]
You will then need to update your calling code to first obtain an access code. Use the javascrip API to do this. If the code is being executed from a page in Emby dashboard, use:
ApiClient.ajax({});
and get the url using this
ApiClient.getUrl("submit/torrent")
If the webpage is hosted outside of the dashboard, you will need to implement the ApiClient and get your access token.

Many thanks!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...