Ember-simple-auth: Authenticating from URL - ember.js

I have a requirement that I can append ?auth_token=x to any URL in my app and it will restore the session as if you had a cookie or the details were stored in local storage.
I've tried every manner I can think of to implement this over the last few hours but I'm not getting anywhere. Is this possible? Where should I be looking to add such functionality?

I'm answering my own question here as I managed to find a solution, although how correct it is I'm not sure!
application.js:
// Really simple query parameter access
(function($) {
$.QueryString = (function(a) {
if (a == "") return {};
var b = {};
for (var i = 0; i < a.length; ++i)
{
var p=a[i].split('=');
if (p.length != 2) continue;
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
return b;
})(window.location.search.substr(1).split('&'))
})(jQuery);
initializers/authentication.js.coffee:
LocalStorageWithURLAuth = Ember.SimpleAuth.Stores.LocalStorage.extend
init: ->
if $.QueryString['auth_token']
auth_token = $.QueryString['auth_token']
Ember.$.ajax(
async: false
url: '/users/sign_in'
type: 'POST'
data: { auth_token: auth_token }
dataType: 'json'
).then (data) =>
#persist
auth_token: data.auth_token
auth_email: data.auth_email
user_id: data.user_id
authenticatorFactory: 'authenticator:devise'
#_super()
, (error) =>
#_super()
else
#_super()
Ember.Application.initializer
name: 'authentication'
initialize: (container, application) ->
Ember.SimpleAuth.Session.reopen
user: (->
userId = #get 'user_id'
if !Ember.isEmpty userId
container.lookup('store:main').find 'user', userId
).property('user_id')
container.register 'session-store:local-storage-url', LocalStorageWithURLAuth
Ember.SimpleAuth.setup container, application,
storeFactory: 'session-store:local-storage-url'
authenticatorFactory: 'authenticator:devise'
authorizerFactory: 'authorizer:devise'

I'm not sure I understand what you're doing. It seems like you're restoring the session from an auth token in the query string? That's actually what the authenticator's restore method is for (see docs here: http://ember-simple-auth.simplabs.com/ember-simple-auth-devise-api-docs.html#Ember-SimpleAuth-Authenticators-Devise-restore). Also when the application starts isn't the query string empty?

Related

Django2: Submit and store blobs as image files

I have made a few Django projects after having read the tutorial but I am by no means an expert in Django.
I am trying to take a screenshot of the current page and store it (if one does not exist).
To achieve this, we require a few things:
function to get screen shot of current page
function to async post this image to a view which should store it
view that stores the posted image
However, the screen shot function results in a Blob and I am having trouble getting a Django view to properly handle this.
A demo project is available here: https://gitlab.com/SumNeuron/so_save_blob
Function for screenshot
const screenshot = (function() {
function urlsToAbsolute(nodeList) {
if (!nodeList.length) {
return [];
}
var attrName = 'href';
if (nodeList[0].__proto__ === HTMLImageElement.prototype
|| nodeList[0].__proto__ === HTMLScriptElement.prototype) {
attrName = 'src';
}
nodeList = [].map.call(nodeList, function (el, i) {
var attr = el.getAttribute(attrName);
if (!attr) {
return;
}
var absURL = /^(https?|data):/i.test(attr);
if (absURL) {
return el;
} else {
return el;
}
});
return nodeList;
}
function addOnPageLoad_() {
window.addEventListener('DOMContentLoaded', function (e) {
var scrollX = document.documentElement.dataset.scrollX || 0;
var scrollY = document.documentElement.dataset.scrollY || 0;
window.scrollTo(scrollX, scrollY);
});
}
function capturePage(){
urlsToAbsolute(document.images);
urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
var screenshot = document.documentElement.cloneNode(true);
var b = document.createElement('base');
b.href = document.location.protocol + '//' + location.host;
var head = screenshot.querySelector('head');
head.insertBefore(b, head.firstChild);
screenshot.style.pointerEvents = 'none';
screenshot.style.overflow = 'hidden';
screenshot.style.webkitUserSelect = 'none';
screenshot.style.mozUserSelect = 'none';
screenshot.style.msUserSelect = 'none';
screenshot.style.oUserSelect = 'none';
screenshot.style.userSelect = 'none';
screenshot.dataset.scrollX = window.scrollX;
screenshot.dataset.scrollY = window.scrollY;
var script = document.createElement('script');
script.textContent = '(' + addOnPageLoad_.toString() + ')();';
screenshot.querySelector('body').appendChild(script);
var blob = new Blob([screenshot.outerHTML], {
type: 'text/html'
});
return blob;
}
return capturePage
})()
Function to async post Blob
function setupAjaxWithCSRFToken() {
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
}
function asyncSubmitBlob( url, blob ) {
var fd = new FormData();
fd.append('image', blob);
$.ajax({
url: url,
type: "POST",
data: fd,
contentType: false,
processData: false,
success: function(response){ console.log(response) },
error: function(data){ console.log(data) }
})
}
So to submit a screenshot of the current page:
setupAjaxWithCSRFToken()
const page = window.location.pathname;
const blob_url = "{% url 'my-app:post_blob' 'REPLACE' %}".replace(/REPLACE/,page == '/' ? '' : page)
asyncSubmitBlob( blob_url, screenshot() )
View to store the posted blob image
urls.py
...
from django.urls import include, path
...
app_name='my-app'
url_patterns=[
...
path('post_blob/', views.post_blob, {'page':'/'},name='post_blob'),
path('post_blob/<page>', views.post_blob,name='post_blob'),
...
]
views.py
from .models import PageBlob
...
def post_blob(request, page):
if request.FILES: # save screenshot of specified page
try:
pb = PageBlob.objects.all().filter(page=page))
if not pb.count():
pb = PageBlob()
pb.page = page
pb.blob = request.FILES['image']
pb.save()
return HttpResponse('Blob Submitted')
except:
return HttpResponse('[App::my-app]\tError when requesting page_image({page})'.format(page=page))
else: # return screenshot of requested page
try:
# get objects storing screenshot for requested page
pb = PageBlob.objects.all().filter(page=page)
# if one exists
if pb.count():
pb = pb[0]
## this just returns the string literal "blob"
return HttpResponse(str(pb.blob))
return HttpResponse('[App::my-app]\tNo blob for {page}'.format(page=page))
except:
return HttpResponse('[App::my-app]\tError when trying to retrieve blob for {page}'.format(page=page))
return HttpResponse('Another response')
models.py
class PageBlob(models.Model):
page = models.CharField(max_length=500)
blob = models.TextField(db_column='data', blank=True)
But I can not seem to faithfully capture and retrieve the blob.
Many S.O. questions of storing blobs use the model approach with import base64 to encode and decode the blob. One even recommends using the BinaryField. However, Django's documentation states firmly that BinaryField is not a replacement for the handling of static files.
So how could I achieve this?
S.O. posts I have found helpful to get this far
Upload an image blob from Ajax to Django
How to screenshot website in JavaScript client-side / how Google did it? (no need to access HDD)
How to download data in a blob field from database in django?
passing blob parameter to django
Returning binary data with django HttpResponse
https://djangosnippets.org/snippets/1597/
Django Binary or BLOB model field
Django CSRF check failing with an Ajax POST request
How can javascript upload a blob?

Pass parameters through angularjs 2 to restful services

I am trying to call a webservice which is as follows,
#RequestMapping(value = { "/persons" },method = RequestMethod.GET,headers="Accept=application/json")
public List<Person> getDummyData(#RequestParam(value="search", defaultValue="a") String search,HttpServletRequest req){
List<Person> listOfMatchedPersons=listOfPersons.stream().filter(person->person.getName().contains(search)).collect(Collectors.toList());
req.getParameterMap().forEach((k,v)->System.out.println(k+" : "+v));
return listOfMatchedPersons;
}
I want to call this service with some parameter from my UI, but it always executes this method with default value of search i.e. a.
Following is my angularjs 2's service that is consuming this service,
search(term: string) {
var params = new URLSearchParams();
params.set('search', term);
let aopsServices = 'http://localhost:8080/dummy/persons';//?search='+term;
this.ot = this.http
.get(aopsServices,params)
.map(response => response.json())
;
return this.ot;
}
however if i change the url to http://localhost:8080/dummy/persons'?search='+term; it works.
And also what should be the ideal approach to access the restful services if they are secured ?
I see two ways to do that:
Leveraging the URLSearchParams class:
search(term: string) {
var params = new URLSearchParams();
params.set('search', term);
let aopsServices = 'http://localhost:8080/dummy/persons';
this.ot = this.http
.get(aopsServices, { search: params })
.map(response => response.json());
return this.ot;
}
Use of ` (backticks) instead of single quotes '
search(term: string) {
let aopsServices = `http://localhost:8080/dummy/persons?search=${term}`;
this.ot = this.http
.get(aopsServices, { search: params })
.map(response => response.json());
return this.ot;
}
The second approach is more concise but doesn't urlencode the parameter.
I changed my code to
var params = new URLSearchParams();
params.set('search', term);
let aopsServices = 'http://localhost:8080/dummy/persons';
this.ot = this.http
.get(aopsServices,new RequestOptions({search:params}))
.map(response => response.json());
and it worked.
I misread the documentation of get.

How to pass an api key as part of url in my ember app

I have an ember app that is consuming an API. My API requires an API key be sent in the URL like...
myJunk.com/api/v1/shots?api_key=d26da3938adc5f3c8604256194c18501
Here is the ember code I'm trying to get to work...
App.Person = Ember.Model.extend({
name: Ember.attr()
});
App.Person.adapter = Ember.RESTAdapter.create();
App.Person.url = "http://myJunk.com/api/v1/shots?api_key=d26da3938adc5f3c8604256194c18501";
App.Person.collectionKey = "shots";
The issue I'm having is that '.json' is being appended to the URL. Here is the error I get in chrome...
XMLHttpRequest cannot load http://myJunk.com/api/v1/shots?api_key=d26da3938adc5f3c8604256194c18501.json.
Looks like this is a know issue...
What is the right way to do this in ember?
Looks like the fix has not made it to the release version yet. See github comment...
https://github.com/ebryn/ember-model/issues/300
I do this in my ApplicationAdapter as follows:
ajaxOptions: function(url, type, hash) {
if(window.ENV.api_key) {
if(hash === undefined) {
hash = {data: {api_key: window.ENV.api_key}};
} else {
if(hash.data) {
hash.data.api_key = window.ENV.api_key;
} else {
hash.data = {api_key: window.ENV.api_key};
}
}
} else {
Ember.Logger.debug('no api key');
}
return this._super(url, type, hash);
}
With the current user's API key stored in ENV.api_key. This way it is inserted into all requests, POST or GET.

Unable to access the user session in socket.io by parsing the cookie

I am tring to access the user session into my socket connection in order to accept/refuse it.
The problem is, i do get a sessionId string but the session i get with SessionStore.get() is undefined.
I guess the problem could be that i never "signed" the sessionId after getting it (data.cookie['connect.sid']), but i didn't see any way to do it with the cookie module.
var MongoStore = require('connect-mongo')(express);
var SessionStore = new MongoStore({ db: "test" });
var parseCookie = require('cookie').parse;
app.use(express.session({
secret: 'test',
store: SessionStore
}));
io.set('authorization', function(data, accept) {
if (data.headers.cookie) {
data.cookie = parseCookie(data.headers.cookie);
data.sessionID = data.cookie['connect.sid'];
console.log('sessionId: '+data.sessionID);
SessionStore.get(data.sessionID, function(err, session) {
if (err || !session) {
console.log('err: '+err);
console.log('Session: '+session);
accept('Error', false);
} else {
data.session = session;
accept(null, true);
}
});
} else {
return accept('No cookie transmitted.', false);
}
});
Maybe someone see what i am missing here?
The connect.sid cookie is formatted into 2 parts with a prefix. The actual session ID part can be extracted with this fragment:
rawSid.split(".")[0].slice(2);

Configuring RESTAdapter to not set the .json extension for get / list requests

I'm using a cross domain REST api.
I have defined my custom REST adapter to trigg my API.
Pb is to remove the ".json" automaticaly set by ember-model.
How to configure my adapter to avoid setting my "replace function" (url=url.replace('.json', '');)
App.Book.adapter = Ember.RESTAdapter.create({
ajaxSettings: function(url, method) {
var authorization= "Basic " + btoa("login" + ":" + "pwd");
url=url.replace('.json', '');
return {
url: url,
type: method,
dataType: "json",
headers: {
"Authorization": authorization
},
};
}
});
App.Certificate.url='http://mysite/api/v1/books';
How to configure my adapter to avoid setting my "replace function" (url=url.replace('.json', '');)
Since ember-model does not provide any configuration option to change this behaviour, IMHO, your solution by doing url = url.replace('.json', ''); isn't that bad.
Another possible way I can think of could be to reopen the RESTAdapter and override the buildURL function to not include the .json.
Ember.RESTAdapter.reopen({
buildURL: function(klass, id) {
var urlRoot = Ember.get(klass, 'url');
if (!urlRoot) { throw new Error('Ember.RESTAdapter requires a `url` property to be specified'); }
if (!Ember.isEmpty(id)) {
return urlRoot + "/" + id;
} else {
return urlRoot;
}
}
});
But this is not that future proof if the original code changes and you want to update the lib you had to change also your override.
Hope it helps.