Angular Protractor test session persistence - angularjs

I'm working on e2e tests for a web app and I would like to log in a user and persist their session.
Specifically the following scenario:
The log in form is posted with valid credentials.
Angular routes the user to a landing page.
I call browser.get( /* the current url */ )
I expect the current URL to be the same, instead of my user getting kicked back to the log in screen.
We're using HTTP header based auth and I don't know how to configure this scenario for testing purposes.
Is it as simple as activating cookies somewhere? Or maybe supporting the auth headers via a config?

I managed to solve this be simply adding browser.sleep(1000) in before calling browser.get( /* the current url */ ).
Basically Protractor was hammering on the router to fast and my app was kicking me out before auth creds were set. Or, perhaps, might be the HTML5 routing takes a little time to process (our deep links are hashed, but then angular converts the hash to HTML5 routes).

You can use browser.waitForAngular(); too.

You can use promises like this
goAfterLogin: function(){
browser.get('http://www.example.com').then(function(){
return this; //or other return
}).then(function(){
return this;
});
}
Its up to you how will you use it (you as well can create promis with waitForAngular() )

Related

How to handle the response from PayPal In-Context in AngularJS instead of NodeJS

I'm having trouble getting a response from server side NodeJS code after checking out using PayPal In-Context via the paypal-rest-sdk.
I have a PayPal form defined in html, and purposefully did not define the action. I only defined the id and method (get). In my Angular code, I define the action when the PayPal button is clicked:
var payPalForm = document.getElementById('payPalForm');
payPalForm.addEventListener("click", function(e) {
payPalForm.setAttribute('action', "https://10.0.0.25:8001/checkout/" +
$scope.user._id + "/" + $scope.viewEvent._id.valueOf() + "/" +
$scope.viewEvent.eventName + "/" + $scope.viewEvent.price);
});
By doing this, I am able to successfully checkout via PayPal In-Context.
In my NodeJS code, after successful checkout, instead of return res.end(); if successful or res.sendStatus(400, 'bad paypal payment'); if failure, I would like to pass the status back to the Angular, and handle it there.
To reiterate: I do not want to define the post PayPal In-Context route in NodeJS, I would like to do it in Angular.
I need to do this being I am using Onsen UI, and am using a splitter to navigate between pages.
Has anyone successfully done this?
UPDATE AFTER ATTEMPTING THE FIRST ANSWER
I have tried this before, but after attempting to implement Ilia Sky's Answer, the result is the PayPal In-Context checkout does not execute correctly. The script runs correctly (as determined by output in the terminal), but the In-Context window loads with "?" as a parameter and then immediately closes itself. Here are screenshots on how I updated my code. The commented out code is what I had when I was able to successfully checkout, but unable to redirect properly:
I think this is an issue with PayPal, and I'm not sure how to solve it.
This is why I'm wondering if I can listen for a redirect in Angular, and then load a different page when a certain redirect is identified.
I tried this in my Angular controllers, but it only ever executed when the initial page loaded:
I may be wrong, but from what I understand what you want to do is:
Send data to the server (to https://10.0.0.25:8001/checkout/...)
Do something on the server and give a response (res.send etc)
Handle the response on the client
Right?
To do this the easiest solution would be just to send an ajax request to the server and then handle the response.
This means instead of changing the action attribute you can directly make the request via js. With angular that would be done using the $http service.
$http.post('https://10.0...').then(successCallback, errorCallback);
And in success|errorCallback you can do whatever you want such as load a page in the splitter or whatever you want :)
To change what happens when you try to submit the form you can check out ng-submit.
And to get $http you can just add it to your list of arguments for your controller.
Example
app.controller('ExampleController', function($scope, $http) {
$scope.submit = function() {
$http.post('your/long/url').then(function(response) {
mySplitter.content.load('success-page.html');
}, function(response) {
mySplitter.content.load('error-page.html');
});
};
}]);
<form ng-submit="submit()" ng-controller="ExampleController">
...
</form>
I encourage everyone to check out this link.
One of the PayPal developers has posted a sample Kraken with Angular example of the PayPal In-Context Checkout.

Angular routing authorization security

I am new to Angular and one of the things that I am trying to wrap my head around is the route authorization. I am coming from the .NET/IIS world where route authorization is as simple as decorating your API or MVC controller with the [Authorize] attribute.
I have read several posts and documents on how Angular handles routing. My concern is that the authorization is happening on the client. What prevents the user from firing up the dev tools, breakpoint the script execution, and changing the variables in the authorization service that control whether or not the user is authorized to access this route?
As I mentioned, I am new to Angular, so maybe I have misunderstood how the routing works. If this is the case, please correct me.
So, my question is: How can one achieve the same level of security with Angular routing as you would if using server-side routing authorization?
Thank you.
It is evident that pure client side solution cannot exist. Thus, only Angular routing cannot be used in cases when a certain route have to be securely restricted. I am thinking that routing has to be handled on both ends.
I am using Node so here is what I did:
//first evaluate the restricted route
app.get('/admin/*', function(req, res) {
//authorization
var authenticated = call_to_auth_service();
if (!authenticated) {
res.status(403);
res.end();
}
else {
//just remove the front /
var url = req.url.replace('/admin/', 'admin/');
res.render(url);
}
});
//open access - everything else goes back to the index page and there the angular routing takes over
app.get('*', function(req, res){
res.render('index');
});
This works, but I am not sure if this is the best approach. What do you think? Is this the proper way of handling the routing?
Thank you.

Angular adding extra logic to 404 handling on non angular routes

I have an angular site hosted in S3, and I have a 404 route set up (I use hash), if someone for example does
mysite/#/gibberish
goes to
mysite/#/404
and on the s3 bucket we have a redirect rule in place for
mysite/gibberish
goes to
mysite/404.html
all is well
Now I just want to add an extra logic on top that if someone types in
mysite/customerid
which is a 404 to somehow redirect this to an angular controller so I can send this request to right page.
So somehow in my redirect in S3 rule add a reg exp for some incoming request and rather than serve 404.html send it i.e. mysite/#/handlethis
Is this possible ?
Depending on the router of your choice, you could do something like the following (which is what we've done (well, not precisely this, but close)):
ui-router
app.config(function ($urlRouterProvider) {
var regx = /\/#\//; // match against /#/
$urlRouterProvider.otherwise(function ($state, $location) {
if (!regx.test($location.path()) { // if no match
$state.go('customHandlingState', /** params **/, /** configuration **/ });
// Transition to your custom handler state, with optional params/config.
}
});
});
You could pair this up with custom stateChange[Start|Stop|Error|Success] handlers in the run block of your app to customise it to your liking.
I would supply an example of how to do this with ngRoute, but I gave up on ngRoute two years ago and haven't looked back since. As such I have no suggestion to give, nor was I able to find a solution to the problem you present.
I would strongly suggest you scrap the S3 portion of this recipe as it will make your life a lot easier when it comes to client side routing (speaking from personal experience here, it's my opinion on the matter - not fact) and handle your 404's/500's on the client with custom state handlers.
If need be you could hook into some logging service and store some data whenever a client/person ends up in an erroneous state.
I suppose my 'counter question' is; What do you gain from using S3 redirect rules? So as to get a better understanding for the needs and goals here.
Some reference material to go along:
ui-router#$state.go
ui-router#$urlRouterProvider.otherwise
I would suggest using routeParams
https://docs.angularjs.org/api/ngRoute/service/$routeParams
the route would look like this:
/mysite/:cid
then access the id with the controller:
$routeParams.cid
I hope this could help
You can manually configure your server to always serve your index.html(your main html file which includes reference to angular script) on all incoming http requests. Client routing will be handled by Angular

AngularJs authorization layout

I am building a large application with Web API and AngularJs. I built the secure web api with authentication and claim-based authorizations. One requirement is that different users can view different modules on the same template.
I am new to AngularJs. I did the authentication in client side with the tokens. Also, in web api, I created a service to get all the permission given a user id. The response is a list of resource(contoller)/action(method) pairs. How do I implement the correct layout based on authorization rules on client side? Does that solely rely on web api permissions response and show/hide (ng-hide/ng-show) content based on the permissions?
Is this a good approach? What other modules/directives do I need to look into? Such as the loader for not loading the nested route until user request the parent route.
To add complexity, this site also need to work in bi-lingual. I think ng-translate. I mentioned this because it may open up another discussion on whether this may favor MVC instead of AngularJs. But the preference is Angular if the above two problem can be resolved.
All the authentication & authorisation & validation should be done server-side. You can adjust the user interface based on the roles/claims the server tells the browser the current user has.
One way to do this is to create something like a roles/userprofile controller, which will respond with a list of roles the current user has. On the client side you’ll probably want something you can inject everywhere, so you’re able to determine user interface behaviour.
myApp.factory(‘myUser’, function(Roles, $q) {
// Create a promise to inform other folks when we’re done.
var defer = $q.defer();
// For this example I’m using ngResource
Role.query({
/*
No params — let the server figure out who you ‘really’ are.
Depending on WebApi configuration this might just be
as simple as this.User (in context of the controller).
*/
}, function(roles) {
var user = {
roles: roles,
isInRole: function(role) {
return user.roles.indexOf(role) !== -1;
}
};
defer.resolve(user);
});
return defer;
});
Because the factory above is returning a promise we can enforce that myUser is resolved before a certain route/controller instance is created. One little trick I use is to gather all my route definitions in one object, loop through them with an angular.forEach and add a resolve.myUser property to each of them. You can use this to pre-load/initialize other stuff too.
Now inject the myUser anywhere you like:
myApp.controller(‘MyController’, function($scope, myUser) {
// Expose it on the current scope
$scope.myUser = myUser;
});
… and in your markup …
<div class=“my-content-thingy”>
<p>Lorem del ipsum …</p>
<button class=“btn” ng-if=“myUser.isInRole(‘content-editor’)”></button>
</div>
Note: You’ll probably want to use ng-if and not ng-show; the latter keeps the element in the DOM.
Just keep in mind that you don’t authenticate anything on the client side; that all done server side. A simple way is to place Authorize attributes on the appropriate controller actions.
Hope this helps.
A proper approach is to build AngularJS routing configuration as per Authorization on the server. This should be build just after the user is authorized and before the AngularJS app is initialized. That way the authorized user sees a "complete" app based on his roles etc. Using ng-show/ng-hide is not a good way to do it. Also each view should be doing only one thing. So load separate views based on the task that needs to be completed.
Regarding multi language support, this is independent of Authorization. Some time ago, I wrote a custom AngularJS filter that used the jQuery i18next plugin. It was a pretty simple implementation.
However you can now use https://github.com/i18next/ng-i18next
(Sorry for misunderstanding the problem).
I think that using ng-hide/show is not much of a problem. At the end of the day, the user does not have access to the data. I think it should rely on the api permissions + show/hide of presentation. Depends on the complexity you want... You can use MVC with angularjs since it's a large application.

Passport.js, Express.js, and Angular.js routing: how can they coexist?

I apologize this question turned out a bit long, but I have worked on this for some time and really needed to explain all the story.
Background: App based on MEAN stack, trying to authorize Facebook logins using Passport.js.
Following Passport.js guide I implemented something similar to:
// HTML
Add a Facebook login
// send to facebook to do the authentication
app.get('/connect/facebook',isLoggedIn, passport.authorize('facebook',
{ scope : 'email' })
);
// handle the callback after facebook has authorized the user
app.get('/connect/facebook/callback',
passport.authorize('facebook', {
successRedirect : '/profile',
failureRedirect : '/profile'
}));
Notice the target=_self in the html in order to skip Angular routing.
Clearly, authorization works fine. However, redirection does not work, as the routing is handled by Angular. After authorization I never land on /profile (but on the default Angular route).
Therefore, I tried with a custom callback as suggested by Passport.js here, with the hope of passing json data to Angular, and let Angular do the routing. I ended up doing something like:
// In the controller
$http.get("/connect/facebook").success(function(data){
// here I wait for json data from the server and do the routing
});
// I call this route from Angular
app.get('/connect/facebook',isLoggedIn,passport.authorize('facebook',
{ scope : 'email' })
);
// But Facebook lands here!
app.get('/connect/facebook/callback',function(req, res, next) {
passport.authorize('facebook', function(err, user, info) {
res.json({something:smtg});
...
Clearly custom callbacks work for local-login, as Passport.js explains. But here do you see the problem? I call /connect/facebook from Angular, but I should receive some json from /connect/facebook/callback.
I am about to give up Passport, but before this, do you see any solution which would allow landing on /profile after FB authorization, perhaps with a custom message? Many thanks for reading through.
EDIT:
The same question had been reported as an issue on the Passport-Facebook GitHub account. Some additional attempts have been posted there, but not quite the fix yet.
This is a bit more in depth than can be described in one answer, but I'll try to start pointing you in the right direction.
Essentially, Angular.js routes are not really HTML routes at all, but an internal route structure that happens to use the URL for use of the end user. Remember that Angular.js is a client script, and that a full page reload is not desired, as this will reload the entire script. Therefore, /# is used to trick the browser into jumping to a specific bit of code within the already loaded script. (as opposed to the traditional anchor location in the HTML document). Unfortunately (or fortunately), HTML 5 mode allows you to hide the /# part of the url, so instead of seeing http://somesite.com/#/someroute you just see http://somesite.com/someroute. Rest assured, however, that the /# is still there. Angular.js uses the HTML5 pushState (AKA HistoryAPI) to perform the magic replacement.
Given this, if you have called a server route, you are outside the Angular.js script, and any call to load the angular script again will start from the very beginning. You can't actually call your Angular.js route from the server without a full reload. Therefore, you are really doing a double route redirect here. Your server should be calling it's default route for angular, appending /#/someroute to the call. The angular.js page will load, parse off the /#, and redirect to the correct angular route. Keep in mind, however, that if there was any dependency on already loaded objects, those are no longer in memory. Therefore, any route accessed this way should operate as if it is an entry point to your application.
Effectively, you should try using successRedirect : '#/profile', keeping in mind that the profile route in angular should be treated as an app entry point.
Hopefully this gets you started.
If #Claies's way is not working, is it possible you have not get rid of the #= fragment from the facebook callback.
Have a read of this post

Resources