Using an object property as a function and as an object simultaneously - javascript

I was wondering if there is a way to declare an object property as a function, but also as an object, at the same time.
I have a JavaScript program that provides a simple API that sends AJAX requests to a server. My goal is trying to make this API as simple and human-readable as possible.
Basically, I'd like to make it possible to do this:
var app = new App();
app.get.client(123) // Get client ID 123
app.get.client.list() // Get an array of all clients
app.login('username', 'password') // Send credentials to log as username/password
app.login.as('John') // Login using credentials stored in a server-side constant
I doubt that's even possible as I've never anything like it, but I can't think of a more clear and human-readable way to lay out methods. Sure would be nice!

A function’s an object too!
app.get.client = function(id) {
// Get client by ID
};
app.get.client.list = function() {
// List them
};
works as you’d expect.
Personally, though, I’d find:
app.clients.byId(123)
app.clients
app.login('username', 'password')
app.loginAs('John')
more readable.

Related

Cookies or localstorage is best way?

I have some javascript, in which i need either cookies or localstorage to ensure variables aren't lost.
I'm a beginner and i'm not sure which is best to use, but i know both do sort of the same thing.
Basically, i need whatever the javascript is doing to be stored and even when user logs out / logs in, between any amount of days this is still being saved.
Can someone help?
<script>
$(document).ready(function() {
$("input").click(function(e) {
e.preventDefault();
var $challenge_div = $(this).parent().parent();
$challenge_div.data("finish", "false");
$challenge_div.removeClass("show").addClass("hide");
var $challenge_list = $("div[class='hide']");
$.each($challenge_list, function() {
var new_challenge = $(this);
if (new_challenge.data("finish") == false) {
new_challenge.removeClass("hide").addClass("show");
return false;
}
});
if ($("div[class='show']").length == 0) {
$("#message p").html("You finished all your challenges !");
}
});
});
</script>
I'm a beginner and i'm not sure which is best to use, but i know both do sort of the same thing.
Actually, they do very different things.
Cookies are for sending information back and forth between the server and client on every HTTP request.
Web storage is for storing information in the client (only).
For your use case, web storage would be the right thing to use, since you only want the information client-side. It also has a reasonable API (unlike cookies). You can store a string:
localStorage.setItem("key", "value");
// or
localStorage.key = "value"; // See warning below
// or
localStorage["key"] = "value"; // See warning below
(I'd recommend using the setItem method instead of directly assigning to a property on localStorage, so there's no chance of your conflicting with any of localStorage's methods!)
...and get it back later, even in the user left the page entirely and came back:
var x = localStorage.getItem("key");
// or
var x = localStorage.key; // See warning above
// or
var x = localStorage["key"]; // See warning above
console.log(x); // "value"
And remove it if you want:
localStorage.removeItem("key");
Often, it makes sense to use JSON to store your client-side information, by using some kind of settings object and storing it by converting it to JSON:
localStorage.setItem("settings", JSON.stringify(settings));
...and using JSON.parse when getting it (back):
var settings = JSON.parse(localStorage.getItem("settings"));
if (!settings) {
// It wasn't there, set defaults
settings = {/*...*/};
}
I'm not sure which is best to use, but I know both do sort of the same thing.
Not really. A key difference in Cookies and Local Storage is that cookies will be sent along with every server request you make.[1]
Requesting some data using AJAX, cookies will be included in the request.
Navigating to a new page, cookies will be included in the request.
So the disadvantage is that you are transferring additional data to the server every time you make a request. More so if you are storing a lot of data into the cookies.
Local storage is merely kept in the browser (until you clear it explicitly), but it is not sent along with every request.
The decision is simple, if you require that data available to the server with request parameters, you should use cookies, else use local storage.
One more thing, from what you are saying, it seems like you intend to store the user's progress in a game. Keep in mind that both cookie and local storage are accessible to the user. So they can tamper with that data if they want.
If it is critical to prevent user's from changing the data, you must store the data on the server instead.
Do read: JavaScript: client-side vs. server-side validation
[1]. This may not be entirely true with HTTP2 but that's a different topic.

Update context variables using .js. Its possible?

i'm trying to send a user name to my WCS but i not sure how can i do that using a request. My consersation ask for the user email and use a js script to return a json from my sql server...
I'm doing something like that:
What's your email? // WCS
caique.rodrigues#test.com //USER
Just a minute.<script>getUserInformation('caique.rodrigues#test.com');</script> // WCS
nodeTrue //USER (I sent this after confirmated if the user exist and got the user name.)
Hi <span id='myText'></span><script>document.getElementById('myText').innerHTML = userName;</script>! //WCS
I know this is not the best way to do that but it is working. I'm trying to call my js functions using something like "output:{'action':}" (And handle it in my node.js like the 'car dashboard sample'), but, it's possible send a varible from my .js to a conversation context?
I had the same problem a few months ago. In this case, I used functions for access the context variables on the client side (Using this example from Ashley and accessing the context), but, for security issues of some company data, I did need to use custom code in the server-side... And, for this case, you can use the method call for Watson Conversation, and access the context variables and creates values with custom code in your favor.
Something like this:
conversation.message(payload, function (err, data) {
data.context.yourValue = returnFromYourDatabase;
if (err) {
return res.status(err.code || 500).json(err);
}
updateMessage(payload, data, req, res);
});
});
You can see the function updateMessage, this call is one example from IBM Developers, and this function is used to update the message in every request. You can use this function to set too, because this function get the parameters from the call.
For example:
function updateMessage(input, response) {
response.context.yourValue = returnFromYourDatabase;
var responseText = null;
if (!response.output) {
response.output = {};
}
}
See API Reference for Watson Conversation Service using Node.js.
See the Project with Node.js by IBM Developers.

JS: Node.js and Socket.io - globals and architecture

Dear all,
Im working with JS for some weeks and now I need a bit of clarification. I have read a lot of sources and a lot of Q&A also in here and this is what I learned so far.
Everything below is in connection with Node.js and Socket.io
Use of globals in Node.js "can" be done, but is not best practice, terms: DONT DO IT!
With Sockets, everything is treated per socket call, meaning there is hardly a memory of previous call. Call gets in, and gets served, so no "kept" variables.
Ok I build up some chat example, multiple users - all get served with broadcast but no private messages for example.
Fairly simple and fairly ok. But now I am stuck in my mind and cant wrap my head around.
Lets say:
I need to act on the request
Like a request: "To all users whose name is BRIAN"
In my head I imagined:
1.
Custom object USER - defined globally on Node.js
function User(socket) {
this.Name;
this.socket = socket; }
2.
Than hold an ARRAY of these globally
users = [];
and on newConnection, create a new User, pass on its socket and store in the array for further action with
users.push(new User(socket));
3.
And on a Socket.io request that wants to contact all BRIANs do something like
for (var i = 0; i < users.length; i++) {
if(user[i].Name == "BRIAN") {
// Emit to user[i].socket
}}
But after trying and erroring, debugging, googling and reading apparently this is NOT how something like this should be done and somehow I cant find the right way to do it, or at least see / understand it. can you please help me, point me into a good direction or propose a best practice here? That would be awesome :-)
Note:
I dont want to store the data in a DB (that is next step) I want to work on the fly.
Thank you very much for your inputs
Oliver
first of all, please don't put users in a global variable, better put it in a module and require it elsewhere whenever needed. you can do it like this:
users.js
var users = {
_list : {}
};
users.create = function(data){
this._list[data.id] = data;
}
users.get = function(user_id){
return this._list[user_id];
};
users.getAll = function(){
return this._list;
};
module.exports = users;
and somewhere where in your implementation
var users = require('users');
For your problem where you want to send to all users with name "BRIAN",
i can see that you can do this good in 2 ways.
First.
When user is connected to socketio server, let the user join a socketio room using his/her name.
so it will look like this:
var custom_namespace = io.of('/custom_namespace');
custom_namespace.on('connection', function(client_socket){
//assuming here is where you send data from frontend to server
client_socket.on('user_data', function(data){
//assuming you have sent a valid object with a parameter "name", let the client socket join the room
if(data != undefined){
client_socket.join(data.name); //here is the trick
}
});
});
now, if you want to send to all people with name "BRIAN", you can achieve it by doing this
io.of('/custom_namespace').broadcast.to('BRIAN').emit('some_event',some_data);
Second.
By saving the data on the module users and filter it using lodash library
sample code
var _lodash = require('lodash');
var Users = require('users');
var all_users = Users.getAll();
var socket_ids = [];
var users_with_name_brian = _lodash.filter(all_users, { name : "BRIAN" });
users_with_name_brian.forEach(function(user){
socket_ids.push(user.name);
});
now instead of emitting it one by one per iteration, you can do it like this in socketio
io.of('/custom_namespace').broadcast.to(socket_ids).emit('some_event',some_data);
Here is the link for lodash documentation
I hope this helps.

Anonymous Users with Parse SDK

I'm aware that Parse.com does not support Anonymous Users for the Javascript SDK which is what I'm using now. I've asked a Parse staff member what an alternative for those using the Parse Javascript SDK and want to have something like the Anonymous User feature offered for the Parse ios SDK might be. I was told by the Parse staff member: "This is not officially supported yet, but you might be able to implement something similar by generating a random username and password that is stored in localStorage for this user". Now, right now, the following code allows me to save information to my Parse database
var MYObject = Parse.Object.extend("MYObject");
var myObject = new MYObject();
var SomeStuff = "Test";
myObject.set("RECORD",SomeStuff);
myObject.save(null, { success: function(myObject)
{ //alert alert('New object created with objectId: ' + myObject.id); }
This creates a new class then adds "RECORD" and "Test". It works. Yet this is saved without needing a username or password at all. I'm wondering why just allowing users to save data like that can't be sufficient instead of having the Anonymous User feature Parse offers or in my case, an alternative solution for the Anonymous User feature since Anonymous User is not supported by the Parse Javascript SDK which is what I'm using. Is the reason the Anonymous User feature offered in the first place a matter of security? Should I resort to the alternative solution given to me by Parse staff or is it unnecessary?
can you just generate a 'random' or a 'guid' and then plug that into User.username with password&email undefined... On the insert of that user, you have a valid Parse.User object that is anonymous. The return from the User.insert() is 'token' which never expire. You can use cookie to store the {"token":val, "username":val}.
Without a passwd, you never log the user in and will always be forced to call cloudcode where you can pass in the user's token (-H "X-Parse-Session-Token: rcid...") in place of a validated session established with 'login'.
I've used this technique in REST API where i want to onboard users without any input to text fields. They provide no info , only agreeing to use an anonymous cloud account.
I know this answer is very late, but it's relevant because nothing has changed. There is no Class for Anonymous users in the Parse JS SDK.
The reason why you can create, save, edit and delete objects without having an User Session is because you can create objects that anyone can use; I.E, "Public Objects". You can set ACL credentials on these objects as well, but you will not be associating new objectsIds with userObjectIds and therefore will only be able to update said objects in Cloud Code using your apps MasterKey.
var Foo = Parse.Object.extend("Foo");
var foo = new Foo();
foo.set("message", "Hello Foo");
foo.save().then(function(foo){
//foo was saved
//anyone can edit it right now
//make it disappear into a black hole
//in other words, nobody can edit without Master Key
var acl = new Parse.ACL();
acl.setPublicReadAccess(false); //nobody can read it
acl.setPublicWriteAccess(false);//nobody can write it
foo.setACL(acl);
return foo.save();
}).then(function(foo){
//since foo was returned, we can still read it, but
//we cannot edit it anymore...
foo.set("message", "cannot update without Master Key");
return foo.save();
}).then(function(foo){
//this will not run
}, function(error){
//catch error for cannot update foo
log(error);
});
In this example, I start off by creating the Foo object. Then I update the message column and save it. The saved object is returned and I create an ACL that will prevent anyone for reading and writing to Foo. Then I set Foos ACL and save it again. The saved object is returned and I try to update the message column again. This time an error occurs and the error callback logs the error. This happens because I cannot update foo anyone, unless I use the Master Key and that must take place in Cloud Code.
Parse.Cloud.useMasterKey();
foo.save().then.... //after second return of foo.save() above

Javascript json eval() injection

I am making an AJAX chat room with the guidance of an AJAX book teaching me to use JSON and eval() function.
This chat room has normal chat function and a whiteboard feature.
When a normal text message comes from the php server in JSON format, the javascript in browser does this:
Without Whiteboard Command -------------------------------------------
function importServerNewMessagesSince(msgid) {
//loadText() is going to return me a JSON object from the server
//it is an array of {id, author, message}
var latest = loadText("get_messages_since.php?message=" + msgid);
var msgs = eval(latest);
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
} ...
The whiteboard drawing commands are sent by server in JSON format with special user name called "SVR_CMD", now the javascript is changed slightly:
With Whiteboard Command --------------------------------------------------
function importServerNewMessagesSince(msgid) {
//loadText() is going to return me a JSON object from the server
//it is an array of {id, author, message}
var latest = loadText("get_messages_since.php?message=" + msgid);
var msgs = eval(latest);
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
if (msg.author == "SVR_CMD") {
eval(msg.contents); // <-- Problem here ...
//I have a javascript drawLine() function to handle the whiteboard drawing
//server command sends JSON function call like this:
//"drawLine(200,345,222,333)" eval() is going to parse execute it
//It is a hacker invitation to use eval() as someone in chat room can
//insert a piece of javascript code and send it using the name SVR_CMD?
else {
displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
}
} ...
Now, if the hacker changes his username to SVR_CMD in the script, then in the message input start typing javascript code, insdead of drawLine(200,345,222,333), he is injecting redirectToMyVirusSite(). eval() will just run it for him in everyone's browser in the chat room.
So, as you can see, to let the eval to execute a command from an other client in the chat room is obviously a hacker invitation. I understand the book I followed is only meant to be an introduction to the functions. How do we do it properly with JSON in a real situation?
e.g. is there a server side php or .net function to javascriptencode/escape to make sure no hacker can send a valid piece of javascript code to other client's browser to be eval() ? Or is it safe to use JSON eval() at all, it seems to be a powerful but evil function?
Thank you,
Tom
What is this book? eval is evil, there is not a single reason to use it, ever.
To transform a JSON string into a javascript object, you can do the following:
var obj = JSON.parse(latest)
Which means you can then use:
[].forEach.call(obj, function( o ) {
// You can use o.message, o.author, etc.
} )
To do the opposite (javascript object -> JSON string), the following works:
var json = JSON.stringify(obj)
It only is unsafe if the executed code is generated by other clients and not by the server. Of course you would need to prevent anybody to use that name, though I don't understand why you would use the "author" field? Just send an object {"whiteboard":"drawLine(x,y,z)"} instead of {"author":"SVR_CMD","contents":"drawLine(x,y,z)"}.
But it is right, eval() is still an invitation for hackers. One can always send invalid data and try to influence the output more or less directly. The only way for escaping is a proper serialisation of the data you want to receive and send - the drawings data. How do you receive the whiteboard commands? There is no serverside "escape" function to make javascript code "clean" - it would always be a security hole.
I would expect a serialisation like
message = {
"author": "...", // carry the information /who/ draws
"whiteboard": {
"drawline": [200, 345, 222, 333]
}
}
so you can sanitize the commands (here: "drawline") easiliy.
The use of eval() might be OK if you have very complex commands and want to reduce the transferred data by building them serverside. Still, you need to parse and escape the received commands from other clients properly. But I'd recommend to find a solution without eval.
Setting eval issue aside, do not use field that can be filled by user - .author in your code - for authentication purposes. Add another field to your JSON message, say .is_server_command that when present, would signify special treating of message. This field is will be not depended on user input and thus wouldn't be hijacked by "hacker".

Categories

Resources