Commit 157ab370 authored by Pedro Eduardo Trujillo's avatar Pedro Eduardo Trujillo
Browse files

Refactoriza lógica de sesión en nuevo componente

Extrae lógica de login y logout de las diferentes partes donde se
manejaba, para separar la interfaz de la funcionalidad. Ahora está
presente en un nuevo componente Session, instanciado desde App y
disponible para todos mediante publicación/suscripción a canales.

Implementa control de errores de login/logout y adapta componentes de
interfaz para hacer uso del nuevo componente Session.
parent 830d5569
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ define([
	, 'src/app/component/layout/InnerLayoutImpl'
	, 'src/app/component/layout/OuterLayoutImpl'
	, 'src/app/component/Loading'
	, 'src/app/component/session/Session'
	, 'src/app/component/meta/MetaTags'
	, 'src/app/component/ModuleStore'
	, 'src/app/component/Router'
@@ -32,6 +33,7 @@ define([
	, InnerLayoutImpl
	, OuterLayoutImpl
	, Loading
	, Session
	, MetaTags
	, ModuleStore
	, Router
@@ -159,6 +161,10 @@ define([
				parentChannel: parentChannel,
				globalNode: rootNode
			});

			new Session({
				parentChannel: parentChannel
			});
		},

		_defineSubscriptions: function() {
+123 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
	, 'dojo/_base/lang'
	, 'src/app/component/session/_Login'
	, 'src/app/component/session/_Logout'
	, 'src/component/base/_Module'
	, 'src/component/base/_Store'
], function(
	declare
	, lang
	, _Login
	, _Logout
	, _Module
	, _Store
) {

	return declare([_Module, _Store, _Login, _Logout], {
		//	summary:
		//		Módulo para gestionar el inicio y fin de sesión del usuario, así como mantenerla activa.

		constructor: function(args) {

			this.config = {
				ownChannel: 'session',
				events: {
					USER_LOGGED_IN: 'userLoggedIn',
					USER_LOGGED_OUT: 'userLoggedOut',
					USER_LOGIN_ERROR: 'userLoginError',
					USER_LOGOUT_ERROR: 'userLogoutError'
				},
				actions: {
					USER_LOGIN: 'userLogin',
					USER_LOGGED_IN: 'userLoggedIn',
					USER_LOGIN_ERROR: 'userLoginError',
					USER_LOGOUT: 'userLogout',
					USER_LOGGED_OUT: 'userLoggedOut',
					USER_LOGOUT_ERROR: 'userLogoutError'
				}
			};

			lang.mixin(this, this.config, args);
		},

		_defineSubscriptions: function() {

			this.subscriptionsConfig.push({
				channel: this.getChannel('USER_LOGIN'),
				callback: '_subUserLogin'
			},{
				channel: this.getChannel('USER_LOGOUT'),
				callback: '_subUserLogout'
			});
		},

		_definePublications: function() {

			this.publicationsConfig.push({
				event: 'USER_LOGGED_IN',
				channel: this.getChannel('USER_LOGGED_IN')
			},{
				event: 'USER_LOGGED_OUT',
				channel: this.getChannel('USER_LOGGED_OUT')
			},{
				event: 'USER_LOGIN_ERROR',
				channel: this.getChannel('USER_LOGIN_ERROR')
			},{
				event: 'USER_LOGOUT_ERROR',
				channel: this.getChannel('USER_LOGOUT_ERROR')
			});
		},

		_initialize: function() {

			this._loginTargets = [
				this._oauthLoginTarget,
				this._oidLoginTarget
			];

			this._logoutTargets = [
				this._oauthLogoutTarget,
				this._oidLogoutTarget
			];

			this.target = this._loginTargets.concat(this._logoutTargets);
		},

		_subUserLogin: function(req) {

			this._userLogin(req);
		},

		_subUserLogout: function() {

			this._userLogout();
		},

		_dataAvailable: function(res, resWrapper) {

			const target = resWrapper.target;

			if (this._loginTargets.includes(target)) {
				this._loginDataAvailable(res, resWrapper);
			} else if (this._logoutTargets.includes(target)) {
				this._logoutDataAvailable(res, resWrapper);
			} else {
				console.error('Received data from unknown target:', target);
			}
		},

		_errorAvailable: function(error, status, resWrapper) {

			const target = resWrapper.target;

			if (this._loginTargets.includes(target)) {
				this._loginErrorAvailable(error, status, resWrapper);
			} else if (this._logoutTargets.includes(target)) {
				this._logoutErrorAvailable(error, status, resWrapper);
			} else {
				console.error('Received error from unknown target:', target);
			}
		}
	});
});
+121 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
	, 'dojo/_base/lang'
	, 'dojo/Deferred'
	, 'dojo/promise/all'
	, 'src/redmicConfig'
	, 'src/util/Credentials'
], function(
	declare
	, lang
	, Deferred
	, PromiseAll
	, redmicConfig
	, Credentials
) {

	return declare(null, {
		//	summary:
		//		Lógica relativa al inicio de sesión del usuario.

		constructor: function(args) {

			this.config = {
				_oauthLoginTarget: redmicConfig.services.getOauthToken,
				_oidLoginTarget: redmicConfig.services.getOidToken
			};

			lang.mixin(this, this.config, args);
		},

		_userLogin: function(loginData) {

			this._prepareTokenPromise();
			this._getToken(loginData);
		},

		_prepareTokenPromise: function() {

			this._getTokenDfds = {};

			this._getTokenDfds[this._oauthLoginTarget] = new Deferred();
			this._getTokenDfds[this._oidLoginTarget] = new Deferred();

			PromiseAll(Object.values(this._getTokenDfds)).then(
				lang.hitch(this, this._onGotTokensSuccess),
				lang.hitch(this, this._onGotTokensFailure));
		},

		_getToken: function(loginData) {

			const data = {
				username: loginData.user,
				password: loginData.pass
			};

			const options = {
				data: data,
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded'
				}
			};

			this._emitEvt('REQUEST', {
				method: 'POST',
				target: this._oauthLoginTarget,
				options: options
			});

			this._emitEvt('REQUEST', {
				method: 'POST',
				target: this._oidLoginTarget,
				options: options
			});
		},

		_loginDataAvailable: function(res, resWrapper) {

			const target = resWrapper.target,
				tokenData = res.data;

			this._getTokenDfds[target].resolve(tokenData);
		},

		_loginErrorAvailable: function(error, status, resWrapper) {

			const target = resWrapper.target;

			this._getTokenDfds[target].reject({ error, status });
		},

		_onGotTokensSuccess: function(tokensData) {

			const oauthTokenData = tokensData[0],
				oidTokenData = tokensData[1];

			this._emitEvt('USER_LOGGED_IN', tokensData);

			this._addUserOidData(oidTokenData);
			this._addUserOauthData(oauthTokenData);
		},

		_onGotTokensFailure: function(errorData) {

			this._emitEvt('USER_LOGIN_ERROR', errorData);
		},

		_addUserOidData: function(data) {

			const oidAccessToken = data.access_token;

			Credentials.set('oidAccessToken', oidAccessToken);
		},

		_addUserOauthData: function(data) {

			const oauthAccessToken = data.access_token;

			Credentials.set('accessToken', oauthAccessToken);
		}
	});
});
+130 −0
Original line number Diff line number Diff line
define([
	'dojo/_base/declare'
	, 'dojo/_base/lang'
	, 'dojo/Deferred'
	, 'dojo/promise/all'
	, 'src/redmicConfig'
	, 'src/util/Credentials'
], function(
	declare
	, lang
	, Deferred
	, PromiseAll
	, redmicConfig
	, Credentials
) {

	return declare(null, {
		//	summary:
		//		Lógica relativa al cierre de sesión del usuario.

		constructor: function(args) {

			this.config = {
				_oauthLogoutTarget: redmicConfig.services.logoutOauth,
				_oidLogoutTarget: redmicConfig.services.logoutOid
			};

			lang.mixin(this, this.config, args);
		},

		_userLogout: function() {

			if (this._userIsLoggedIn()) {
				this._revokeToken();
			} else {
				this._removeUserData();
			}
		},

		_revokeToken: function() {

			this._prepareLogoutDfd();
			this._sendOauthLogoutRequest();
			this._sendOidLogoutRequest();
		},

		_prepareLogoutDfd: function() {

			this._logoutDfds = {};

			this._logoutDfds[this._oauthLogoutTarget] = new Deferred();
			this._logoutDfds[this._oidLogoutTarget] = new Deferred();

			PromiseAll(Object.values(this._logoutDfds)).then(
				lang.hitch(this, this._onLogoutSuccess),
				lang.hitch(this, this._onLogoutFailure));
		},

		_sendOauthLogoutRequest: function() {

			const data = {
				token: Credentials.get('accessToken')
			};

			this._sendLogoutRequest(data, this._oauthLogoutTarget);
		},

		_sendOidLogoutRequest: function() {

			const data = {
				token: Credentials.get('oidAccessToken')
			};

			this._sendLogoutRequest(data, this._oidLogoutTarget);
		},

		_sendLogoutRequest: function(data, target) {

			this._emitEvt('REQUEST', {
				method: 'POST',
				target: target,
				query: data,
				requesterId: this.getOwnChannel()
			});
		},

		_logoutDataAvailable: function(res, resWrapper) {

			const target = resWrapper.target,
				logoutData = res.data;

			this._logoutDfds[target].resolve(logoutData);
		},

		_logoutErrorAvailable: function(error, status, resWrapper) {

			const target = resWrapper.target;

			this._logoutDfds[target].reject({ error, status });
		},

		_onLogoutSuccess: function(logoutData) {

			this._emitEvt('USER_LOGGED_OUT', logoutData);

			this._removeUserOidData();
			this._removeUserOauthData();
		},

		_onLogoutFailure: function(errorData) {

			this._emitEvt('USER_LOGOUT_ERROR', errorData);
		},

		_removeUserOidData: function() {

			Credentials.set('oidAccessToken', null);
		},

		_removeUserOauthData: function() {

			Credentials.set('accessToken', null);
		},

		_userIsLoggedIn: function() {

			return !!Credentials.get('accessToken');
		}
	});
});
+6 −2
Original line number Diff line number Diff line
@@ -103,7 +103,8 @@ define([
			META_TAGS: "metaTags",
			LOADING: "loading",
			ALERT: "alert",
			COMMUNICATION: "communicationCenter"
			COMMUNICATION: "communicationCenter",
			SESSION: "session"
		},

		//	actionsPaused: Object
@@ -171,6 +172,8 @@ define([
		//		Nombre del canal por donde se va a controlar el módulo de moduleStore
		//	communicationChannel: String
		//		Nombre del canal por donde se van a publicar las notificaciones.
		//	sessionChannel: String
		//		Nombre del canal por donde se gestiona la sesión del usuario autenticado.

		//	i18n: Object
		//		Traducciones globales
@@ -203,7 +206,8 @@ define([
				metaTagsChannel: this._buildChannel(this.rootChannel, this.globalOwnChannels.META_TAGS),
				loadingChannel: this._buildChannel(this.rootChannel, this.globalOwnChannels.LOADING),
				alertChannel: this._buildChannel(this.rootChannel, this.globalOwnChannels.ALERT),
				communicationChannel: this._buildChannel(this.rootChannel, this.globalOwnChannels.COMMUNICATION)
				communicationChannel: this._buildChannel(this.rootChannel, this.globalOwnChannels.COMMUNICATION),
				sessionChannel: this._buildChannel(this.rootChannel, this.globalOwnChannels.SESSION)
			};

			lang.mixin(this, this.config);
Loading