0
votes

I want to make some Sitecore data available to a non-Sitecore page. The data will be requested by JQuery and should be in JSON format. We will also need to update items, execute workflow actions, etc.

We are on Sitecore 6.4 so, as I understand it, we don't have ItemWebAPI or MVC available as options. .NET is version 4

We tried doing it with a GET request, looking at query string parameters and branching to one function or another. One problem was that the default max size of the query string was way too small for the data we are passing, and setting it to the max allowed (which is apparently against security best practices) is also not reliably enough. So for that reason and some cache-related issues we moved to POST. JQuery couldn't seem to send POST data in the way the page wanted to get it, so we had to introduce some code to parse the request payload text (i.e. we couldn't use Request.Form["something"]). We did get this working but it is ugly. Between that and the branching it is a tangle.

I am now looking at WebMethods. In addition to simplifying the code, WebMethods also give us some free stuff like error messages in json format, sensible error messages for missing params, etc.

I have a test page set up to make requests (just HTML and JQuery) and an ASPX page to do the WebMethods. I put the URL of the WebMethods page in the web.config's IgnoreUrlPrefixes as described here: WebMethod not accessible in Sitecore . I can get to the Sitecore databases by using Sitecore.Configuration.Factory.GetDatabase(databasename).

So WebMethods overall seem to be better to code and work better than what we had before. Now I can read the code and I am just writing functions that work instead of spending most of my time figuring out why things aren't working and implementing workarounds.

But we do lose the Sitecore context, as described in the answer to the referenced question. I have not been able to find a way to get the Sitecore user.

I can see in Chrome Developer Tools when I make a request that there is a sitecore_userticket cookie going out with the request. I have tried feeding the value of that cookie (as well as the result of Sitecore.Web.Authentication.TicketManager.GetCurrentTicketId) to Sitecore.Web.Authentication.Ticket.Parse but I get a parse error. Is there a way to use that cookie to get the user?

Or is there a way to get the user from the ASP.NET_SessionId cookie?

If neither of those are workable, what would be a good way to manage login info? I am aware that I can log a user in programmatically but would prefer not to track the Sitecore username and password in the browser and send them with every request.

I don't have much experience with .NET outside of Sitecore.

2
Please provide some code for your ticket processing, and the error details - James Walford

2 Answers

0
votes

I have one workable solution after re-reading the question @Gabber referenced in the comment (Display original Sitecore username instead of what's currently typed in). In short, I can authenticate the user against the underlying System.Web.Security.MembershipUser, which is set up when a Sitecore account is created. The visitor can use his or her Sitecore username and password. I can then set an AuthCookie with System.Web.Security.FormsAuthentication.SetAuthCookie and whenever I need the Sitecore user I can get the domain and username from HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName] and feed it to Sitecore.Security.Accounts.User.FromName to return the Sitecore user.

One thing that is not so great about this is that if the user is already signed in to Sitecore they will have to authenticate again to set the cookie for the MembershipUser (i.e. I haven't got it to pick up the Sitecore auth for a currently logged in Sitecore user). I think this will be OK for my purposes. Most people who are visiting the WebMethod-fed plain html pages won't be CMS users anyhow. But it would be nice to avoid another login for those who are.

[WebMethod]
public static bool login(string user, string password, string domain)
{
    bool valid = System.Web.Security.Membership.ValidateUser(domain + "\\" + user, password);
    if (valid)
    {
        System.Web.Security.FormsAuthentication.SetAuthCookie(domain + "\\" + user, true);
        return true;
    }
    return false;
}

[WebMethod]
public static string getuser()
{
    HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
    string u = ticket.Name;

    Dictionary<string, string> responseDict = new Dictionary<string, string>(); 

    User user = Sitecore.Security.Accounts.User.FromName(u,false);
    {
        responseDict["domain"] = user.Domain.Name;
        responseDict["user"] = user.Name;
        responseDict["displayname"] = user.DisplayName;
        responseDict["email"] = user.Profile.Email;
        responseDict["fullname"] = user.Profile.FullName;                
    }

    return JsonConvert.SerializeObject(responseDict, Formatting.Indented);
}

Here is the plain html page I am using to test these web methods:

<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>

    <p>domain <input type="text" name="domain" id="domain"/></p>
    <p>user <input type="text" name="user" id="user"/></p>
    <p>password<input type="text" name="password" id="password"/></p>
    <div class="gettoken button">
        <p>Get Token</p>
    </div>
    <p class="tokenresult">token result</p>

    <hr/>

    <div class="getuserinfo button">
        <p>Get User Info</p>
    </div>
    <p class="userresult">result</p>

    <hr/>

    <script>
    $('div.gettoken').click(function ()
    {
        var user = $('#user').val();
        var domain = $('#domain').val();
        var password = $('#password').val();

        $.ajax({
            type: "POST",
            url: "http://sitecore/apis/scitemapi.aspx/setauthcookie",
            data: '{"domain":"' + domain + '", "user": "' + user + '", "password":"' + password + '"}',
            contentType: "application/json; charset=utf-8",
            success: function (msg)
            {
                $("p.tokenresult").text(msg.d);
            }
        });
    });

    $('div.getuserinfo').click(function ()
    {
        $.ajax({
            type: "POST",
            url: "http://sitecore/apis/scitemapi.aspx/getuser",
            data: '{}',
            contentType: "application/json; charset=utf-8",
            success: function (msg2)
            {
                $("p.userresult").text(msg2.d);
            }
        });
    });


    </script>
    <style>

    html{
        font-family:tahoma;
        }

    .button{
        background-color:blue;
        color:white;
        border:1px solid black;
        border-radius:4px;  
        width:12em;
        text-align:center;
        margin-top:1em;
        padding:.5em;
        }

    </style>
</html>
0
votes

Currently you put the a normal aspx page rather doing that. Create a new item in sitecore which ideally should be at some odd location of content tree like

/sitecore/content/home/services/handler

In above path services is just a folder and handler is just another item(could be based on sample template). Apply a layout to it which is inturn a aspx page(Here you code), where you put your webmethods.. That way it become the Sitecore Request which will have the sitecore context and context user if logged-in(otherwise anonymous user) and you also do not need to get context database from factory.. to make it secure you can put Sitecore based authentication or any authentication you prefer. For example: Following make sure on page that request is authenticated otherwise redirected to desired page.

protected override void OnInit(System.EventArgs arguments)
        {
            Assert.ArgumentNotNull(arguments, "arguments");
            checkSecurity(true);
            base.OnInit(arguments);
        }

Second is decide the function at page load and call it..

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                if (Request.QueryString != null && Request.QueryString.Count > 0)
                {
                       string jobName = Request.QueryString["jobName"].ToString();
                       string cacheName = Request.QueryString["cacheName"].ToString();
                       StringBuilder sbAjax = null;
                    if (jobName!=null && jobName.Length > 0)
                    {
                             if (jobName="")
                        {
                           sbAjax=callSomething();
                        this.Page.Response.Clear();
                        if (sbAjax != null)
                            this.Page.Response.Write(sbAjax.ToString());
                        else
                            this.Page.Response.Write("");
                            this.Page.Response.End();
                        }
                     }
                }
           }
}
private StringBuilder callSomething()
{
//Do Something
}