##// END OF EJS Templates
auth: don't break hashing in case of user with empty password....
auth: don't break hashing in case of user with empty password. In some cases such as LDAP user created via external scripts users might set the passwords to empty. The hashing uses the md5(password_hash) to store reference to detect password changes and forbid using the same password. In case of pure LDAP users this is not valid, and we shouldn't raise Errors in such case. This change makes it work for empty passwords now.

File last commit:

r787:048ecbf3 default
r2203:8a18c3c3 default
Show More
channelstream-connection.js
502 lines | 13.3 KiB | application/javascript | JavascriptLexer
Polymer({
is: 'channelstream-connection',
/**
* Fired when `channels` array changes.
*
* @event channelstream-channels-changed
*/
/**
* Fired when `connect()` method succeeds.
*
* @event channelstream-connected
*/
/**
* Fired when `connect` fails.
*
* @event channelstream-connect-error
*/
/**
* Fired when `disconnect()` succeeds.
*
* @event channelstream-disconnected
*/
/**
* Fired when `message()` succeeds.
*
* @event channelstream-message-sent
*/
/**
* Fired when `message()` fails.
*
* @event channelstream-message-error
*/
/**
* Fired when `subscribe()` succeeds.
*
* @event channelstream-subscribed
*/
/**
* Fired when `subscribe()` fails.
*
* @event channelstream-subscribe-error
*/
/**
* Fired when `unsubscribe()` succeeds.
*
* @event channelstream-unsubscribed
*/
/**
* Fired when `unsubscribe()` fails.
*
* @event channelstream-unsubscribe-error
*/
/**
* Fired when listening connection receives a message.
*
* @event channelstream-listen-message
*/
/**
* Fired when listening connection is opened.
*
* @event channelstream-listen-opened
*/
/**
* Fired when listening connection is closed.
*
* @event channelstream-listen-closed
*/
/**
* Fired when listening connection suffers an error.
*
* @event channelstream-listen-error
*/
properties: {
isReady: Boolean,
/** List of channels user should be subscribed to. */
channels: {
type: Array,
value: function () {
return []
},
notify: true
},
/** Username of connecting user. */
username: {
type: String,
value: 'Anonymous',
reflectToAttribute: true
},
/** Connection identifier. */
connectionId: {
type: String,
reflectToAttribute: true
},
/** Websocket instance. */
websocket: {
type: Object,
value: null
},
/** Websocket connection url. */
websocketUrl: {
type: String,
value: ''
},
/** URL used in `connect()`. */
connectUrl: {
type: String,
value: ''
},
/** URL used in `disconnect()`. */
disconnectUrl: {
type: String,
value: ''
},
/** URL used in `subscribe()`. */
subscribeUrl: {
type: String,
value: ''
},
/** URL used in `unsubscribe()`. */
unsubscribeUrl: {
type: String,
value: ''
},
/** URL used in `message()`. */
messageUrl: {
type: String,
value: ''
},
/** Long-polling connection url. */
longPollUrl: {
type: String,
value: ''
},
/** Long-polling connection url. */
shouldReconnect: {
type: Boolean,
value: true
},
/** Should send heartbeats. */
heartbeats: {
type: Boolean,
value: true
},
/** How much should every retry interval increase (in milliseconds) */
increaseBounceIv: {
type: Number,
value: 2000
},
_currentBounceIv: {
type: Number,
reflectToAttribute: true,
value: 0
},
/** Should use websockets or long-polling by default */
useWebsocket: {
type: Boolean,
reflectToAttribute: true,
value: true
},
connected: {
type: Boolean,
reflectToAttribute: true,
value: false
}
},
observers: [
'_handleChannelsChange(channels.splices)'
],
listeners: {
'channelstream-connected': 'startListening',
'channelstream-connect-error': 'retryConnection',
},
/**
* Mutators hold functions that you can set locally to change the data
* that the client is sending to all endpoints
* you can call it like `elem.mutators('connect', yourFunc())`
* mutators will be executed in order they were pushed onto arrays
*
*/
mutators: {
connect: function () {
return []
}(),
message: function () {
return []
}(),
subscribe: function () {
return []
}(),
unsubscribe: function () {
return []
}(),
disconnect: function () {
return []
}()
},
ready: function () {
this.isReady = true;
},
/**
* Connects user and fetches connection id from the server.
*
*/
connect: function () {
var request = this.$['ajaxConnect'];
request.url = this.connectUrl;
request.body = {
username: this.username,
channels: this.channels
};
for (var i = 0; i < this.mutators.connect.length; i++) {
this.mutators.connect[i](request);
}
request.generateRequest()
},
/**
* Overwrite with custom function that will
*/
addMutator: function (type, func) {
this.mutators[type].push(func);
},
/**
* Subscribes user to channels.
*
*/
subscribe: function (channels) {
var request = this.$['ajaxSubscribe'];
request.url = this.subscribeUrl;
request.body = {
channels: channels,
conn_id: this.connectionId
};
for (var i = 0; i < this.mutators.subscribe.length; i++) {
this.mutators.subscribe[i](request);
}
if (request.body.channels.length) {
request.generateRequest();
}
},
/**
* Unsubscribes user from channels.
*
*/
unsubscribe: function (unsubscribe) {
var request = this.$['ajaxUnsubscribe'];
request.url = this.unsubscribeUrl;
request.body = {
channels: unsubscribe,
conn_id: this.connectionId
};
for (var i = 0; i < this.mutators.unsubscribe.length; i++) {
this.mutators.unsubscribe[i](request);
}
request.generateRequest()
},
/**
* calculates list of channels we should add user to based on difference
* between channels property and passed channel list
*/
calculateSubscribe: function (channels) {
var currentlySubscribed = this.channels;
var toSubscribe = [];
for (var i = 0; i < channels.length; i++) {
if (currentlySubscribed.indexOf(channels[i]) === -1) {
toSubscribe.push(channels[i]);
}
}
return toSubscribe
},
/**
* calculates list of channels we should remove user from based difference
* between channels property and passed channel list
*/
calculateUnsubscribe: function (channels) {
var currentlySubscribed = this.channels;
var toUnsubscribe = [];
for (var i = 0; i < channels.length; i++) {
if (currentlySubscribed.indexOf(channels[i]) !== -1) {
toUnsubscribe.push(channels[i]);
}
}
return toUnsubscribe
},
/**
* Marks the connection as expired.
*
*/
disconnect: function () {
var request = this.$['ajaxDisconnect'];
request.url = this.disconnectUrl;
request.params = {
conn_id: this.connectionId
};
for (var i = 0; i < this.mutators.disconnect.length; i++) {
this.mutators.disconnect[i](request);
}
// mark connection as expired
request.generateRequest();
// disconnect existing connection
this.closeConnection();
},
/**
* Sends a message to the server.
*
*/
message: function (message) {
var request = this.$['ajaxMessage'];
request.url = this.messageUrl;
request.body = message;
for (var i = 0; i < this.mutators.message.length; i++) {
this.mutators.message[i](request)
}
request.generateRequest();
},
/**
* Opens "long lived" (websocket/longpoll) connection to the channelstream server.
*
*/
startListening: function (event) {
this.fire('start-listening', {});
if (this.useWebsocket) {
this.useWebsocket = window.WebSocket ? true : false;
}
if (this.useWebsocket) {
this.openWebsocket();
}
else {
this.openLongPoll();
}
},
/**
* Opens websocket connection.
*
*/
openWebsocket: function () {
var url = this.websocketUrl + '?conn_id=' + this.connectionId;
this.websocket = new WebSocket(url);
this.websocket.onopen = this._handleListenOpen.bind(this);
this.websocket.onclose = this._handleListenCloseEvent.bind(this);
this.websocket.onerror = this._handleListenErrorEvent.bind(this);
this.websocket.onmessage = this._handleListenMessageEvent.bind(this);
},
/**
* Opens long-poll connection.
*
*/
openLongPoll: function () {
var request = this.$['ajaxListen'];
request.url = this.longPollUrl + '?conn_id=' + this.connectionId;
request.generateRequest()
},
/**
* Retries `connect()` call while incrementing interval between tries up to 1 minute.
*
*/
retryConnection: function () {
if (!this.shouldReconnect) {
return;
}
if (this._currentBounceIv < 60000) {
this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv;
}
else {
this._currentBounceIv = 60000;
}
setTimeout(this.connect.bind(this), this._currentBounceIv);
},
/**
* Closes listening connection.
*
*/
closeConnection: function () {
var request = this.$['ajaxListen'];
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
this.websocket.onclose = null;
this.websocket.onerror = null;
this.websocket.close();
}
if (request.loading) {
request.lastRequest.abort();
}
this.connected = false;
},
_handleChannelsChange: function (event) {
// do not fire the event if set() didn't mutate anything
// is this a reliable way to do it?
if (!this.isReady || event === undefined) {
return
}
this.fire('channelstream-channels-changed', event)
},
_handleListenOpen: function (event) {
this.connected = true;
this.fire('channelstream-listen-opened', event);
this.createHeartBeats();
},
createHeartBeats: function () {
if (typeof self._heartbeat === 'undefined' && this.websocket !== null
&& this.heartbeats) {
self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000);
}
},
_sendHeartBeat: function () {
if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) {
this.websocket.send(JSON.stringify({type: 'heartbeat'}));
}
},
_handleListenError: function (event) {
this.connected = false;
this.retryConnection();
},
_handleConnectError: function (event) {
this.connected = false;
this.fire('channelstream-connect-error', event.detail);
},
_handleListenMessageEvent: function (event) {
var data = null;
// comes from iron-ajax
if (event.detail) {
data = JSON.parse(event.detail.response)
// comes from websocket
setTimeout(this.openLongPoll.bind(this), 0);
} else {
data = JSON.parse(event.data)
}
this.fire('channelstream-listen-message', data);
},
_handleListenCloseEvent: function (event) {
this.connected = false;
this.fire('channelstream-listen-closed', event.detail);
this.retryConnection();
},
_handleListenErrorEvent: function (event) {
this.connected = false;
this.fire('channelstream-listen-error', {})
},
_handleConnect: function (event) {
this.currentBounceIv = 0;
this.connectionId = event.detail.response.conn_id;
this.fire('channelstream-connected', event.detail.response);
},
_handleDisconnect: function (event) {
this.connected = false;
this.fire('channelstream-disconnected', {});
},
_handleMessage: function (event) {
this.fire('channelstream-message-sent', event.detail.response);
},
_handleMessageError: function (event) {
this.fire('channelstream-message-error', event.detail);
},
_handleSubscribe: function (event) {
this.fire('channelstream-subscribed', event.detail.response);
},
_handleSubscribeError: function (event) {
this.fire('channelstream-subscribe-error', event.detail);
},
_handleUnsubscribe: function (event) {
this.fire('channelstream-unsubscribed', event.detail.response);
},
_handleUnsubscribeError: function (event) {
this.fire('channelstream-unsubscribe-error', event.detail);
}
});