Check if user has been timed out in Devise - javascript

Because I'm using javascript to perform an action instead of a controller method, I need to check if a user has been timed out before that action is rendered. To do this, I tried added and deleting cookies in my application_controller.rb like this:
Warden::Manager.after_set_user do |user,auth,opts|
auth.cookies[:signed_in] = 1
end
Warden::Manager.before_logout do |user,auth,opts|
auth.cookies.delete :signed_in
end
and in my javascript file:
myCookie = getCookie("signed_in");
if (myCookie == null)
{
//redirect to sign in page
} else {
//perform my action
}
function getCookie(name) {
var dc = document.cookie;
var prefix = name + "=";
var begin = dc.indexOf("; " + prefix);
if (begin == -1) {
begin = dc.indexOf(prefix);
if (begin != 0) return null;
}
else
{
begin += 2;
var end = document.cookie.indexOf(";", begin);
if (end == -1) {
end = dc.length;
}
}
return decodeURI(dc.substring(begin + prefix.length, end));
}
However, this doesn't seem to work on session timeout, only on logout. I've seen a couple hacky ways to accomplish this, but I can't seem to find anything directly from Devise or Warden to use or override a method to test if a user has been timed-out or take action if a user has been timed-out. I'm on Rails 5 and Devise 4.2.1.

You will need to make a request that action to the server to see if the user has timedout, because when you set the timeout, that is server side, so there is no change in the client when this time passed out, the only way to know is to make an ajax request to the server to see if the session timedout user.timedout?(Time.now.utc) or make the validation directly on the request for the action (in the controller), the user will always try to run the action, but on the server, you validate if the user has timedout, then you don't let them do it and redirect to the login page.

Related

How can I display a message or loading animation while flask.send_from_directory is processing?

I have a simple web application that calls the Spotify API, and, among other things, allows a user to download their "Liked Songs". The 'download' view writes a csv file of the 'Liked Songs' to a tmp directory and then downloads that file using flask.send_from_directory.
#bp.route('/download', methods=['GET'])
#login_required
def download():
...
# Edited with more details below
return send_from_directory(
temp_dir,
target[0],
as_attachment=True,
attachment_filename=file_name
)
It all works as intended but can take a while if a user has many thousands of Liked Songs. During that 20+ seconds I would like to give the user some feedback that the task is processing -- a message or loading animation. I have written a loading animation used elsewhere in my site, but in those cases the view ends with a redirect. I also can reveal a modal with javascript. However, a modal or animation will not automatically close when the "Open Liked Songs.csv" window pops up asking the user to "Open" or "Save", nor after one of those is selected (i.e., when the download view is completed).
Is there a way to trigger an event in the Browser Object Model using the window object for the "Open Liked Songs.csv" pop-up window? I do not know how to identify that specific window. Any other suggestions?
UPDATE:
Thanks to #v25 I realize more details are needed. Here is my code again:
#bp.route('/download', methods=['GET'])
#login_required
def download(backup=False):
...
if backup: # For download of 'Liked Songs'
playlist_id = '000library'
file_name = 'Liked Songs.csv'
url = get_endpoint("get-user-saved-tracks")
query_limit = 50
count = 0
tracks = []
# The Spotify endpoint is limited at maximum of 50
# so I need to iterate with offsets.
# I think this is what is causing the delay.
while not len(tracks) % query_limit:
params["limit"] = query_limit
params["offset"] = query_limit * count
section = sh.get_playlist_tracks(header, url, **params)
tracks += section["items"]
count += 1
if not tracks:
flash("Something went wrong. We couldn't get your playlist.")
return redirect(url_for('routes.profile'))
with NamedTemporaryFile(
mode='w+t',
encoding='utf8',
newline='',
prefix=playlist_id + '.',
dir=temp_dir,
delete=False,
) as csvfile:
...
# Writes csv file to temp directory
...
return send_from_directory(
temp_dir,
target[0],
as_attachment=True,
attachment_filename=file_name
)
When I monitor this process with my browser's (FireFox) dev tools 'Network' tab, I can see that it takes a while before status code 200 is returned for my '/download' request. At that point the 'Open Liked Songs.csv' window appears. I think I need some way to trigger my javascript closeModal() when status 200 is returned (polling?). As #v25 suggests below, I am pretty sure it is the iteration of 50-track chunks at the Spotify API that is slowing this down.
I was able to accomplish my goal by setting a client-side cookie when the download is complete. I used the flask.Response.set_cookie method:
def download():
# Code interacting with Spotify API
# Code writing API response to csv file
resp = make_response(send_from_directory(
temp_dir,
target_file,
as_attachment=True,
attachment_filename=file_name
))
resp.set_cookie(
'download',
value=cookie_val,
samesite='Strict'
)
return resp
I use javascript to check for the presence of that cookie at intervals, and to close the relevant modal when the cookie is found:
function closeModal(element) {
element.className += " hidden";
}
function showModalProc() {
showModal("processing");
document.getElementById("processing").scrollIntoView();
}
/* Borrowed getCookie(cname) from
https://www.w3schools.com/js/js_cookies.asp */
function getCookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for(let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
setInterval(checkCookie, 1000); // Checks for cookie every second.
function checkCookie() {
let cookie = getCookie("download");
element = document.getElementById("processing");
if (cookie != "" && cookie.startsWith("{{ session.user.id }}")) {
closeModal(element);
// Clear this cookie; set SameSite=Strict to avoid browser warning.
document.cookie = "download=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict;";
}
}

Get HTML content update of an URL

Basically i want to have an update content of the web,
https://www.investing.com/indices/indices-futures
In Dow 30, the last value is updating itself(real-time update from Investing.com server) and i would like to know is there any method to capture the change of values without requesting the website again, so that i can update on my code asynchronously. Since all i found online about being notified on change is based on their own html, but in my case it is external url, so i am asking here to gain some insight
You can add some code into the chrome console and track this value every second to notify you.
let last_value = -1
let class_selector = 'pid-8873-last'
setInterval(function() {
let v = document.getElementsByClassName(class_selector)[0].innerText
if (v != last_value) {
console.log("Value as been updated to " + v)
last_value = v
}
}, 1000)
> Value as been updated to 25,799.5
> Value as been updated to 25,798.5
But you must have a browser open, and create an ajax request when value is updated.
If you don't want any Browser, but be run into a server, you can check PhantomJS
EDIT WITH PHANTOMJS
They're some update to do to work with PhantomJS.
You need to replace let by var
document isn't accessible, so you need to use evaluate
https may require to add --ssl-protocol=any
./test.js
var page = require('webpage').create();
page.open('https://www.investing.com/indices/indices-futures', function(status) {
var last_value = -1
setInterval(function() {
var value = page.evaluate(function() {
return document.getElementsByClassName('pid-8873-last')[0].innerText
})
if (value != last_value) {
console.log("Value as been updated to " + value)
last_value = value
}
}, 1000)
// phantom.exit()
})
Then run it from the same directory:
# phantomjs test.js
Value as been updated to 25,799.0
I think you need to check what is websocket. this would be cool start; https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

Session doesn't work

So, I'm trying to make a web application. During registration, I require from the user to enter his working experience. While at it, I test if the time span in which the user has been working overlaps with previously entered time spans, just to warn him of it. I use controller and JS script for this.
This is my controller method:
public void TimeCheck()
{
string time = Request.QueryString.ToString();
using (ITExpertsContext db = new ITExpertsContext())
{
int id = db.Users.FirstOrDefault(x => x.Email.Equals(User.Identity.Name)).UserId;
List<WorkingAt> currentHistory = db.WorkingAts.Where(x => x.UserId == id).ToList();
TimeFrame frame = new TimeFrame();
frame.Since = DateTime.Parse(time.Split('&')[0].Split('=')[1]);
frame.Until = DateTime.Parse(time.Split('&')[1].Split('=')[1]);
foreach (WorkingAt w in currentHistory)
{
if ((w.Since < frame.Until && w.Until > frame.Until) || (w.Since < frame.Since && w.Until > frame.Since))
{
Session["Time"] = "1";
return;
}
}
Session["Time"] = "0";
}
}
And this is my JS method from the view:
function TimeCheck() {
var request = new XMLHttpRequest();
request.open("GET", "/account/TimeCheck/?since=" + $("#Since").val() + "&until=" + $("#Until").val());
request.send();
request.onreadystatechange = function () {
if (request.status == 200 & request.readyState == 4) {
if (sessionStorage.Time == "1") {
var choice = confirm("The time you selected overlaps with your previous working experience. Do you still want to add it?");
if (choice == true) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
}
}
Problem with all of this is my session does not change at all, aka does not exist. It works well when I set it from JS (for testing purpose). What am I doing wrong?
I'm using latest VS 2017 ver 15.7, so I'm assuming my MVC is 5.2.3, if that's even needed to be known.
EDIT:
The code in controller reaches to the end without any problems. I did the debug of controller line by line and it works as I designed it. Just before "return" I tried reading session["Time"] and it shows it's there. Once the execution goes back to the JS the browser does not show there's a session data under the key "Time", and therefore my "if(sessionStorage.Time == "1")" statement is pointless.
sessionStorage is defined in the HTML5 spec. which is client side only, so setting a Session variable on the server has no effect on sessionStorage client side.
See: Does HTML5 sessionStorage exist on the server or client?

Save last language preference of the user in a Cookie

I'm running a Wordpress Multisite Installation with two languages: Hebrew and English.
I have a plugin called Geo IP that helps me to redirect users based on their IP country.
But actually I need more.
I would like to save the last language the user choose.
Example :
if a user close the tab of my site on the english language, I would like that when he comes back, he'll get the english language. Vice versa for Hebrew.
I'm not a pro developer, but I think a cookie can be a solution, and I would like the solution to be in JS if possible.
Update: the code I made ! WDYT guys ?
function get_language {
var myLang = getcookie ('language');
if ( myLang == 'hebrew') {
window.location = "http://zeek.me/he/";
}
else if ( myLang == 'english') {
window.location = "http://zeek.me";
}
else {
window.location = "http://zeek.me";
}
}
function set_language(lang) {
var setLang = setcookie ('language', lang, 30);
var englishClick = document.getElementById('#english_lang');
var hebrewClick = document.getElementById('#hebrew_lang');
englishClick.addEventListener('click', function() {
set_language('english');
})
hebrewClick.addEventListener('click', function() {
set_language('hebrew');
})
}
What you guys think ?
Any solution ?
Thanks,
Simon
As you want a solution with Javascript, you should consider the localStorage. Cookies are nice if you want to know the selected language server-side, but if you just need it local, localStorage is better (reasons below).
To set a localStorage item, use
localStorage.setItem(key, value);
and afterwards to view the value, use
localStorage.getItem(key);
localStorage has a few advantages vs. cookies. Some of them are:
Cookies are included with every HTTP request, thereby slowing down your website by sometimes needlessly transmitting the same data over and over
Cookies are included with every HTTP request, thereby sending data unencrypted over the internet
Cookies are limited to about 4 KB of data
Sounds pretty basic, cookies are what you want. You can stick with javascript, or use php cookies. You opted for a javascript solution.
You'll need a few functions to make this work. Here are some examples below, but these are not working code. You'll need to edit them to do the language switching.
function init_language() {
// this function called when no language has been defined yet
var lang = getcookie( 'language' );
if ( lang == 'hebrew' ) hebrew_pls();
}
function switch_language() {
// When the user takes action to change language, ie, clicks a flag icon
if ( selected_language == 'hebrew' ) hebrew_pls();
}
function hebrew_pls() {
set_language('hebrew'); // aka, whatever you want to do
setcookie( 'language', 'hebrew', 30 ); // remember the language for 30 days
}
Here are the cookie functions I've been using for awhile. It's based on "How do I create and read a value from cookie?". I have modified these so they are a bit easier to use. If you don't like my modifications, there are plenty of alternatives online. Unfortunately JavaScript does not have an easy way to store cookies by default (without third party plugins/scripts).
/*
setCookie( name, value, days, [path = "/"] )
Sets a cookie, expires after "days" have passed
getCookie( name, default )
Gets the value of a cookie, or returns "default". Note: Does not set the cookie to default.
*/
function setCookie(c_name, value, exdays, path) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + ((exdays == null) ? "" : ("; expires=" + exdate.toUTCString()));
document.cookie = c_name + "=" + c_value + ((path == null) ? "; path=/" : "; path=" + path);
}
function getCookie(c_name, c_default) {
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x == c_name) {
return unescape(y);
}
}
if (typeof c_default != 'undefined') return c_default;
return false;
}

Does anyone know how can I keep the Javascript variable even though the page has been refreshed?

Example:
In the main page cliked on a button (NEW), the
page then will using Javascript to
open a new page in a new window by
calling redirectPage().
In the main page clicked on a button (EXIT),
then page then will call
confirmExit(), then
closeChildWindows() to closed all
popup new window before redirect to
another new page.
However, the JS variable
(childWindowHandles) will be always
reset if I refresh the main page,
and this cause the page unable to
close all other popup window before
relocated while EXIT button being
clicked
Does anyone know how can I solve this problem? By able to keep the JS variable (childWindowHandles) even the main page being refresh?
var childWindowHandles = new Array();
function redirectPage(url)
{
childWindowHandles[childWindowHandles.length] = window.open(url)
}
function confirmExit(url)
{
closeChildWindows()
window.location=url
}
function closeChildWindows()
{
for (var loop=0; loop<childWindowHandles.length; loop++)
{
if (!childWindowHandles[loop].closed)
{
childWindowHandles[loop].close();
}
}
}
You can use cookies to persist values...
Edit: You might find useful a simple object that I use:
Usage:
// Store a key/value for 1 day:
cookieManager.set('name', 'a value', 1);
// Retrieve a value associated to a key:
var value = cookieManager.get('name');
// Remove a key/value:
cookieManager.remove('name');
Implementation:
var cookieManager = {
set: function (name, value, expireDays) {
var expireDate = new Date();
expireDate.setDate(expireDate.getDate() + expireDays);
document.cookie = name + "=" + escape(value) +
((!expireDays) ? "" : ";expires="+expireDate.toGMTString());
},
get: function (key) {
var start,end;
if (document.cookie.length > 0) {
start = document.cookie.indexOf(key + "=");
if (start != -1) {
start = start + key.length + 1;
end = document.cookie.indexOf(";",start);
if (end == -1) {
end = document.cookie.length;
}
return unescape(document.cookie.substring(start,end));
}
}
return "";
},
remove: function (key) {
this.set(key, '', -1);
}
}
You can use cookies or window.name:) window.name to store session variables
Per this post here on SO, Firefox 3.5, Safari 4, and IE8 support HTML5 Storage.
Or use PersistJS which simplifies your access to whichever back-end storage mechanisms are available. (But cookie-less)
Use window.name
Positives:
it will live for the time of browser session - user closes window and it's gone
it won't put additional traffic on the wire like cookies do
it works even when cookies are disabled
at least 2MB space (Opera's limit is this low, other's have 32/64MB)
I also suggest you use javascript object for storing various values and serialize it using JSON and put that string into window.name.
Just make sure you don't persist any vulnerable data inside... For security reasons.
You can use sessionStorage.
Check this out:
html5_webstorage

Categories

Resources