I am using Javascript, webdriverio (v2.1.2) to perform some data extraction from an internal site. The internal site is SSO enabled, so if I have been authenticated on another application, I need not login for this application (common in enterprise intranet applications).
I plan to achieve the below,
Create a client with required capabilities
Pass the required URL
For fun : Print the title of the page
Check if an element exist on the page. If yes, then it's a login page. If not, then it's not login page
login = function (username, password) {
if (!browserClientUtil) {
throw "Unable to load browserClientUtil.js";
}
browserClientUtil
.createClient()
.url(_Url)
.title(function (err, res) {
console.log('Title is: ' + res.value);
}) .isExisting('input#login_button.login_button', function (err, isExisting) {
browserClientUtil.getCurrentClient()
.setValue('input#USER.input', username)
.setValue('input#PASSWORD.input', password)
//.saveScreenshot('ultimatixLoginDetails.png')
.click('input#login_button.login_button')
.pause(100);
handlePostLogin();
});
};
Is this the best way to do? I tried to separate the code for verifying login page in a separate function, it didn't work as everything in webdriver happens as part of callback and I am not sure if I am doing it in a right way.
How do I return from a callback, that will in-turn be the final value returned by that function?
login = function (username, password) {
if (!browserClientUtil) {
throw "Unable to load browserClientUtil.js";
}
browserClientUtil
.createClient()
.url(_Url)
.title(function (err, res) {
console.log('Title is: ' + res.value);
});
if(isThisLoginPage()){
browserClientUtil.getCurrentClient()
.setValue('input#USER.input', username)
.setValue('input#PASSWORD.input', password)
//.saveScreenshot('ultimatixLoginDetails.png')
.click('input#login_button.login_button')
.pause(100);
handlePostLogin();
}
};
isThisLoginPage = function() {
var client = browserClientUtil.getCurrentClient();
if(!client) {
throw "Unable to get reference for current client, hence cannot validate if this is login page.";
}
client.isExisting('input#login_button.login_button', function (err, isExisting) {
if(isExisting) {
return true;
}
});
return false;
};
You can create your own workflow by creating own commands that wrap other ones. For example you can make an own command to login:
browserClientUtil.addCommand("login", function(url, user, pw, cb) {
this.url(url)
.setValue('#username', user)
.setValue('#password', pw)
.submitForm('#loginForm')
.call(cb);
});
This allows you to hide "complex" asynchronous webdriver actions behind a simple function. It is easy to create an powerful toolchain. At the end your test script looks like:
browserClientUtil
.login("http://example.com/login", "john.doe", "testpass")
.getTitle(function(err, title) {
console.log(title);
})
// ...
Cheers
Related
I am using http cloud function ( https://firebase.google.com/docs/functions/http-events ) to write documents to a firestore collection:
exports.hello = functions.https.onRequest(
(req: { query: { name: string } }, res: { send: (arg0: string) => void }) => {
console.log(req.query.name);
var name = req.query.name || 'unknown';
res.send('hello' + name);
admin
.firestore()
.collection('ulala')
.doc()
.set({ token: 'asd' }, { merge: true });
}
);
this is a test. The problem is that, once you deploy and get the link to the function, it is executable by everyone. I would like instead that only I (project owner) can use it . Is it possible to do this?
One possible solution is to restrict your HTTPS Cloud Function to only a specific "Admin" user of your app.
There is an official Cloud Function sample which shows how to restrict an HTTPS Function to only the Firebase users of the app/Firebase project: Authorized HTTPS Endpoint sample.
You need to adapt it to check if the user is the Admin user. For example by checking the userID in the try/catch block at line 60 of the index.js file (untested).
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
if (decodedToken.uid !== "<the admin user uid>") {
throw new Error("Wrong user");
} else {
req.user = decodedIdToken;
next();
}
return;
} catch (error) {
functions.logger.error('Error while verifying Firebase ID token:', error);
res.status(403).send('Unauthorized');
return;
}
The two drawbacks of this approach are:
Your Admin user needs to be declared as a Firebase project user in the Authentication service
You hard code the Admin userID in your Cloud Function (you could use the Google Cloud Secret Manager service to securely store it as a configuration value, see the doc).
IMPORTANT SIDE NOTE:
In your Cloud Function you call the send() method before the asynchronous work is complete:
res.send('hello' + name);
admin
.firestore()
.collection('ulala')
.doc()
.set({ token: 'asd' }, { merge: true });
By calling the send() method, you actually terminate the Cloud Function, indicating to the Cloud Functions instance running your function that it can shut down. Therefore in the majority of the cases the asynchronous set() operation will not be executed.
You need to do as follows:
admin
.firestore()
.collection('ulala')
.doc()
.set({ token: 'asd' }, { merge: true })
.then(() => {
res.send('hello' + name);
})
I would suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series as well as read this page of the documentation which explain this key point.
I am trying to build a full stack web application using React JS and Spring. I have created an API for Login.
#PostMapping("/users/login")
public Status loginUser(#Valid #RequestBody PortalUser user) {
List<PortalUser> users = userRepository.findAll();
for (PortalUser other : users) {
if (other.equals(user)) {
return Status.SUCCESS;
}
}
return Status.FAILURE;
}
This API checks if user has entered correct login credentials or not. If yes, then it returns Enum as "SUCCESS" and if not then it returns Enum as "FAILURE".
The web API works fine on Postman. I now want to call the same from my frontend but I am unable to do so. Could anyone help me out with the same?
const user_base_url = "http://localhost:8080/users";
class CustomerService{
authenticateUser(user) {
return axios.post(user_base_url+ '/login', user);
}
}
I have created this function to call the API using axios.
validateUser = () => {
let user = {
username: this.state.email,
password: this.state.password
};
authenticateUser(user);
I am unable to proceed after this.
I basically want to authenticate the User when the login button (the Validate User function is called) is pressed.
Any help would be highly appreciated.
You can do something like this
const user_base_url = "http://localhost:8080/users";
class CustomerService{
authenticateUser(user) {
return axios.post(user_base_url+ '/login', user)
.then((res)=>{
if(res.data === 'SUCCESS') {
//user logged in
} else if(res.data === 'FAILURE') {
//login failed
}})
.catch(err) {
console.error(err)
}
}
}
I got this error
"type error alert is not a function ".
So I created an alert function but got this error
reference error alert is not defined
when I try to execute its function
function alertMessage(messageObject) {
alert(messageObject);
return true;
}
app.post("/login", function(req, res) {
const username = req.body.username;
const password = req.body.password
User.findOne({
email: username
},
function(err, foundUser) {
if (foundUser) {
if (foundUser.password !== password) {
alertMessage("Password Is Incorrect");
} else {
if (foundUser) {
if (foundUser.password === password) {
res.render("compose");
}
}
};
};
});
});
From app.post, I'm guessing this is code running in Node.js via Express.js. Node.js doesn't have an alert function, that's something browsers provide. In app.post you're replying to a response from the browser. If there's a login error, you need to send a reply to the post saying there's a login error. It looks like your code is expecting to present a page in response to the post (rather than being called via ajax), so you'd render a page saying the login failed. (You might consider using ajax instead, so the page doesn't have to refresh in this case, but that's beside the point.)
I am having trouble using the Parse Server JS SDK to edit and save a user.
I am signing in, logging in and retrieving the user just fine, I can call without exception user.set and add/edit any field I want, but when I try to save, even when using the masterKey, I get Error 206: Can t modify user <id>.
I also have tried to use save to direcly set the fields, same result.
A interesting thing is that in the DB, the User's Schema get updated with the new fields and types.
Here is my update function:
function login(user, callback) {
let username = user.email,
password = user.password;
Parse.User.logIn(username, password).then(
(user) => {
if(!user) {
callback('No user found');
} else {
callback(null, user);
}
},
(error) => {
callback(error.message, null);
}
);
}
function update(user, callback) {
login(user, (error, user) => {
if(error) {
callback('Can t find user');
} else {
console.log('save');
console.log('Session token: ' + user.getSessionToken());
console.log('Master key: ' + Parse.masterKey);
user.set('user', 'set');
user.save({key: 'test'}, {useMasterKey: true}).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR - ' + require('util').inspect(err));
callback(error.message);
}
);
}
});
}
And a exemple of the error:
update
save
Session token: r:c29b35a48d144f146838638f6cbed091
Master key: <my master key>
ERR- ParseError { code: 206, message: 'cannot modify user NPubttVAYv' }
How can I save correctly my edited user?
I had the exact same problem when using Parse Server with migrated data from an existing app.
The app was created before March 2015 when the new Enhanced Sessions was introduced. The app was still using legacy session tokens and the migration to the new revocable sessions system was never made. Parse Server requires revocable sessions tokens and will fail when encountering legacy session tokens.
In the app settings panel, the Require revocable sessions setting was not enabled before the migration and users sessions were not migrated to the new system when switching to Parse Server. The result when trying to edit a user was a 400 Bad Request with the message cannot modify user xxxxx (Code: 206).
To fix the issue, I followed the Session Migration Tutorial provided by Parse which explain how to upgrade from legacy session tokens to revocable sessions. Multiple methods are described depending on your needs like enableRevocableSession() to enable these sessions on a mobile app, if you're only having a web app, you can enforce that any API requests with a legacy session token to return an invalid session token error, etc.
You should also check if you're handling invalid session token error correctly during the migration to prompt the user to login again and therefore obtain a new session token.
I had the same error and neither useMasterKey nor sessionToken worked for me either. :(
Here's my code:
console.log("### attempt 1 sessionToken: " + request.user.getSessionToken());
var p1 = plan.save();
var p2 = request.user.save(null, {sessionToken: request.user.getSessionToken()});
return Parse.Promise.when([p1, p2]).then(function(savedPlan) {
...
}
I see the matching session token in log output:
2016-08-21T00:19:03.318662+00:00 app[web.1]: ### attempt 1 sessionToken: r:506deaeecf8a0299c9a4678ccac47126
my user object has the correct ACL values:
"ACL":{"*":{"read":true},"PC7AuAVDLY":{"read":true,"write":true}}
I also see a bunch of beforeSave and afterSave logs with user being "undefined". not sure whether that's related.
beforeSave triggered for _User for user undefined:
I'm running latest parser-server version 2.2.18 on Heroku (tried it on AWS and results are the same)
function login(logInfo, callback) {
let username = logInfo.email,
password = logInfo.password;
Parse.User.logIn(username, password).then(
(user) => {
if(!user) {
callback('No user found');
} else {
callback(null, user);
}
},
(error) => {
callback(error.message, null);
}
);
}
function update(userInfo, data, callback) {
login(userInfo, (error, user) => {
if(error) {
callback('Can t find user');
} else {
getUpdatedData(user.get('data'), data, (error, updateData) => {
if(error) {
callback(error);
} else {
user.save({data: updateData}, /*{useMasterKey: true}*/ {sessionToken: user.get("sessionToken")}).then(
(test) => {
callback();
}, (err) => {
callback(error.message);
}
);
}
});
}
});
}
For some reason, retrying to use sessionToken worked.
This is not how asynchronous functions work in JavaScript. When createUser returns, the user has not yet been created. Calling user.save kicks off the save process, but it isn't finished until the success or error callback has been executed. You should have createUser take another callback as an argument, and call it from the user.save success callback.
Also, you can't create a user with save. You need to use Parse.User.signUp.
The function returns long before success or error is called.
I inherited a Windows 8 application that is written with XAML. So in C# when I make this call
user = await MobileServices.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
(This is for Azure Mobile Services)
The user object is ONLY giving me the Token and the MicrosoftAccount:..............
In order to get to authenticate people, I need to be able to see WHO is requesting access...
I looking at articles like below, but I seem to be missing something? Is this javascript in the article something I would have to write in Node.js?
Example article:
http://blogs.msdn.com/b/carlosfigueira/archive/2013/12/12/expanded-login-scopes-in-azure-mobile-services.aspx
Currently to be able to get more information about the logged in user, you need to make a second call to the service to retrieve the user info. You don't really need to ask for additional login scopes (the topic of the post you mentioned) to retrieve the user name, since that is given by default for all the providers.
This post should have the code you need to write in the server side (node.js) to get more information about the logged in user. The TL;DR version is given below:
On the server side: add this custom API (I'll call it "userInfo"; set the permission of GET to "user", and all others to admin):
exports.get = function(request, response) {
var user = request.user;
user.getIdentities({
success: function(identities) {
var accessToken = identities.microsoft.accessToken;
var url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + accessToken;
var requestCallback = function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.error('Error sending data to the provider: ', err);
response.send(statusCodes.INTERNAL_SERVER_ERROR, body);
} else {
try {
var userData = JSON.parse(body);
response.send(200, userData);
} catch (ex) {
console.error('Error parsing response from the provider API: ', ex);
response.send(statusCodes.INTERNAL_SERVER_ERROR, ex);
}
}
}
var req = require('request');
var reqOptions = {
uri: url,
headers: { Accept: "application/json" }
};
req(reqOptions, requestCallback);
}
});
}
On the client side, after a successful login, call that API:
user = await MobileServices.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
var userInfo = await MobileServices.MobileService.InvokeApiAsync(
"userInfo", HttpMethod.Get, null);
userInfo will contain a JObject with the user information. There is an open feature request to make this better at http://feedback.azure.com/forums/216254-mobile-services/suggestions/5211616-ability-to-intercept-the-login-response.