Kentico: PortalTemplate.aspx explicitly throwing a 404 error when directly invoked - kentico

We work on a product that is a series of components that could be installed on different CMSs and provide different services. We take a CMS agnostic approach and try to use the same code in all the CMSs as much as possible (we try to avoid using CMS API as much as we can).
Some part of the code needs to work with the current URL for some redirections so we use Request.Url.ToString() that is something that has worked fine in other environments but in Kentico instead of returning the current page we always get a reference to CMSPages/PortalTemplate.aspx with a querystring parameter aliasPath that holds the real URL. In addition to that, requesting the Template page using a browser gives you a 404 error.
Example:
Real URL (this works fine on a browser):
(1) https://www.customer.com/Membership/Questionnaire?Id=7207f9f9-7354-df11-88d9-005056837252
Request.Url.ToString() (this gives you a 404 error on a browser):
(2) https://www.customer.com/CMSPages/PortalTemplate.aspx?Id=7207f9f9-7354-df11-88d9-005056837252&aliaspath=/Membership/Questionnaire
I've noticed that the 404 error is thrown explicitly by the template code when invoked directly. Please see below code from Page_Init method of PortalTemplate.aspx.cs:
var resolvedTemplatePage = URLHelper.ResolveUrl(URLHelper.PortalTemplatePage);
if (RequestContext.RawURL.StartsWithCSafe(resolvedTemplatePage, true))
{
// Deny direct access to this page
RequestHelper.Respond404();
}
base.OnInit(e);
So, if I comment the above code out my redirection works fine ((2) resolves to (1)). I know it is not an elegant solution but since I cannot / don't want to use Kentico API is the only workaround I could find.
Note that I know that using Kentico API will solve the issue since I'm sure I will find an API method that will return the actual page. I'm trying to avoid that as much as possible.
Questions: Am I breaking something? Is there a better way of achieving what I trying to accomplish? Can you think on any good reason I shouldn't do what I'm doing (security, usability, etc)?
This is kind of a very broad question so I was not able to find any useful information on Kentico docs.
I'm testing all this on Kentico v8.2.50 which is the version one of my customers currently have.
Thanks in advance.

It's not really recommended to edit the source files of Kentico, as you may start to run into issues with future upgrades and also start to see some unexpected behaviour.
If you want to get the original URL sent to the server before Kentico's routing has done its work, you can use Page.Request.RawUrl. Using your above example, RawUrl would return a value of /Membership/Questionnaire?Id=7207f9f9-7354-df11-88d9-005056837252, whereas Url will return a Uri with a value of https://www.customer.com/CMSPages/PortalTemplate.aspx?Id=7207f9f9-7354-df11-88d9-005056837252&aliaspath=/Membership/Questionnaire (as you stated).
This should avoid needing to use the Kentico API and also avoid having to change a file that pretty much every request goes through when using the portal engine.
If you need to get the full URL to redirect to, you can use something like this:
var redirectUrl = Request.Url.GetLeftPart(UriPartial.Authority) + Request.RawUrl;

Related

A third party application may be attempting to make unauthorized access to your account - Ameritrade

I was trying to do some simple authorization for ameritrade's developer platform. I was attempting.
According to the platform, the Endpoint I need to access is is:
https://auth.tdameritrade.com/auth?response_type=code&redirect_uri={uri}&client_id={client_id}}%40AMER.OAUTHAP
https://developer.tdameritrade.com/content/simple-auth-local-apps
When looking at the client_id, for the dev application, I was noticing that they may actually be referencing the Applications, Consumer Key instead? So i did just that, but when attempting to query the information, it returns: A third-party application may be attempting to make unauthorized access to your account. The reason why i think it is the consumer key, is listed at: https://developer.tdameritrade.com/content/getting-started
So I ended up doing something like:
from urllib.parse import urlencode, quote_plus
url = "https://auth.tdameritrade.com/auth?response_type=code&redirect_uri={uri}&client_id={client_id}}%40AMER.OAUTHAP".format(
uri=urlencode("http://localhost", quote_via=quote_plus),
client_id="JHBDFGJH45OOUDFHGJKSDBNG" #Sample
)
I dont think this is because I am currently in a different country currently, I think that something else is wrong here.
It doesnt follow through with it, but instead returns a 400 error with that information. Im not sure whats wrong though.
This happens when you copied the callback URI incorrectly. Imagine if this were a client application, and TD detected that the application is trying to send the user to a different URL than the app is configured with. If they send the callback request to that application, it will receive the token and gain full control over your account.
Have you double and triple checked that you're copying the callback URL correctly, protocol name, ports, and trailing slashes and everything? Also, consider using an API library instead of writing your own. You can find documentation about this specific error here.
I had this issue and I solved it using simply using http://127.0.0.1 on the call back URI of the App.
I then used below URL and it worked as expected.
https://auth.tdameritrade.com/auth?response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1&client_id={MyConsumerKey}%40AMER.OAUTHAP
Just in case anyone is still having this problem, make sure the callback URI is spelled EXACTLY the same as you specified when creating the app. I was having this problem because I set the callback on the TD developer website to "https://localhost/" and used "https://localhost" in the URL instead (missing the slash at the end). As soon as I added the slash at the end, it worked.
I found out that the issue is caused by the way the callback URL is set. It have to be exactly the same as the callback URL you have typed in at the apps details on the TD developer API page. I tried several permutations and indeed to get the authorization to work both have to be the same. eg. https or http.. end with '/' or does not, it matters. There is also no need to URL encode it.

Cypress e2e testing - How to get around Cross Origin Errors?

I'm testing a web app that integrates Gmail, Slack, Dropbox etc. I'm trying to write end to end tests with Cypress.io to verify that auth flows are working. Cypress restricts me from navigating outside my app's domain and gives me a Cross Origin Error. The Cypress docs say that testing shouldn't involve navigating outside your app. But the entire purpose of testing my app is to make sure these outside auth flows are functioning.
The docs also say you can add
"chromeWebSecurity": false
to the cypress.json file to get around this restriction. I have done this, but am still getting cross origin errors (this is at the heart of my question. I would ideally get around this restriction).
I have attempted cypress' single-sign-on example. https://github.com/cypress-io/cypress-example-recipes#logging-in---single-sign-on
I was not able to make it work, and it's a lot more code than I think is necessary.
I've commented on this thread in github, but no responses yet.
Full error message:
Error: CypressError: Cypress detected a cross origin error happened
on page load:
> Blocked a frame with origin "https://www.example.com" from
accessing
a cross-origin frame.
Before the page load, you were bound to the origin policy:
> https://example.com
A cross origin error happens when your application navigates to a new
superdomain which does not match the origin policy above.
This typically happens in one of three ways:
1. You clicked an <a> that routed you outside of your application
2. You submitted a form and your server redirected you outside of your
application
3. You used a javascript redirect to a page outside of your application
Cypress does not allow you to change superdomains within a single test.
You may need to restructure some of your test code to avoid this
problem.
Alternatively you can also disable Chrome Web Security which will turn
off this restriction by setting { chromeWebSecurity: false } in your
'cypress.json' file.
https://on.cypress.io/cross-origin-violation
setting { "chromeWebSecurity": false } in my 'cypress.json' file worked for me
If you are trying to assert the proper navigation to gmail...
You should stub the function that handles that and assert that the request contains the necessary key value pairs. Without more information on the intent of this test it is hard to give specific advice. It sounds like you would want to have a "spy"(type of test double).
Here is the documentation for spies: https://docs.cypress.io/guides/guides/stubs-spies-and-clocks.html#Stubs
If you are trying to verify the contents of the email
You will want to use a library to handle reading gmail. cy.task can be used to invoke JavaScript from an external library. This Medium article has a good write up on how to do this.
Medium article: https://medium.com/#levz0r/how-to-poll-a-gmail-inbox-in-cypress-io-a4286cfdb888
TL;DR of article
Setup and define the custom task(method) that will check gmail(uses "gmail-tester" in the example)
Use cypress to trigger the email(obviously)
Capture/define data(like email subject, dynamic link, email content)
Assert the data returned from gmail-tester is as expected
DON'T
Use the GMail UI in your test in an effort to avoid test flake (all UI testing has flakiness), and potential UI changes to the Gmail app that require updates to your test. The backend methods that gmail-tester uses are less likely to change overtime compared to the UI. You also avoid the CORS error.
Disabling cross-origin security, if you must...(eek bugs!)
If you must, add chromeWebSecurity: false to the cypress.json config file. Be sure to add it inside of the curly braces. There should only be one set of braces in that file.
NOTE: One cannot simply use cy.visit(<diffSuperDomain>); there is an open issue. Apparently this is a very difficult change to make in cypress.
One potential workaround is to only have one super domain per test. It should work if you set the chromeWebSecurity: to false and only have one domain per test(it block). Careful, as it opens you up to cascading failures as one test will rely on the next. Hopefully they fix this soon.
https://docs.cypress.io/guides/guides/web-security.html#Disabling-Web-Security
Since Cypress 9.6.0 you can set "experimentalSessionAndOrigin": true in cypress.json. This allows your tests to operate in multiple domains using the origin command. Example from the official blog:
it('navigates', () => {
cy.visit('/')
cy.get('h1').contains('My Homepage')
cy.origin('www.acme.com', () => {
cy.visit('/history/founder')
cy.get('h1').contains('About our Founder, Marvin Acme') // đź‘Ť
})
})
At that blog entry there are also examples how to use this to authenticate at another domain. Worked fine for me with Keycloak using both Chrome and Firefox.
There are a few simple workarounds to these common situations:
Don’t click <a> links in your tests that navigate outside of your application. Likely this isn’t worth testing anyway. You should ask yourself: What’s the point of clicking and going to another app? Likely all you care about is that the href attribute matches what you expect. So make an assertion about that. You can see more strategies on testing anchor links in our “Tab Handling and Links” example recipe.
You are testing a page that uses Single sign-on (SSO). In this case, your web server is likely redirecting you between superdomains, so you receive this error message. You can likely get around this redirect problem by using cy.request() to manually handle the session yourself.
If you find yourself stuck and can’t work around these issues you can just set this in your cypress.json file. But before doing so you should really understand and read about the reasoning here.
// cypress.json
{
"chromeWebSecurity": false
}

Accept QueryString for Image delivered by Controller

I'm trying to make a controller that delivers a random background image. I have a MVC route from /random-background.jpg to /MyController/RandomBackground, and it's returns File(backgroundImage, "image/jpeg"). This is working fine.
I've disabled the 404 plugin for all QueryStrings that ends with random-image, so the image is showing up. However, it won't take any parameters, such as random-background.jpg?width=200. &format=jpg doesn't have any effect either.
How do I get ImageResizer to accept this for a "real image"? Do I have to use ImageResizer.ImageJob? If so, I would love an example I can understand :)
ImageResizer V3 and earlier cannot 'post-process' the result of an MVC action or third-party HTTPHandler.
You will need to use one of the virtual providers plugins, use URL rewriting, or implement your own IVirtualImageProvider.
See the best practices guide for an explanation of 'why'.

Modifying content delivered by node-http-proxy

Due to some limitations about the web services I am proxying, I have to inject some JS code so that it allows the iframe to access the parent window and perform some actions.
I have built a proxy system with node-http-proxy which works pretty nicely. However I have spent unmeasurable hours trying to modify the content (on my own, using harmon as well, etc) that is being sent to the user without any success. I have found some articles and even some questions here but all of them are outdated and are not useful anymore.
I was wondering if someone can give me an actual example about how to do this, because I am unable to do it and maybe it is just that it is impossible to do at this point?
I haven't tried harmon, but I did try cheerio and it works.
However, I used http-mitm-proxy and not node-http-proxy.
If you are using http-mitm-proxy, you need to return a promise in the response handler. Otherwise, the proxy continues to send the original response without picking up your changes.
I have recently written another proxy at:
https://github.com/noeltimothy/noelsproxy
I'm going to add response handling to this soon. This one uses a callback mechanism, which means it wont return the response until the caller signals it to.
You should be able to use 'cheerio' and alter the content in JQuery style.

Detecting where user has come from a specific website

I'm wanting to know if it's possible to detect which website a user has come from and serve to them different content based on which website they have just come from.
So if they've come from any other website on the internet and landed on my page, they will see my normal html and css page, but if they come from a specific website (this specific website would have also been developed by me so I have control over the code server-side and client-side) then I want them to see something slightly different.
It's a very small difference that I want them to see, and that's why I don't want to consider taking them to a different version of the website or a different page.
I'm also not sure if this solution will be placed on the page they coming from or the page that they arriving on?
Hope that's clear. Thanks!
I would add a URL parameter like http://example.com?source=othersite. This way you can easily adjust the parameter and can use javascript to detect this and slightly alter your landing page.
Otherwise, you can use the HTTP referrer sent via the browser to detect where they came from, but you would need to tell us your back end technology to get an example of that, as it differs a bit.
In javascript, you can do something as easy as
if(window.location.href.indexOf('source=othersite') > 0)
{
// alter DOM here
}
Or you can use a URL Parameter parser as suggested here: How to get the value from the GET parameters?
What you want is the Referer: HTTP header. It will give the URL of the page which the user came from. Bear in mind that the Referer can easily be spoofed, so don't take it as a guarantee if security is an issue.
Browsers may disable the referer, though. Why not just use a URL parameter?

Resources